答案 0 :(得分:3)
该方法的动态绑定在运行时在具有相同签名的不同实现之间应用,但是当编译器查看签名时,静态地确定所有可能方法的集合。由于您将p
声明为Parent
类,因此在运行时它将查找Parent
类中存在的方法,如果存在更具体的实现(因为子类) ,就像在你的例子中),然后它将被选择为祖先。
由于您的方法没有覆盖任何内容,因此在编译时它将选择一个不同的签名(Object
的签名)并忽略它。由于属性p
的类型,它在运行时似乎不是匹配的可能性。
答案 1 :(得分:0)
您上传了Child
至Parent
Parent p = new Child();
当您调用p.doIt("test");
时,您将获得Child
行为的唯一方法是通过重写方法。
由于您已将Child
中的方法更改为doIt(String s)
,因此不再覆盖Parent
中的任何内容,因此doIt(Object o)
中会调用Parent
。
答案 2 :(得分:0)
您的答案实际上非常简单:您对方法的更改破坏了对继承的使用。
Parent只有一个doIt方法,它接受一个Object参数。当您在父级上调用doIt(“test”)时,它会查看是否在子级中覆盖它。由于未在子节点中覆盖doIt(Object s),因此使用父方法。即使你传递一个String,仍会调用父方法,因为doIt(Object s)与doIt(String s)不同。
简单地说,当您更改签名时,您没有覆盖该方法,而是超载它。
答案 3 :(得分:0)
要调用的方法签名是在编译时确定的,因此p.doIt(“test”)将在运行时调用相应类的doIt(Object o)方法。 doIt(String s)甚至没有看过。想象一下,当编写Poly makeItHappen时,Child.java不存在 - 您还必须将Child构造函数抽象为另一个类中的工厂方法以使其编译。您可以重新实现工厂方法和Child.java,而无需重新编译Poly.java。这允许您编程到基类提供的接口和相对有效的函数调用。您的Poly makeItHappen只需要知道doIt(Object o)存在。
如果您考虑通过虚函数表实现继承的可能性,那么doIt(String s)和doIt(Object o)具有不同的表条目。 Parent只有doIt(Object o)的条目。 Child既有doIt(Object o)的条目,它是与Parent相同的主体,也是doIt(String s)的条目,它是它自己的条目。在编译时,要调用的方法是doIt(Object o)槽中的方法。
答案 4 :(得分:0)
嗯,试图解决'为什么'运行时没有播放隐藏和寻找与整个类定义寻找'更好'匹配方法比编译器要求的方法,将举例。显然这个API首先是可怕的,但是你可以想象使用很长的方法签名会不会意外地发生这种情况。
/**
* Vendor API you program to
*/
public class IPv4Manager {
public void terminateSocket(Object obj) {
//terminate IPv4 socket
}
}
/**
* Vendor class that's injected at runtime that you have no knowledge of
* and do not compile against.
*/
public class IPv4And6Manager extends IPv4Manager {
public void terminateSocket(Byte[] packet) {
//terminate IPv6 socket
}
}
/**
*Your user code
*/
public void terminateIPv4Socket(Byte[] packet) {
IPv4Manager manager = managerFactory.getV4Manager(); //returns you an instance of 4And6Manager
manager.terminateSocket(packet);
}
你是否真的希望运行时试图超越你并调用比编译器要求的“更好”的方法?