继承和多态都构成IS-
A关系吗?在运行时发生继承和“重写”多态性而在编译时发生“过载”多态性是真的吗?我之所以这样问,是因为许多论坛似乎给出了相互矛盾且常常令人困惑的答案。
谢谢!
对于问题的第一部分,我认为维基百科提供了一个很好的html" target="_blank">定义:
在面向对象的程序设计中,子类型多态或包含多态是类型理论中的一个概念,其中名称可以表示许多不同类的实例,只要它们与某个公共超类相关即可。包含多态性通常通过子类型化来支持,即,不同类型的对象可以完全替代另一种类型(其基本类型)的对象,因此可以通过公共接口进行处理。或者,可以通过类型强制(也称为类型转换)来实现包含多态性。
面向对象编程中的另一种称为多态性的 Wikipedia术语似乎很好地回答了您的问题。本文中的第二篇参考文章“
了解类型,数据抽象和多态性”也详细介绍了此问题。
Java中的这种子类型化功能是通过继承类和接口来实现的。尽管从继承的角度来看,Java的子类型功能可能并不总是很明显。以与泛型的协方差和逆方差为例。而且,数组在序列层次结构中的任何地方都不明显,但它们是可序列化和可克隆的。也可以说,通过原始扩展转换,Java中的数字类型也是多态的。并且运算符的行为取决于其操作数。
无论如何,继承在某些这种多态性的实现中起着重要作用。
重载与覆盖
问题的第二部分似乎是关于选择给定方法的实现。显然,如果一个类重写了一个方法,并且您创建了该类的实例,则即使要通过父类的引用访问该对象,也要调用该方法的重写版本。
正如您所指出的那样, 正确 选择方法的 实现
是在运行时完成的,现在,要调用的方法的签名是在编译时确定的。由于重载是关于具有相同名称和不同签名的不同方法的,所以这就是为什么重写方法选择发生在编译时的原因。
编译时的覆盖方法选择
第15.12节“
方法调用表达式
”中的Java语言规范(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
该类(带有主类的类),然后再次运行该程序,则输出将为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
该类。
问题内容: 假设我有两个班级:动物和狗。狗是动物的子类。我执行以下代码: 现在,我可以通过a变量来调用Dog类的方法。 但是我的问题是:如果我可以通过Dog对象(继承性)调用Animal的所有方法,那么为什么要使用多态原理呢?我可以声明: 通过此声明,可以使用所有Animal方法和Dog方法。那么为什么要使用多态呢?非常感谢您的回答。 问题答案: 在Java中,多态和继承的概念被“焊接在一起”。通
继承和多态 - 这是Python中一个非常重要的概念。 如果你想学习,你必须更好地理解它。 继承 (Inheritance) 面向对象编程的一个主要优点是重用。 继承是实现同样的机制之一。 继承允许程序员首先创建通用类或基类,然后将其扩展到更专业的类。 它允许程序员编写更好的代码。 使用继承,您可以使用或继承基类中可用的所有数据字段和方法。 稍后您可以添加自己的方法和数据字段,因此继承提供了一种组
类的继承性是面向对象语言的基本特性,多态性的前提是继承性。Java支持继承性和多态性。本章讨论Java继承性和多态性。 12.1 Java中的继承 为了了解继承性,先看这样一个场景:一位面向对象的程序员小赵,在编程过程中需要描述和处理个人信息,于是定义了类Person,如下所示: //Person.java文件 package com.a51work6; import java.util.Dat
问题内容: 假设Java具有以下层次结构类: 这是C#中相同代码的(盲)重复: 当我执行Java代码时,我得到了C#返回的信息。 对我来说,C#的结果更有意义,因为引用B调用了它自己的方法。 Java设计者决定打印而不是打印的逻辑是什么?我的意思是,为什么引用B在C中使用覆盖方法?这种方法的优势是什么? 如何更改Java代码以像C#一样打印出来?我的意思是,我怎么教Java调用它使用的完全引用的方
在OOP程序设计中,当我们定义一个class的时候,可以从某个现有的class继承,新的class称为子类(Subclass),而被继承的class称为基类、父类或超类(Base class、Super class)。 比如,我们已经编写了一个名为Animal的class,有一个run()方法可以直接打印: class Animal(object): def run(self):
在OOP程序设计中,当我们定义一个class的时候,可以从某个现有的class继承,新的class称为子类(Subclass),而被继承的class称为基类、父类或超类(Base class、Super class)。 比如,我们已经编写了一个名为Animal的class,有一个run()方法可以直接打印: class Animal(object): def run(self):