Javainheritance与多态
inheritance和多态是否构成IS-A关系? 并且在运行时发生inheritance和“重写”多态是真的,而在编译时发生“重载”多态吗? 我问这个的原因是因为许多论坛似乎给出了相互矛盾且经常令人困惑的答案。
谢谢!
对于问题的第一部分,我认为维基百科提供了一个很好的定义:
在面向对象的编程中,子类型多态性或包含多态性是类型理论中的一个概念,其中名称可以表示许多不同类的实例,只要它们与某些共同的超类相关即可。 通常通过子类型支持包含多态性,即,不同类型的对象完全可替代另一种类型的对象(它们的基本类型),因此可以通过公共接口来处理。 或者,包含多态性可以通过类型强制来实现,也称为类型转换。
在面向对象编程中,另一个名为Polymorphism的 Wikipedia artile似乎很好地回答了你的问题。 本文中的第二篇参考文章称为“理解类型”,“数据抽象”和“多态”,这些内容也非常详细地介绍了这一问题。
除了其他方法之外,Java中的这种子类型特征通过inheritance类和接口来实现。 尽管Java的子类型特征在inheritance方面可能并不明显。 以协方差和与generics的逆变为例。 此外,数组是Serializable和Cloneable,尽管在类型层次结构中的任何位置都不明显。 也可以说通过原始扩展转换,Java中的数字类型也是多态的。 并且操作符根据其操作数进行polimorphically行为。
无论如何,inheritance在实现某些多态性中起着重要作用。
重载与覆盖
问题的第二部分似乎是关于选择给定方法的实现。 显然,如果类重写了一个方法并且您创建了该类的实例,则希望调用该方法的重写版本,即使您通过父类的引用访问该对象也是如此。
正如您所指出的那样,选择正确的方法实现是在运行时完成的,现在要调用的方法的签名是在编译时决定的。 由于重载是关于具有相同名称和不同签名的不同方法,因此可以说在编译时发生重写方法选择。
在编译时重写方法选择
第15.12节“ 方法调用表达式 ”中的Java语言规范 (JLS)详细说明了编译器在选择正确的调用方法时遵循的过程。
在那里,您会注意到这是一个编译时任务。 JLS在第15.12.2小节中说:
此步骤使用方法的名称和参数表达式的类型来定位可访问和适用的方法可能有多个此类方法,在这种情况下,选择最具体的方法。
要validation此编译时的性质,您可以执行以下测试。
声明一个这样的类并编译它。
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
被覆盖,因此被调用的实现是ChooseMethodB
的实现。
现在,在MethodChooserB
添加一个overriden方法:
public void doSomething(Number n){ System.out.println("Number B"); }
并重新编译这个,然后再次运行main方法。
现在,您获得输出Number B
因此,在运行时选择了实现,而不需要重新编译MethodChooser
类。
多态性:不同对象接收相同消息并以不同方式响应的能力。
inheritance是实现它的一种方式,但并非必要。 见鸭子打字
方法的重载是’编译时语法助手’ – 因为每个方法在编译后都获得唯一的签名。 与多态无关。
我想你是正确的。
多态性考虑对象的运行时类型来决定方法的执行,并且选择哪个重载的方法在运行时不会以动态方式决定,它取决于编译时参数的类型。
多态性是遗传的一种效应。 它只能在相互扩展的类中发生。
多态性确实在运行时发生; 我从来没有听说过“过载多态”。
inheritance发生在编译时,你写的那一刻:
class A extends B { }
只有inheritance构成IS-A关系。 多态与它无关。
“重载”是多态的例子。 您可以在此处了解有关运行时和编译时多态的更多信息
•inheritance定义了两个类之间的父子关系,多态性利用该关系在代码中添加动态行为。
•inheritance通过允许子类从父类inheritance行为来鼓励代码可重用性。 另一方面,Polymorphism允许子进程重新定义父类中已定义的行为。 如果没有多态性,孩子不可能在用父引用变量表示的情况下执行自己的行为,但是使用多态性就可以完成。
•Java不允许多重inheritance类,但允许接口的多重inheritance,这实际上是实现多态性所必需的。 例如,一个类可以同时是Runnable,Comparator和Serializable,因为这三个都是接口。 这使得它们在代码中传递,例如,您可以将此类的实例传递给接受Serializable的方法,或传递给接受Comparator的Collections.sort()。
•多态性和inheritance都允许面向对象的程序发展。 例如,通过使用inheritance,您可以在身份validation系统中定义新用户类型,并且通过使用多态,您可以利用已编写的身份validation代码。 由于,Inheritance保证最小的基类行为,依赖于超类或超级接口的方法仍然可以接受基类的对象并可以对其进行身份validation。