继承和多态是否构成IS-A关系?并且在运行时发生继承和“重写”多态是真的,而在编译时发生“重载”多态吗?我之所以这么说是因为很多论坛似乎都给出了相互矛盾且常常令人困惑的答案。
谢谢!
答案 0 :(得分:8)
对于问题的第一部分,我认为Wikipedia提供了一个很好的定义:
在面向对象的编程中,子类型多态或包含 多态性是类型理论中的一个概念,其中一个名称可以表示 许多不同类的实例,只要它们相关联 一些常见的超级课程。包含多态性通常是 通过子类型支持,即不同类型的对象 完全可替代另一种类型的对象(它们的基础 type(s))因此可以通过通用接口处理。 或者,可以通过类型实现包含多态性 强制,也称为铸造。
另一个名为Polymorphism in object-oriented programming的维基百科文章似乎很好地回答了你的问题。本文中的第二个参考文献On Understanding Types, Data Abstraction, and Polymorphism也非常详细地介绍了这些问题。
Java中的这种子类型功能是通过继承类和接口来实现的。尽管Java的子类型特征在继承方面可能并不明显。例如,协方差和与泛型相反的情况。此外,数组是Serializable和Cloneable,尽管在类型层次结构中的任何位置都不明显。也可以说通过原始扩展转换,Java中的数字类型也是多态的。并且运算符根据其操作数进行多形态处理。
无论如何,继承在实现某些多态性中起着重要作用。
重载与覆盖
问题的第二部分似乎是关于选择给定方法的实现。显然,如果一个类重写了一个方法并且你创建了该类的实例,那么即使你通过父类的引用访问该对象,也希望调用该方法的重写版本。
方法的正确实现的选择是在运行时完成的,正如您所指出的那样,现在要调用的方法的签名是在编译时决定的。由于重载是关于具有相同名称和不同签名的不同方法,因此可以说在编译时重写方法选择。
在编译时覆盖方法选择
第15.12节Java Language Specification中的Method Invocation Expressions(JLS)详细解释了编译器选择正确调用方法所遵循的过程。
在那里,您会注意到这是编译时任务。 JLS在第15.12.2小节中说:
此步骤使用方法名称和参数表达式的类型 找到既可访问又适用的方法 可能存在多个这样的方法,在这种情况下,选择最具体的方法。
要验证此编译时的性质,您可以进行以下测试。
声明一个这样的类并编译它。
public class ChooseMethod {
public void doSomething(Number n){
System.out.println("Number");
}
}
声明第二个类,它调用第一个方法并编译它。
public class MethodChooser {
public static void main(String[] args) {
ChooseMethod m = new ChooseMethod();
m.doSomething(10);
}
}
如果您调用main,则输出显示为Number
。
现在,向ChooseMethod
类添加第二个更具体的重载方法,并重新编译它(但不要重新编译其他类)。
public void doSomething(Integer i) {
System.out.println("Integer");
}
如果再次运行main,则输出仍为Number
。
基本上,因为它是在编译时决定的。如果重新编译MethodChooser
类(具有main的类)并再次运行程序,则输出将为Integer
。
因此,如果要强制选择其中一个重载方法,则参数类型必须与编译时参数的类型相对应,而不仅仅是在运行时。
在运行时覆盖方法选择
同样,方法的签名在编译时决定,但实际的实现是在运行时决定的。
声明一个这样的类并编译它。
public class ChooseMethodA {
public void doSomething(Number n){
System.out.println("Number A");
}
}
然后声明第二个扩展类并编译:
public class ChooseMethodB extends ChooseMethodA { }
在MethodChooser类中,您可以:
public class MethodChooser {
public static void main(String[] args) {
ChooseMethodA m = new ChooseMethodB();
m.doSomething(10);
}
}
如果你运行它,你得到输出Number A
,这是好的,因为该方法尚未在ChooseMethodB
中被覆盖,因此被调用的实现是ChooseMethodA
的实现
现在,在MethodChooserB
中添加覆盖方法:
public void doSomething(Number n){
System.out.println("Number B");
}
重新编译这个,然后再次运行main方法。
现在,您获得输出Number B
因此,在运行时选择了实现,而不需要重新编译MethodChooser
类。
答案 1 :(得分:5)
多态性:不同对象接收相同消息并以不同方式响应的能力。
继承一种实现它的方法,但不是必需的。见Duck Typing
重载方法是'编译时语法助手' - 因为每个方法在编译后都会获得唯一的签名。与多态无关。
答案 2 :(得分:1)
我认为你是对的。
多态性认为对象的运行时类型决定了方法的执行,并且选择哪个重载的方法在运行时不会以动态方式决定,它取决于编译时参数的类型。
答案 3 :(得分:0)
多态性是遗传的影响。它只能在相互扩展的类中发生。
多态性确实在运行时发生;我从来没有听说过“过载多态”。
继承发生在编译时,你写的那一刻:
class A extends B
{
}
答案 4 :(得分:0)
只有继承才构成IS-A关系。多态与它无关。
“重载”是多态的例子。您可以了解有关运行时和编译时多态性here
的更多信息答案 5 :(得分:0)
•继承定义了两个类之间的父子关系,多态性利用该关系在代码中添加动态行为。
•继承通过允许子类从父类继承行为来鼓励代码可重用性。另一方面,Polymorphism允许子进程重新定义父类中已定义的行为。如果没有多态性,孩子不可能在由父引用变量表示的情况下执行自己的行为,但是使用多态可以完成它。
•Java不允许多重继承类,但允许接口的多重继承,这实际上是实现多态性所必需的。例如,一个类可以同时是Runnable,Comparator和Serializable,因为这三个都是接口。这使得它们在代码中传递,例如您可以将此类的实例传递给接受Serializable的方法,或传递给接受比较器的Collections.sort()。
•Polymorphism和Inheritance都允许面向对象的程序发展。例如,通过使用继承,您可以在身份验证系统中定义新用户类型,并且通过使用多态,您可以利用已编写的身份验证代码。由于,Inheritance保证最小的基类行为,依赖于超类或超级接口的方法仍然可以接受基类的对象并且可以对其进行身份验证。