使用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的方法? aClassB一个实例,而ClassBmethod()有一个Integer参数……

我的问题是,为什么不使用ClassB的方法?

不对。 使用的方法是ClassB的方法,它inheritance自ClassA


我认为这里混淆的主要原因是该方法实际上没有被覆盖 ,而是重载 。 尽管IntegerNumber的子类型,但由于方法参数在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 
  1. 由于这两个方法没有重载且实例属于类a,因此从A到B不会发生任何调度
  2. B有一个最佳匹配方法,然后选择它
  3. B无法处理Float类型的参数,因此选择了一种方法

要清除我在classAclassB添加的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种方法:

  1. A类有两个重载方法:func(Number)[说方法1]和func(整数)[说方法2]
  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种方法:

  1. A类中的func(Number)
  2. B类中的func(Number),inheritance自A类
  3. 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);