使用inheritance的重载方法
我有两节课:
public class ClassA { public void method(Number n) { System.out.println("ClassA: " + n + " " + n.getClass()); } }
和:
public class ClassB extends ClassA { public void method(Integer d) { System.out.println("ClassB: " + d + " " + d.getClass()); } }
但是当我跑步时:
ClassA a = new ClassB(); a.method(3);
我明白了:
ClassA: 3 class java.lang.Integer
我的问题是,为什么不使用ClassB
的方法? a
是ClassB
一个实例,而ClassB
的method()
有一个Integer
参数……
我的问题是,为什么不使用ClassB的方法?
不对。 使用的方法是ClassB
的方法,它inheritance自ClassA
。
我认为这里混淆的主要原因是该方法实际上没有被覆盖 ,而是重载 。 尽管Integer
是Number
的子类型,但由于方法参数在Java中是不变的,因此方法public void method(Integer d)
不会覆盖方法public void method(Number n)
。 因此, ClassB
最终会有两个(重载)方法。
静态绑定用于重载方法,编译器选择具有最特定参数类型的方法。 但在这种情况下,为什么编译器选择public void method(Number n)
而不是public void method(Integer d)
。 这是因为您用于调用方法的引用是ClassA
类型。
ClassA a = new ClassB(); //instance is of ClassB (runtime info) a.method(3); //but the reference of type ClassA (compiletime info)
ClassA
唯一的方法是public void method(Number n)
,这就是编译器选择的方法。 请记住,这里预期的参数类型是Number
,但传递的实际参数(整数3)是自动装入Integer
类型的。 它起作用的原因是因为方法参数在Java中是协变的。
现在,我认为很清楚它打印的原因
ClassA:3类java.lang.Integer
您的问题源于这样的事实(引自官方Javainheritance的教程):
在子类中,您可以重载从超类inheritance的方法。 这样的重载方法既不隐藏也不重写超类方法 – 它们是新方法,对于子类来说是唯一的
有关更多详细信息,请参阅官方Java教程: http : //docs.oracle.com/javase/tutorial/java/IandI/override.html
a是ClassA类型,因此ClassB中的方法对于实例a是不可见的 ,除非它被声明为ClassB
ClassB a = new ClassB();
将产生您的预期输出。 Number是Integer的超类型。 因此,无论您传入的是什么,都将自动装入适当的子类型,并调用ClassA中的方法。 尝试传球
a.method(3.0f) // Float a.method(3.0) // Double
因为数字3会自动装箱到整数。
请参阅以下链接: http : //www.javabeat.net/articles/print.php?article_id = 31
常规规则:隐式扩展参数以匹配方法参数。 从一个包装类扩展到另一个包装类是不合法的。
因为参数中的Number和Integer创建了两个不同的方法签名。 因此,B类只有两种不同的方法可供使用。
由于这两个操作具有不同的参数(参数)类型(即使它们是子类),因此它们被认为是不同的(与C不同),您没有使用第二个方法覆盖第一个方法。 相反,你最终得到了B类,现在有两种方法
public void method(Number n) and public void method(Integer n)
默认情况下,当您执行a.method(3)3时,会将其转换为Integer对象。 您可以通过致电来validation这一点
a.method((Number)3); //this would call the second method/operation.
您还可以通过使用reflection来迭代B类的方法来validation这一点。
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
- 由于这两个方法没有重载且实例属于类a,因此从A到B不会发生任何调度
- B有一个最佳匹配方法,然后选择它
- B无法处理Float类型的参数,因此选择了一种方法
要清除我在classA
和classB
添加的show()
方法。
public void show() { System.out.println(getClass()); }
我打电话是这样的,
// 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
这里方法(数字n)和方法(整数d)具有不同的签名。 它没有压倒一切。 它正在超载。
但show()方法是方法重写。
在案例1中, 只有对象a可以访问A类的方法。 a是类型classA,类B中的方法不可见。 这就是你调用classA方法的原因。 但是对于show()方法,因为它是重写方法,所以调用类B的show()方法。
在案例2中,A类和B类的两种方法都可以通过对象b访问,因为ClassB扩展了ClassA。
您有以下代码
Class A a = new ClassB(); a.method(3);
但是假设你有一个方法,其中“a”和“3”作为参数传递给你,你仍然执行相同的代码
public void foo(A a, Number n) { a.method(n); }
编译器不知道您是要传递A类还是B类(或数字或整数)。 它仍然必须解析方法,以便它可以从a.method进行类型检查返回值
我做了一些关于这个问题的研发,并提出解决方案来消除你的困惑。 希望它能帮助你理解。
寻找以下代码:
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); } }
如果您将运行此代码,您将获得输出:
“在b func”
“在b func” 。
在这种情况下,这里有4种方法:
- A类有两个重载方法:func(Number)[说方法1]和func(整数)[说方法2]
- 由于inheritance,B类也有2种方法。 所以它有func(Number)[说方法3]和func(整数)[说方法4]
现在当你在B的引用上调用b.func(3)时,它将看到“方法3”和“方法4”,它们具有最适合派生类的参数。 这里,Number和Integer类都适合参数3 ,但Integer是从Number派生的,因此将调用func(Integer)[方法3]。 因此输出是“In b func”
第二个输出也是“In b方法”,因为逻辑相同。 首先要记住,你不能在A类没有的A类引用上调用任何方法。 所以你只能在它所拥有的A类的引用上调用这些方法。 无论实例是A类还是子类的实例。
您需要用2个术语来理解它,编译,链接和执行。
现在,A类同时具有这两种方法,因此当编译器在类A的引用上查看a.func(3)时,编译器将查看类A的“方法1”和“方法2”并绑定具有参数的方法签名。最合适的派生类。 那么是“func(整数)”。
现在在运行时,将执行func(Integer),它被调用为B类,因为实例是B类。(在运行时,方法是从类中执行的,其实例正在调用方法)。 所以调用方法4。 因此输出。
我相信,你会有困惑,为什么不调用方法2而调用方法4。
如果您将运行以下代码:
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); } }
输出将是:
在B类的b函数中
在B类的b函数中
现在您可以通过上述说明理解此代码。 我们要么在A类或B类的引用上调用fun(3)。每次调用B类的方法时(方法4)。 因为实例属于B类。但是如果A类没有(方法2)。 不会在“a.func(3)”上调用方法4
让我们看看下面的代码:
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); } }
该计划的输出是:
在bfunction
在一个function
现在你可能会感到困惑,为什么输出不同?
请记住,这里不是4种方法。 这里只有3种方法:
- A类中的func(Number)
- B类中的func(Number),inheritance自A类
- B类中的func(Integer)
现在,如果你调用a.fun(3),A类没有func(Integer),你就不能在类A的引用上调用哪个类没有。 因此编译器不会绑定func(Integer),因为A类中没有这样的方法。但是还有另一种方法func(Number)可以使用相同的代码调用a.func(3)java的Autoboxing概念。
所以当a.func(3)调用时,它基本上调用func(Number)。 现在因为实例是B类,所以调用了B类的方法func(Number)。 因此输出是“在一个function”
这是一个非常大的答案,但我深入解释,因此您可以轻松了解不同用例的输出的不同可能性。
享受编码!
在重载时,方法解析始终由编译器根据引用类型进行处理。 因此,在重载运行时对象[new ClassB()]不起任何作用。 因此,在您的情况下,执行了ClassA的方法。
ClassA a = new ClassB(); a.method(3);