How is an overloaded method chosen when a parameter is the literal null value?
Asked Answered
B

8

98

I came across this question in a quiz,

public class MoneyCalc {

   public void method(Object o) {
      System.out.println("Object Verion");
   }

   public void method(String s) {
      System.out.println("String Version");
   }

   public static void main(String args[]) {
      MoneyCalc question = new MoneyCalc();
      question.method(null);
   }
}

The output of this program is "String Version". But I was not able to understand why passing a null to an overloaded method chose the string version. Is null a String variable pointing to nothing ?

However when the code is changed to,

public class MoneyCalc {

   public void method(StringBuffer sb) {
      System.out.println("StringBuffer Verion");
   }

   public void method(String s) {
      System.out.println("String Version");
   }

   public static void main(String args[]) {
      MoneyCalc question = new MoneyCalc();
      question.method(null);
   }
}

it gives a compile error saying "The method method(StringBuffer) is ambiguous for the type MoneyCalc"

Beamends answered 23/10, 2012 at 14:42 Comment(3)
You can assign a string to a null value so it is valid and the order for java and most programming languages is fit to the closest type and then to object.Snag
https://mcmap.net/q/136548/-overloaded-method-selection-based-on-the-parameter-39-s-real-typeEricerica
Apparently this was closed as duplicate by people who only read the title. The actual question here is about why a specific overload was chosen, not "what is null".Delanadelancey
S
102

Is null a String variable pointing to nothing ?

A null reference can be converted to an expression of any class type. So in the case of String, this is fine:

String x = null;

The String overload here is chosen because the Java compiler picks the most specific overload, as per section 15.12.2.5 of the JLS. In particular:

The informal intuition is that one method is more specific than another if any invocation handled by the first method could be passed on to the other one without a compile-time type error.

In your second case, both methods are still applicable, but neither String nor StringBuffer is more specific than the other, therefore neither method is more specific than the other, hence the compiler error.

Sacerdotalism answered 23/10, 2012 at 14:44 Comment(12)
And how is the String overload more specific than the Object overload?Backrest
Because an Object can take any type and wrap it in an Object, whereas a String can only take a String. In this case, String is more specific of a type compared to the Object type.Snag
@user1610015: See the JLS for details - I've included the "informal" version in the answer.Sacerdotalism
Thanks for the answer. However in my second scenario why didn't it choose the string method again as it didn't give any compile error earlier.Beamends
@zakSyed: What do you mean "as it didn't give any compile error earlier"? In the second case, it's ambiguous - neither String nor StringBuffer is more specific than the other.Sacerdotalism
@Beamends If you were asked what is more specialized "String" or "Object", what would you say? Evidently "String", right? If you were asked: what is more specialized "String" or "StringBuffer"? There is no answer, they are both orthogonal specializations, how can you choose between them? Then you must be explicit regarding which one you want (i.e. by casting your null reference question.method((String)null))Trilby
@Beamends - You may want to snatch a book on understanding data types. You have to understand why your second case is ambiguous. It's two different types namely, String and StringBuffer its the same if you would of overloaded this method with String and Int.Snag
@JonSkeet: That makes sense. So basically it looks for a method according to the most specific rule and if it not able to decide which is more specific then it would throw a compile-time error.Beamends
@JonH: In the case of string and int, the code compiles and returns the output "String version".Beamends
@Beamends - right, in your specific case, my point was String and Int are just two different types that specialize in just that their types. If you pass an int the compiler looks at the most specific method (given you are dealing with overloaded functions) and uses that one.Snag
@Snag "null" is reference type, if one of the methods receives a primitive type as parameter (i.e. int) it will not even be considered by the compiler when choosing the right method to invoke for a reference of type null. It is confusing if by "Int" you meant java.lang.Integer or if you meant the primitive type int.Trilby
@EdwinDalorzo - lost my mind you are right int was a very bad example. Use a reference type zak.Snag
T
9

Additionally, the JLS 3.10.7 also declares that "null" is a literal value of the "null type". Therefore there exists a type called "null".

Later, the JLS 4.1 states that there exists a null type of which is impossible to declare variables, but you can use it through the null literal only. Later it says:

The null reference can always undergo a widening reference conversion to any reference type.

Why the compiler chooses to widen it to String might well be explained in Jon's answer.

Trilby answered 23/10, 2012 at 14:52 Comment(5)
I'm quite sure that type is Void.Squabble
@Squabble There is no mention of that in the Java Language Specification. Can you cite your reference so that we all can verify your claim?Trilby
I never read that thing. But I did do some Java this summer and you can overload a method taking an Object with a method taking a Void object.Squabble
It's more of a class than a type.Squabble
@Squabble Of course you can. You can overload a method in Java with whichever any other type that you want. That, nonetheless, has nothing to do with the question, or my answer.Trilby
S
2

You can assign a string to a null value so it is valid and the order for java and most programming languages is fit to the closest type and then to object.

Snag answered 23/10, 2012 at 14:44 Comment(0)
T
2

To answer the question in the title: null is neither a String nor an Object, but a reference to either can be assigned to null.

I'm actually surprised this code even compiles. I tried something similar previously and I got a compiler error saying that the call was ambiguous.

However, in this case, it seems like the compiler is choosing the method which is lowest on the food chain. It's assuming that you want the least generic version of the method in order to help you out.

I'll have to see if I can dig up the example where I got a compiler error in this (seemingly) exact same scenario, though...]

EDIT: I see. In the version I made, I had two overloaded methods accepting a String and an Integer. In this scenario, there is no "most specific" parameter (as in Object and String), so it can't choose between them, unlike in your code.

Very cool question!

Tithe answered 23/10, 2012 at 14:45 Comment(1)
null is neither but it can be assigned to both, that is the difference and it will compile why wouldn't it - it's absolutely valid code.Snag
F
0

As String type is more specific than Object type. Let's say you add one more method that takes an Integer type.

public void method(Integer i) {
      System.out.println("Integer Version");
   }

Then you will get a compiler error saying that the call is ambiguous. As now we two equally specific methods with same precedence.

Fleurette answered 2/10, 2015 at 11:17 Comment(0)
P
0

Java compiler gives most derived class type to assign null.

Here is the example to understand it :

class A{

    public void methodA(){
        System.out.println("Hello methodA");
    }
}

class B extends A{
    public void methodB(){
        System.out.println("Hello methodB");
    }
}

class C{
    public void methodC(){
        System.out.println("Hello methodC");
    }
}

public class MyTest {

     public static void fun(B Obj){
         System.out.println("B Class.");
     }
     public static void fun(A Obj){
         System.out.println("A Class.");
     }

    public static void main(String[] args) {
        fun(null);
    }
}

output: B Class.

on the other hand:

public class MyTest {

     public static void fun(C Obj){
         System.out.println("B Class.");
     }
     public static void fun(A Obj){
         System.out.println("A Class.");
     }

    public static void main(String[] args) {
        fun(null);
    }
}

Result : The method fun(C) is ambiguous for the type MyTest

Hope it will help to understand this case better.

Pirali answered 24/11, 2016 at 11:42 Comment(0)
P
0

Source: https://docs.oracle.com/javase/specs/jls/se8/html/jls-15.html#jls-15.12.2.5
Concept: Most specific method
Explanation: If more than one member method is both accessible and applicable to a method invocation, it is necessary to choose one to provide the descriptor for the run-time method dispatch. The Java programming language uses the rule that the most specific method is chosen. Try casting the null on to specific type and the method you wish will be automatically called.

Pedroza answered 12/3, 2019 at 12:50 Comment(1)
In the first example, String extends Object, so "most specific" is the method taking String, but in the second example, String and StringBuffer both extend Object, so they are equally specific and therefore the compiler cannot make a choiceArbuthnot
T
-1

I would say neither. NULL is a state not a value. Check out this link for more info on this (the article applies to SQL, but I think it helps with your question as well).

Torsk answered 23/10, 2012 at 14:46 Comment(1)
null is very definitely a value, as defined in the JLS.Kast

© 2022 - 2024 — McMap. All rights reserved.