Javadynamic绑定和方法重写

昨天我接受了一个两小时的技术电话面试(我通过了,呜呼!),但是我完全禁止了关于Java中dynamic绑定的问题。 而这又令人费解,因为几年前当我是一名助教时,我曾经把这个概念传授给大学生,所以我给他们提供错误信息的前景有些令人不安。

这是我给的问题:

/* What is the output of the following program? */ public class Test { public boolean equals( Test other ) { System.out.println( "Inside of Test.equals" ); return false; } public static void main( String [] args ) { Object t1 = new Test(); Object t2 = new Test(); Test t3 = new Test(); Object o1 = new Object(); int count = 0; System.out.println( count++ );// prints 0 t1.equals( t2 ) ; System.out.println( count++ );// prints 1 t1.equals( t3 ); System.out.println( count++ );// prints 2 t3.equals( o1 ); System.out.println( count++ );// prints 3 t3.equals(t3); System.out.println( count++ );// prints 4 t3.equals(t2); } } 

我声称输出应该是在重载的equals()方法中的两个单独的打印语句:在t1.equals(t3)t3.equals(t3) 。 后一种情况是显而易见的,在前一种情况下,尽pipet1引用了Objecttypes的引用,但它被实例化为Testtypes,所以dynamic绑定应该调用被覆盖的方法forms。

显然不是。 我的面试官鼓励我自己运行这个程序,而且看到,只有一个输出来自于重写的方法:在t3.equals(t3)

我的问题是,为什么? 正如我已经提到的,即使t1是Objecttypes的引用(所以静态绑定会调用Object的equals()方法),dynamic绑定应该根据引用的实例化types来调用最具体的方法版本。 我错过了什么?

Java对重载的方法使用静态绑定,对重写的方法使用dynamic绑定。 在你的例子中,equals方法被重载(具有与Object.equals()不同的参数types),所以调用的方法在编译时绑定到引用types。

这里有一些讨论

事实上,这是平等的方法是不相关的,除了超载,而不是覆盖它是一个常见的错误,你已经知道,根据你对面试中的问题的答案。

编辑: 这里也是一个很好的描述。 这个例子显示了一个与参数types相关的类似问题,而是由相同的问题引起的。

我相信,如果绑定实际上是dynamic的,那么任何情况下,调用者和参数是一个实例的testing将导致重写的方法被调用。 所以t3.equals(o1)将是唯一不会打印的情况。

Testequals方法不覆盖java.lang.Objectequals方法。 看看参数types! Test类正在使用接受Test的方法重载equals

如果equals方法打算重写,它应该使用@Override注释。 这会导致编译错误,指出这个常见的错误。

有趣的是,在Groovy代码(可以编译成一个类文件)中,只有一个调用会执行print语句。 (将Test与Object进行比较显然不会调用Test.equals(Test)函数)。这是因为groovy确实可以完全dynamic的input。 这是特别感兴趣的,因为它没有任何显式dynamictypes的variables。 我曾经在几个地方读过,认为这被认为是有害的,因为程序员希望groovy去做java事情。

Java不支持参数的协变,只在返回types中。

换句话说,尽pipe重写方法中的返回types可能是重写的内容的子types,但对于参数来说则不是这样。

如果你在Object中的equals参数是Object,那么在一个子类中放入一个等于其他任何东西的东西将是一个重载的,而不是重写的方法。 因此,该方法将被调用的唯一情况是当参数的静态types是Test时,如T3的情况。

与求职面试过程祝你好运! 我很乐意接受这样一个问题,而不是我教给我的学生常见的algorithm/数据结构问题。

我认为关键在于equals()方法不符合标准:它接受另一个Test对象,而不是Object对象,因此不会覆盖equals()方法。 这意味着你实际上只有重载它才能在给定的时候给它一个特殊的对象。Object对象调用Object.equals(Object o)。 通过任何IDE查看代码都会显示两个用于testing的equals()方法。

该方法被重载而不是重写。 等于总是以Object为参数。

顺便说一句,在Bloch的有效的Java(你应该拥有)你有一个项目。

dynamic绑定 (DD)和静态绑定 (SB)中的一些注意事项search一段时间后:

1.定时执行 :(参考1)

  • DB:在运行时
  • SB:编译时间

2.用于

  • DB:重写
  • SB:超载(静态,私人,最终)(参考2)

参考:

  1. 执行哪个方法更喜欢使用的平均parsing器
  2. 因为不能用修饰符static,private或final来覆盖方法
  3. http://javarevisited.blogspot.com/2012/03/what-is-static-and-dynamic-binding-in.html

如果添加了另一个方法,覆盖而不是重载,它将在运行时解释dynamic绑定调用。

/ *以下程序的输出是什么? * /

 public class DynamicBinding { public boolean equals(Test other) { System.out.println("Inside of Test.equals"); return false; } @Override public boolean equals(Object other) { System.out.println("Inside @override: this is dynamic binding"); return false; } public static void main(String[] args) { Object t1 = new Test(); Object t2 = new Test(); Test t3 = new Test(); Object o1 = new Object(); int count = 0; System.out.println(count++);// prints 0 t1.equals(t2); System.out.println(count++);// prints 1 t1.equals(t3); System.out.println(count++);// prints 2 t3.equals(o1); System.out.println(count++);// prints 3 t3.equals(t3); System.out.println(count++);// prints 4 t3.equals(t2); } } 

我发现了一篇关于dynamic与静态绑定的有趣文章。 它带有一个模拟dynamic绑定的代码。 它使我的代码更具可读性。

https://sites.google.com/site/jeffhartkopf/covariance

另请参阅这个问题,密切相关: 重写JAVA等于方法怪癖

“为什么?”这个问题的答案 这就是Java语言的定义。

引用维基百科有关协变和逆变的文章 :

返回types协方差在Java编程语言版本J2SE 5.0中实现。 参数types必须与方法重写完全相同(不变),否则该方法被重载并行定义。

其他语言是不同的。

很明显,这里没有压倒一切的概念。 这是方法重载。 Object类的Object()方法接受Objecttypes的引用参数,这个equals equal()方法接受Testtypes的引用参数。

我将尝试通过两个例子来解释这个例子,这些例子是我在网上遇到的一些例子的扩展版本。

 public class Test { public boolean equals(Test other) { System.out.println("Inside of Test.equals"); return false; } @Override public boolean equals(Object other) { System.out.println("Inside of Test.equals ot type Object"); return false; } public static void main(String[] args) { Object t1 = new Test(); Object t2 = new Test(); Test t3 = new Test(); Object o1 = new Object(); int count = 0; System.out.println(count++); // prints 0 o1.equals(t2); System.out.println("\n" + count++); // prints 1 o1.equals(t3); System.out.println("\n" + count++);// prints 2 t1.equals(t2); System.out.println("\n" + count++);// prints 3 t1.equals(t3); System.out.println("\n" + count++);// prints 4 t3.equals(o1); System.out.println("\n" + count++);// prints 5 t3.equals(t3); System.out.println("\n" + count++);// prints 6 t3.equals(t2); } } 

这里,对于计数值为0,1,2和3的行, 我们在equals()方法中引用o1t1Object 。 因此,在编译时, Object.class文件中的equals()方法将是有界的。

但是,即使t1的 引用Object ,它也具有Test类的 初始化
Object t1 = new Test();
因此,在运行时调用public boolean equals(Object other)这是一个

重写的方法

在这里输入图像描述

现在,对于计数值为4和6, t3 引用初始化 Test的方法是直接调用equals()方法,其参数为对象引用,并且是

重载的方法

好!

同样,为了更好地理解编译器将调用的方法,只需单击该方法,Eclipse将突出显示它认为在编译时调用的类似types的方法。 如果它在编译时没有被调用,那么这些方法就是方法重写的一个例子。

在这里输入图像描述