null parameter in method call different in 1.4 v.s. 1.5?

M

mingclee1

Hello,

I have a question when passing null in the parameter when invoking a
method. Consider:

public class Test1

public void method3(Object o){

System.out.println("Object");
}

public void method3(String s){
System.out.println("string s");
}

/**
* @param args
*/
public static void main(String[] args) {
// TODO Auto-generated method stub
Test1 test = new Test1();
test.method3(null);
}

}

In JDK 1.5 or 5.0(latest one from sun), it will call method3(String s).
And if I comment out method3(String s), then it will call
method3(Object o). However in JDK1.4.2, I get compilation error:
the method method3(Object) is ambiguous for the type Test1

So, my question is why is it in JDK1.5, when calling
test.method3(null), it knows to invoke method3(String) over
method3(Object)? And also what is really happening when someone invoke
a method with null parameter without specifying a type? (Like
method3(null);)

Thanks in advance for any help!
 
W

wjm

I've just compiled and run your example code against 1.4.2_06 and 1.5
and it worked fine. It prints "string s".
 
H

Hemal Pandya

Hello,
I have a question when passing null in the parameter when invoking a
method. Consider:

public class Test1

public void method3(Object o){

System.out.println("Object");
}

public void method3(String s){
System.out.println("string s");
}

/**
* @param args
*/
public static void main(String[] args) {
// TODO Auto-generated method stub
Test1 test = new Test1();
test.method3(null);
}

}

In JDK 1.5 or 5.0(latest one from sun), it will call method3(String s).
And if I comment out method3(String s), then it will call
method3(Object o). However in JDK1.4.2, I get compilation error:
the method method3(Object) is ambiguous for the type Test1

So, my question is why is it in JDK1.5, when calling
test.method3(null), it knows to invoke method3(String) over
method3(Object)? And also what is really happening when someone invoke
a method with null parameter without specifying a type? (Like
method3(null);)

Thanks in advance for any help!

I don't know the answer, but want to add that this seems to happen not
just for String but for any other type. If the second declaration of
method3 above accepts a Test1 then it is still that method that gets
called. If there are two methods and neither accepts a Object then the
compiler gives ambiguity error.

That is, conversion from null to any type is preferred over conversion
to type Object. Seems like a bug to me. but what do I know.
 
C

Chris Uppal

In JDK 1.5 or 5.0(latest one from sun), it will call method3(String s).
And if I comment out method3(String s), then it will call
method3(Object o). However in JDK1.4.2, I get compilation error:
the method method3(Object) is ambiguous for the type Test1

As far as I can see, your example compiles OK in both 1.4.2 and 1.5.0. In both
cases I think the compiler is correct (though I haven't gone back to check the
letter of the spec [*]) since given:

method(Object o) {System.out.println("Object"); }
method(String s) {System.out.println("String"); }

and a call:

method(null);

both versions of method() are available for consideration by the compiler's
lookup algorithm, but the String-taking version is unambiguously more specific,
and hence is accepted as the correct version to call. Odd, I know, but I think
that's according to the spec.

Note that given the quite similar method definitions:

method(String s) {System.out.println("String"); }
method(StringBuffer sb) {System.out.println("StringBuffer"); }

the method call is /not/ unambiuous, and is rejected by both versions of the
compiler.

The compiler's behaviour changed somewhere around 1.4.0 or 1.4.1 in some
aspects of how it looks up methods. Perhaps you were testing with a slightly
earlier version than you think ?


([*] Normally I /do/ go back to the spec to look these things up, but JLS2 is
now obsolete, and JLS3 incorporates so horribly much extra complexity in the
details of method lookup that I feel no desire at all to wade into it.)

-- chris
 
M

mingclee1

The compiler's behaviour changed somewhere around 1.4.0 or 1.4.1 in some
aspects of how it looks up methods. Perhaps you were testing with a slightly
earlier version than you think ?

You were right. I used Eclipse 3.0, and it uses JIT3.0. I did try
1.4.2_05, and got the same result you mentioned. So I guess the
question is can anyone think of a reason that conversion from null to
any type is preferred over type object?

Thanks
 
T

Thomas Hawtin

You were right. I used Eclipse 3.0, and it uses JIT3.0. I did try

IIRC, Eclipse uses it's own compiler which probably isn't entirely bug
free. Neal Gafter's CV said that he removed all known bugs from the 1.4
compiler. He was less successful with 5.0.
1.4.2_05, and got the same result you mentioned. So I guess the
question is can anyone think of a reason that conversion from null to
any type is preferred over type object?

null isn't treated any differently from any other expression. However
null is odd in that it has the type which is a subtype of every other
(reference) type.

For any lookup we want the most specific method. If I have a method
overloaded to take parameter String or Object, then I don't want the
compiler to claim that it is ambiguous every time I try to use the
method with a literal string.

It's covered in Bloch & Gafter's Java Puzzlers book. Must be a good book
because I get a mention (or two) in the acknowledgments.

Tom Hawtin
 
T

Thomas G. Marshall

Thomas Hawtin coughed up:
IIRC, Eclipse uses it's own compiler which probably isn't entirely bug
free. Neal Gafter's CV said that he removed all known bugs from the
1.4 compiler. He was less successful with 5.0.


null isn't treated any differently from any other expression. However
null is odd in that it has the type which is a subtype of every other
(reference) type.

To be pedantic, I'm not sure that's true. The null reference is the only
permitted value of an expression of the null /type/. (JLS2 4.1). It can
always be /cast/ to any reference type, but it is not a subtype of anything.


....[rip]...
 
H

Hemal Pandya

Thomas said:
Thomas Hawtin coughed up:
(e-mail address removed) wrote: [....]

null isn't treated any differently from any other expression. However
null is odd in that it has the type which is a subtype of every other
(reference) type.

To be pedantic, I'm not sure that's true. The null reference is the only
permitted value of an expression of the null /type/. (JLS2 4.1). It can
always be /cast/ to any reference type, but it is not a subtype of anything.

Actually I think it is, at least per JLS3. 4.10: The subtypes of a type
T are all types U such that T is a supertype of U, and the null type.

Also relevant is Assignment Conversion (5.2): A value of the null type
(the null reference is the only such value) may be assigned to any
reference type, resulting in a null reference of that type.

This perhaps more permissive then the 4.1, in that such a cast is
guaranteed to not throw ClassCastException. But it still does not
explain why, from null type, conversion to String is preferred over
conversion to Object.

It seems that given a class C1 and class C2 extends C1 then C2 is
considered "closer" to the null type then C1. The logic seems to be: C1
< null type; C2 < null type; C1 < C2 => (null type - C2) lessThen (null
type - C1)

This is as if the null type multiply inherits (Yes!) from all leaf
types in the universe. This is demonstrated by the following example.
C1 < C2 < C3.
Class D has two overloaded methods foo one of which takes a C1 and the
other a C2.
Calling D.foo with a C3 resolves to D.foo(C2)
Calling D.foo with null resolves to D.foo(C2)

public class Test2
{
public static void main(String[] args) {
D.foo(new C3()); // calls D.foo(C2)
D.foo(null); // calls D.foo(C2)
}

static class C1 {}
static class C2 extends C1 {}
static class C3 extends C2 {}

static class D
{
static void foo(C1 c) { System.out.println("D.foo(C1)"); }
static void foo(C2 c) { System.out.println("D.foo(C2)"); }
}
}

I think this explains the behavior, but what do I know.
 
P

Patricia Shanahan

Hello,

I have a question when passing null in the parameter when invoking a
method. Consider:

public class Test1

public void method3(Object o){

System.out.println("Object");
}

public void method3(String s){
System.out.println("string s");
}

/**
* @param args
*/
public static void main(String[] args) {
// TODO Auto-generated method stub
Test1 test = new Test1();
test.method3(null);
}

}

In JDK 1.5 or 5.0(latest one from sun), it will call method3(String s).
And if I comment out method3(String s), then it will call
method3(Object o).

I think this is covered by "15.12.2.2 Choose the Most Specific Method",
http://java.sun.com/docs/books/jls/second_edition/html/expressions.doc.html#18428

The compile time processing of a method call finds the methods that are
both accessible (proper protection etc.) and applicable (suitable
parameter types). Clearly, both method3 declarations are accessible.
Because null is method invocation convertible to either Object or
String, both are applicable.

The next step is to find the most specific method. "The informal
intuition is that one method declaration 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."

Any invocation that can be handled by method3(String) could also be
handled, without a compile-time type error, by method3(Object). More
formally, they appear in the same class, and String can be converted to
Object by method invocation conversion, but Object cannot be converted
to String by method invocation conversion.

Clearly, method3(String) is the most specific method, and is otherwise
appropriate for the call.

If method3(String) were commented out, method3(Object) would be the
only applicable and accessible method, so it would necessarily be the
most specific method.

Patricia
 
T

Thomas G. Marshall

Hemal Pandya coughed up:
Thomas said:
Thomas Hawtin coughed up:
(e-mail address removed) wrote: [....]

null isn't treated any differently from any other expression.
However null is odd in that it has the type which is a subtype of
every other (reference) type.

To be pedantic, I'm not sure that's true. The null reference is the
only permitted value of an expression of the null /type/. (JLS2
4.1). It can always be /cast/ to any reference type, but it is not
a subtype of anything.

Actually I think it is, at least per JLS3. 4.10: The subtypes of a
type T are all types U such that T is a supertype of U, and the null
type.

Fair enough. The rest of your post indicates mostly behavior, which does
not define. The sentence above nails it. Interesting, and thanks.

....[rip]...
 
T

Thomas G. Marshall

Patricia Shanahan coughed up:
Hello,

I have a question when passing null in the parameter when invoking a
method. Consider:

public class Test1

public void method3(Object o){

System.out.println("Object");
}

public void method3(String s){
System.out.println("string s");
}

/**
* @param args
*/
public static void main(String[] args) {
// TODO Auto-generated method stub
Test1 test = new Test1();
test.method3(null);
}

}

In JDK 1.5 or 5.0(latest one from sun), it will call method3(String
s). And if I comment out method3(String s), then it will call
method3(Object o).

I think this is covered by "15.12.2.2 Choose the Most Specific
Method",
http://java.sun.com/docs/books/jls/second_edition/html/expressions.doc.html#18428

The compile time processing of a method call finds the methods that
are both accessible (proper protection etc.) and applicable (suitable
parameter types). Clearly, both method3 declarations are accessible.
Because null is method invocation convertible to either Object or
String, both are applicable.

The next step is to find the most specific method.

Yep. What I find interesting is when the compiler has found two
alternatives of equal specificity. You end up seeing code that must cast
null, which always seems a little goofy, but is a requirement:

public class CastNull
{

static interface OneI {}
static interface TwoI {}

static class Foo implements OneI, TwoI {}

static public void method(OneI oneI) {}

static public void method(TwoI twoI) {}


public static void main(String[] args)
{
method((OneI)null); // required cast!
}
}

Now, there are situations where the cast is not required, because of the
specificity rule you mention. But they are not altogether incredibly clear,
IMO. But in a simple case:

public class CastNull2
{

static interface OneI {}

static class Foo implements OneI {}

static public void method(OneI oneI)
{
System.out.println("OneI method");
}

static public void method(Foo foo)
{
System.out.println("Foo method");
}


public static void main(String[] args)
{
method(null); // ok, calls Foo method
method((OneI)null); // ok, calls OneI method
}
}

....both method() calls are ok. The first one matches the null to the Foo
class. HOWEVER, I would strongly recommend that in similar situations that
one actually make the first method() call look like this:

method((Foo)null); // clinical paranoia

Only less confusion for the heavily caffeinated engineer at 3am.

....[rip]...
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,968
Messages
2,570,153
Members
46,701
Latest member
XavierQ83

Latest Threads

Top