I have two classes:
public class ClassA {
public void method(Number n) {
System.out.println("ClassA: " + n + " " + n.getClass());
}
}
and:
public class ClassB extends ClassA {
public void method(Integer d) {
System.out.println("ClassB: " + d + " " + d.getClass());
}
}
But when I run:
ClassA a = new ClassB();
a.method(3);
I get:
ClassA: 3 class java.lang.Integer
My question is, why isn't ClassB's method being used? a is an instance of ClassB, and ClassB's method() has an Integer parameter...
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
Your issue stems from the fact that (as quoted from the official Java tutorials on Inheritance):
In a subclass, you can overload the methods inherited from the superclass. Such overloaded methods neither hide nor override the superclass methods—they are new methods, unique to the subclass.`
Refer to the official Java tutorials for more details: http://docs.oracle.com/javase/tutorial/java/IandI/override.html
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
because number 3 is automatically boxed to Integer.
please see the link below: http://www.javabeat.net/articles/print.php?article_id=31
General Rule: Arguments are implicitly widened to match method parameters. It's not legal to widen from one wrapper class to another.
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.
Since the two operations have different argument (parameter) types (Even though they are subclasses) they are considered different (Unlike C) you did not override the first method with the second one. Instead you ended up with class B which now has two methods
public void method(Number n) and
public void method(Integer n)
by default when you did a.method(3) 3 was casted to an Integer object. You can verify this by calling
a.method((Number)3); //this would call the second method/operation.
You can also verify this by using reflection to iterate on class B's methods.
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
- Since the two methods are NOT overloaded and the instance is of class a, no dispatch occurs from A to B
- B has a best match method, then it's chosen
- B can't handle a parameter of type Float, so A method is chosen
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
I did some R&D on this problem and come up with solution to remove your confusion. Hopefully it will help you to understand.
Look for some below code:
class A {
public void func(Number obj){
System.out.println("In a func");
}
public void func(Integer obj){
System.out.println("In b func");
}
}
class B extends A {
}
public class X {
public static void main(String s[]){
B b = new B();
b.func(3);
A a = new B();
a.func(3);
}
}
If you will run this code you will get output:
"In b func"
"In b func".
In this case, here are 4 methods:
- class A is having both the overloaded methods: func(Number)[say method 1] and func(Integer)[say method 2]
- class B is also having 2 methods because of inheritance. So it has func(Number)[say method 3] and func(Integer)[say method 4]
Now when you call b.func(3) on reference of B, it will see the "method 3" and "method 4", which have parameter with most fit derived class. Here both Number and Integer classes are fit for argument 3, but Integer is derived from Number, so func(Integer)[method 3] will be called. Hence the output is "In b func"
Second output is also "In b method", because of same logic. First remember, you can not call any method on class A reference which class A is not having. So you can only call those methods on reference of class A which it have. No matter the instance is of class A or it's subclass's instance.
You need to understand it in 2 terms, compilation and linking and execution.
Now Class A is having both the methods, so when compiler look a.func(3) on reference of class A. Compiler will look into "methods 1" and "method 2" of class A and bind the method signature which have arguments with most fit derived class. So is "func(Integer)".
Now at run time, func(Integer) will be executed, it is called of class B, because instance is of class B. (At runtime, method is executed from the class, whose instance is calling the method). So method 4 is called. Hence the output.
I am sure, you will have confusion, why method 2 is not called and method 4 is called.
If you will run below code:
class A {
public void func(Number obj){
System.out.println("In a func");
}
public void func(Integer obj){
System.out.println("In b func");
}
}
class B extends A {
public void func(Number obj){
System.out.println("In a func of class B");
}
public void func(Integer obj){
System.out.println("In b func of class B");
}
}
public class X {
public static void main(String s[]){
B b = new B();
b.func(3);
A a = new B();
a.func(3);
}
}
Output will be:
In b func of class B
In b func of class B
Now you can understand this code with above explanation. Either we have called fun(3) on reference of class A or class B. Each time class B's method is called (method 4). Because the instance is of class B. But if class A will not have (method 2). Method 4 will not be called on "a.func(3)"
Lets have a look on below code:
class A {
public void func(Number obj){
System.out.println("In a func");
}
}
class B extends A {
public void func(Integer obj){
System.out.println("In b func");
}
}
public class X {
public static void main(String s[]){
B b = new B();
b.func(3);
A a = new B();
a.func(3);
}
}
Output of this program is:
In b func
In a func
Now you may have confusion why is it different output?
Remember, here are not 4 methods. Here are only 3 methods:
- func(Number) in class A
- func(Number) in class B, inherited from class A
- func(Integer) in class B
Now if you call a.fun(3), class A is not having func(Integer), and you can not call method on reference of class A which class not have. So compiler will not bind func(Integer) because there is no such method in class A. But there is another method func(Number) which can be called with the same code a.func(3) Autoboxing concept of java.
So when a.func(3) called, it basically call func(Number). Now because the instance is of class B, method func(Number) of class B is called. Hence the output is "In a func"
This is a very big answer, but I explained in deep, so you can easily understand different possibilities of outputs in different usecases.
Enjoy coding!
In overloading, method resolution always takes care by compiler based on reference type. So, In overloading runtime object[new ClassB()] does not play any role. Therefore, in your case the method of ClassA was executed.
ClassA a = new ClassB();
a.method(3);
来源:https://stackoverflow.com/questions/12832055/using-inherited-overloaded-methods