4

这个问题是在我和我的朋友们准备考试的时候出现的。在调用静态分配变量的方法时,我们注意到了奇怪的行为。

代码>单词,所以我们开始:

class C {

    public void a(double x) {}
}

class D extends C {
    void b() {} 
}

class F extends D {

    void a(int i) {} 
    void a(double d) {} 

现在,做

D foo = new F();
foo.a(1);

这会带来什么?好吧..它在F中运行方法a(double)

这是我们认为发生的事情:

  1. 程序首先在静态类型D中寻找一个:那里什么都没有。
  2. 转到它的超类C。没有找到a(int)而是找到a(double)
  3. 决定这是我的意思的签名(即a(双)),但首先,在所有这些搜索之后,让我们先看看它说的动态类型!
  4. 在 F 中运行a(double) !

它是否正确?这意味着它爬上了层次结构以找到一些适合从 int 到 double 的类型转换的方法。在此之后,它检查动态类型是否具有这个新解释的签名。

我注意到如果我添加

void a(int) {}

** 在C 类中,运行上面的调用时,它会在F中给我a(int) !

有人可以解释一下这个过程中涉及的机制吗?为什么代码会以这种方式运行/编译?这背后的技术原因是什么?关于类似情况,还有什么需要注意的吗()

4

2 回答 2

7

原因是因为 Java 是静态类型的。参数类型的调度是在编译时完成的,而不是运行时。

编译代码时,编译器会看到您正在调用a在静态类型对象上命名的方法D。它在 中查找兼容的方法D,并找到一个方法(继承自C)。它生成代码以执行对C.a(double).

在运行时,虚拟调用调度对象的实际类型(不是参数!),所以它最终调用F.a(double),因为它覆盖了C.a(double).

对象的运行时类型是F并且F碰巧具有不同的方法,如果它在编译时已知,则该方法将是有效的这一事实是无关紧要的。如果你想要这种行为,你需要反思。

如果您添加了C.a(int),编译器将看到名为ain 的两种不同方法D,并根据重载规则选择采用 int 的方法。

于 2012-12-16T04:27:34.267 回答
4

要调用的方法在编译时解析,而不是运行时解析。由于编译器只在编译时知道 foo 是 a D,因此只有一个 method a(double),即被调用的方法。然而,准确a(double)地调用哪个对象是动态完成的(在运行时),这就是为什么F.a(double)会被调用而不是C.a(double).

这称为单一调度:调用的方法在它被调用的对象上是动态的,但在其参数类型上是静态的。

如果你C完全删除了那个方法,它就不会调用a(int)F它只会编译失败,说它找不到那个方法。

于 2012-12-16T04:28:06.973 回答