我想知道为什么要打电话
z.f(-6);
类M中的指的是B类中的以下函数:
public void f(double y) {
this.x = (int) y + B.y;
}
而不是在A类中使用函数f,因为b。涵盖了A。或者更确切地说是使用
public void f (int y) {
this.x = y*2;
B.y = this.x;
}
在B类中,其中至少参数类型匹配。
以下完整代码:
public class A {
public int x = 1;
public A(int x) {
this.x += x;
}
public A (double x) {
x += x;
}
public void f(double x) {
this.x = this.x + (int) (x + B.y);
}
}
public class B extends A {
public static int y = 3;
public int x = 0;
public B (double x) {
super((int) x);
}
public void f(int y) {
this.x = y*2;
B.y = this.x;
}
public void f(double y) {
this.x = (int) y + B.y;
}
}
public class M {
public static void main (String[] args){
A a = new A(B.y);
a.f(1);
B b = new B(3.0);
A z = b;
z.f(-5.0);
z.f(-6);
System.out.println(b.x + " " + z.x);
}
}
答案 0 :(得分:3)
z
的静态类型为A
,因此z.f(-6)
只能绑定到A
中的方法,在本例中为A.f(int)
。
语言是这样设计的,以便
A z = new B(3.0);
z.f(-6);
将始终与
相同A z = complicatedWayToComputeTrue() ? new B(3.0) : new A(3.0);
z.f(-6);
如果编译器要绑定到不同的方法签名,因为它可以证明A z
总是持有B
,那么这会为语言引入各种非局部效果,使其变得非常困难调试或维护java程序。
想象一下有人试图维持
final A z = complicatedWayToComputeTrue() ? new B(3.0) : new A(3.0);
// 1000 lines elided
z.f(-6);
将其更改为
A z = new B(3.0);
// 1000 lines elided
z.f(-6);
如果编译器现在可以证明A
始终是B
并将Z.f
绑定到B
中的方法,那么维护者将会感到困惑。
答案 1 :(得分:3)
z.f(-6);
z
的静态类型为A
,其中只有一个名为f
的方法。该方法采用double
参数,可以提升文字值-6
。因此,在编译时,调用绑定到A.f(double)
。
在运行时z
发现类型为B
,它会使用自己的A.f(double)
覆盖B.f(double)
,这样就可以调用此方法。
答案 2 :(得分:1)
Java是单一调度,而您尝试执行的是double dispatch(调用的方法取决于动态运行时类和参数)。
在Java中调用的方法的签名是在编译时确定的;这意味着对象的声明类确定绑定哪个方法。覆盖子类中的方法会影响绑定的实现,但是重载方法不会(因为重载的方法具有不同的签名)。
在类B中,你使用一个带有int的版本重载f(),当使用一个声明为类A的对象时,这个方法似乎不存在(你不能调用它,也不会调用它) )。
总结:
答案 3 :(得分:0)
我可能错了,但如果您将z
类型的对象A
链接到类型为B
的对象,它仍会绑定为B
类型,这就是它在类B
而不是在类A
中执行方法的原因。请注意,由于您未使用A
,因此不会创建new
类型的对象。
正如Mike Samuel所说,默认-6
应被视为int
,但这不符合您的解释。我会试着找到一个合适的答案。