22

我有两节课:

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使用 's 方法? a是 的一个实例ClassB,而ClassB'smethod()有一个Integer参数...

4

11 回答 11

26

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

不对。使用的方法是ClassB的方法,它继承自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). 这是因为您用来调用该方法的引用是 type 的ClassA

ClassA a = new ClassB(); //instance is of ClassB (runtime info)
a.method(3);             //but the reference of type ClassA (compiletime info)

唯一的方法ClassApublic void method(Number n),所以这就是编译器选择的方法。请记住,这里预期的参数类型是Number,但传递的实际参数(整数 3)被自动装箱为类型Integer。它起作用的原因是方法参数在 Java 中是协变的。

现在,我想很清楚为什么它会打印

ClassA:3类java.lang.Integer

于 2012-10-11T04:25:51.143 回答
10

您的问题源于以下事实(引用自有关继承的官方 Java 教程):

在子类中,您可以重载从超类继承的方法。这种重载的方法既不会隐藏也不会覆盖超类方法——它们是新方法,是子类独有的。

有关详细信息,请参阅官方 Java 教程:http: //docs.oracle.com/javase/tutorial/java/IandI/override.html

于 2012-10-11T04:10:24.947 回答
6

a 是 ClassA 类型,因此 ClassB 中的方法对实例 a不可见,除非它被声明为 ClassB

ClassB a = new ClassB(); 

将产生您预期的输出。Number 是 Integer 的超类型。因此,您传入的任何内容都将自动装箱为适当的子类型,并且将调用 ClassA 中的方法。尝试通过

a.method(3.0f) // Float
a.method(3.0) // Double
于 2012-10-11T04:33:20.457 回答
4

因为参数中的 Number 和 Integer 创建了两个不同的方法签名。因此,B 类只有两种不同的方法可供使用。

于 2012-10-11T04:09:54.140 回答
4

因为数字 3 自动装箱为整数。

请看下面的链接: http ://www.javabeat.net/articles/print.php?article_id=31

一般规则:参数被隐式扩展以匹配方法参数。从一个包装类扩展到另一个是不合法的。

于 2012-10-11T04:13:21.617 回答
1

由于这两个操作具有不同的参数(参数)类型(即使它们是子类),它们被认为是不同的(与 C 不同),您没有用第二个方法覆盖第一个方法。相反,你最终得到了 B 类,它现在有两种方法

 public void method(Number n)  and
 public void method(Integer n)

默认情况下,当您执行 a.method(3) 3 时,它被强制转换为 Integer 对象。您可以通过调用来验证这一点

a.method((Number)3);      //this would call the second method/operation.

您还可以通过使用反射迭代 B 类的方法来验证这一点。

于 2012-10-11T04:22:33.303 回答
1
  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 类型的参数,所以选择了 A 方法
于 2012-10-11T04:33:04.513 回答
1

为了清除我添加的show()方法classAclassB.

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

这里 method(Number n) 和 method(Integer d) 有不同的签名。它不是压倒一切的。它正在超载。

但是 show() 方法是方法覆盖。

在情况 1 中, 对象 a 只能访问类 A 的方法。a 是 classA 类型,classB 中的方法不可见。 这就是调用您的 classA 方法的原因。但是对于 show() 方法,因为它是被覆盖的方法,所以会调用 B 类的 show() 方法。

在情况 2 中,类 A 和 B 的两个方法都可以通过对象 b 访问,因为 ClassB 扩展了 ClassA。

于 2012-10-11T05:10:48.097 回答
0

您有以下代码

Class A a = new ClassB(); 
a.method(3);

但是假设您有一个方法,其中“a”和“3”作为参数传递给您,您仍然执行相同的代码

public void foo(A a, Number n)
{
    a.method(n);
}

编译器不知道您是要传递 A 类还是 B 类(或数字或整数)。它仍然必须解析该方法,以便它可以对 a.method 的返回值进行类型检查

于 2013-02-20T21:55:25.483 回答
0

我对这个问题进行了一些研发,并提出了解决方案来消除你的困惑。希望它能帮助你理解。

查找以下代码:

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);
    }
}

如果您将运行此代码,您将得到输出:
"In b func"
"In b func"

在这种情况下,这里有 4 种方法:

  1. A 类同时具有重载方法:func(Number)[say method 1] 和 func(Integer)[say method 2]
  2. 由于继承,B 类也有 2 个方法。所以它有 func(Number)[say method 3] 和 func(Integer)[say method 4]

现在,当您在 B 的引用上调用 b.func(3) 时,它将看到“方法 3”和“方法 4”,它们具有最适合派生类的参数。这里 Number 和 Integer 类都适合参数3,但是 Integer 是从 Number 派生的,所以 func(Integer)[method 3] 将被调用。因此输出是“In b func”

由于相同的逻辑,第二个输出也是“In b 方法”。首先请记住,您不能在 A 类引用上调用 A 类没有的任何方法。所以你只能在它拥有的类 A 的引用上调用这些方法。无论实例是 A 类还是它的子类的实例。

您需要从两个方面来理解它,编译和链接和执行。

现在 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);
    }
}

该程序的输出是:
In b func
In a func

现在您可能会感到困惑,为什么它的输出不同?

请记住,这里不是 4 种方法。这里只有3种方法:

  1. A 类中的 func(Number)
  2. B 类中的 func(Number),继承自 A 类
  3. B 类中的 func(Integer)

现在如果你调用 a.fun(3),A 类没有 func(Integer),你不能调用 A 类没有引用的方法。所以编译器不会绑定 func(Integer) 因为类 A 中没有这样的方法。但是还有另一个方法 func(Number) 可以用相同的代码调用 a.func(3) java 的自动装箱概念。

所以当a.func(3)被调用时,它基本上是调用func(Number)。现在因为实例属于 B 类,所以调用 B 类的方法 func(Number)。因此输出是“In a func”

这是一个很大的答案,但我解释得很深入,所以你可以很容易地理解不同用例中输出的不同可能性。

享受编码!

于 2017-03-08T09:51:23.387 回答
-1

在重载中,方法解析总是由编译器根据引用类型来处理。因此,在重载运行时对象[new ClassB()] 中没有任何作用。因此,在您的情况下,执行了 ClassA 的方法。

ClassA a = new ClassB(); 
a.method(3);
于 2017-09-05T12:32:58.180 回答