I have two classes:
public class ClassA {
public void method(Number n) {
System.out.println(\"ClassA: \" + n + \" \" + n.getClass());
}
}
a is of type ClassA so the methods in ClassB will not be visible to instance a unless it is declared as ClassB
ClassB a = new ClassB();
will produce your expected output. Number is the supertype of Integer. So whatever you pass in will be autoboxed to appropriate subtype and the method in ClassA will be called. Try passing
a.method(3.0f) // Float
a.method(3.0) // Double
To clear I added, show()
method in both classA
and classB
.
public void show() {
System.out.println(getClass());
}
I call like this,
// Case 1
ClassA a = new ClassB();
a.method(3);// ClassA: 3 class java.lang.Integer
a.show(); // class ClassB
// Case 2
ClassB b = new ClassB();
b.method(3);// ClassB: 3 class java.lang.Integer
b.show(); // class ClassB
Here method(Number n) and method(Integer d) have different signatures. It is not overriding. It is overloading.
But show() method is method overriding.
In case 1, Only methods of class A are accessible with object a. a is type classA, methods in classB are not visible. That's why your classA method is called. But for show() method as it is overridden method, class B's show() method is called.
In case 2, Both methods of class A and B are accessible with object b as ClassB extends ClassA.
You had the following code
Class A a = new ClassB();
a.method(3);
But imagine you have a method where "a" and "3" are passed to you as a parameter and you still execute the same code
public void foo(A a, Number n)
{
a.method(n);
}
The compiler does not know whether you are going to pass Class A or Class B (or Number or Integer). It must still resolve the method so it can do type checking for the return value from a.method
My question is, why isn't ClassB's method being used?
Not true. The method used is ClassB
's method, which it inherited from ClassA
.
I think the main reason behind the confusion here is the fact that the method actually is not overridden, instead it is overloaded. Although Integer
is a subtype of Number
, since method parameter is invariant in Java, the method public void method(Integer d)
doesn't override the method public void method(Number n)
. So, ClassB
ends up having two (overloaded) methods.
Static binding is used for overloaded methods, and the method with most specific parameter type is chosen by the compiler. But in this case, why does the compiler pick public void method(Number n)
instead of public void method(Integer d)
. That's because of the fact that the reference that you are using to invoke the method is of type ClassA
.
ClassA a = new ClassB(); //instance is of ClassB (runtime info)
a.method(3); //but the reference of type ClassA (compiletime info)
The only method that ClassA
has is public void method(Number n)
, so that's what the compiler picks up. Remember, here the expected argument type is Number
, but the actual argument, the integer 3, passed is auto-boxed to the type Integer
. And the reason that it works is because the method argument is covariant in Java.
Now, I think it's clear why it prints
ClassA: 3 class java.lang.Integer
class ClassA
{
public void method( Number n )
{
System.out.println( "ClassA: " + n + " " + n.getClass() );
}// void method( Number n )
}// class ClassA
public class ClassB
extends
ClassA
{
public void method( Integer d )
{
System.out.println( "ClassB: " + d + " " + d.getClass() );
}// void method( Integer d )
public static void main( String[] args )
{
ClassB b = new ClassB();
ClassA a = b;
a.method( new Integer( 3 )); // 1. ClassA: 3 class java.lang.Integer
b.method( new Integer( 4 )); // 2. ClassB: 4 class java.lang.Integer
b.method( new Float( 5.6 )); // 3. ClassA: 5.6 class java.lang.Float
}// void main( String[] args )
}// class ClassB
Because Number and Integer in the arguments creates two different method signatures. So, class B just has two different methods that are available to use.