继承和多态之间的主要区别是啥?
Posted
技术标签:
【中文标题】继承和多态之间的主要区别是啥?【英文标题】:What is the main difference between Inheritance and Polymorphism?继承和多态之间的主要区别是什么? 【发布时间】:2011-09-12 14:01:56 【问题描述】:今天在模块开卷考试结束时向我提出了这个问题,我发现自己迷路了。我正在阅读Head first Java
,这两个定义似乎完全相同。我只是想知道我自己的主要区别是什么。我知道有很多与此类似的问题,但我没有看到任何一个可以提供明确的答案。
【问题讨论】:
不知何故与这个问题有关:Is polymorphism possible without inheritance 也相关:Isn't polymorphism just a side effect of inheritance? 【参考方案1】:继承是指“类”派生自现有“类”。所以如果你有一个Person
类,那么你就有一个Student
类扩展Person
,Student
继承 Person
拥有的所有东西。您在 Person 中的字段/方法上放置的访问修饰符有一些细节,但这是基本思想。例如,如果您在 Person
上有一个私有字段,Student
将看不到它,因为它的私有和私有字段对子类不可见。
多态性处理程序如何决定它应该使用哪些方法,这取决于它拥有什么类型的东西。如果你有一个Person
,它有一个read
方法,你有一个扩展Person
的Student
,它有自己的read
实现,调用哪个方法由运行时为你确定,取决于您是否拥有Person
或Student
。这有点棘手,但如果你做类似的事情
Person p = new Student();
p.read();
Student 上的 read 方法被调用。这就是多态性的作用。您可以执行该作业,因为 Student
是 Person
,但运行时足够聪明,可以知道 p
的实际类型是 Student。
请注意,不同语言的细节有所不同。例如,您可以在 javascript 中进行继承,但它与在 Java 中的工作方式完全不同。
【讨论】:
@hvgtcodes 所以简而言之,超类-子类关系是继承,父类和子类之间以不同方式实现相同方法的概念,并根据情况调用它们多态性,。我说的对吗? @hvgotcodes 但如果Person
的read
方法使用公共访问修饰符,Student
对象不能访问它们吗?然后Student s = new Student();
会不会更容易?实际上,我仍然没有真正获得多态的好处。
@hvgotcodes Student s = new Student() 可以工作。但是假设你用这个想法写了很多代码,后来你意识到你犯了一个错误。这个人其实不是学生,而是老师。所以你可以简单地将Person p = new Student() 改为Person p = new Teacher(),这样你的生活就会变得简单很多。
我的问题是你为什么要使用Person p = new Student();
而不是Student p = new Student();
?
@PerfectContrast 我认为当您希望将学生、司机、教师等作为人员并将它们分组到列表或其他内容下时,这很有用。因此,当您为所有人调用“读取”时,每个人都会调用自己的“读取”方法。【参考方案2】:
继承是指在子类中使用超类的结构和行为。
多态性是指在子类中改变超类的行为。
【讨论】:
这个答案是否暗示多态性需要继承? @jaco0646 - 在 Java 的上下文中,我认为是这样。 (在其他语言中,可能没有那么多。)请注意,“超类”和“子类”在这里被松散地使用。多态性也可能意味着继承接口中指定(但未实现)的行为。 @AlirezaRahmani - 我不明白你的评论。你的意思是继承不涉及继承属性和行为?这与 Java(以及大多数基于类、面向对象的语言)定义继承的方式相反。来自Java Language Specification, §8.4.8:“一个C类继承来自其直接超类的所有具体方法m
(static
和instance
)的超类...”(后跟继承细节)。对我来说,这听起来像是“代码重用”。
@TedHopp Inheritance 主要是一种多态工具,但有些人尝试将其用作重用/共享代码的一种方式,但后来很危险。基本原理是“如果我继承,那么我可以免费获得所有方法”,但忽略了这两个类可能没有多态关系的事实。
@AlirezaRahmani - 在 Java 中(根据标签,这是 OP 特别询问的内容),类继承最肯定涉及继承行为。这是语言定义的一部分。正如您所描述的那样,这可能会被滥用,这是Java的弱点之一。 (一个相关的弱点涉及声明类来实现接口只是为了导入接口中定义的常量。最终,Java 设计者引入了import static
以消除对接口的这种滥用。)对于 Java 中的纯多态性,使用的工具是接口,而不是类继承。【参考方案3】:
多态性:以相似方式处理不同类型对象的能力。示例:长颈鹿和鳄鱼都是动物,动物可以Move
。如果您有 Animal
的实例,那么您可以调用 Move
而无需知道或关心它是什么类型的动物。
继承:这是同时实现多态性和代码重用的一种方式。
其他形式的多态性:
还有其他实现多态性的方法,例如接口,它只提供多态性但不提供代码重用(有时代码完全不同,例如 Snake 的 Move
与 Dog 的 Move
完全不同,其中在这种情况下,接口将是更好的多态选择。
在其他动态语言中,多态性可以通过 Duck Typing 实现,即类甚至不需要共享相同的基类或接口,它们只需要一个同名的方法。或者甚至像 Javascript 那样更动态,你甚至根本不需要类,只需一个具有相同方法名称的对象就可以多态使用。
【讨论】:
【参考方案4】:主要区别是多态是继承的特定结果。多态性是在运行时根据对象的类型确定要调用的方法。当您有一个类从另一个类继承并覆盖特定方法时,就会出现这种情况。但是,在正常的继承树中,您不必重写任何方法,因此并非所有方法调用都必须是多态的。那有意义吗?这是一个类似的问题,所有福特汽车都是汽车,但并非所有汽车都是福特汽车(尽管不完全是……)。
此外,多态性处理方法调用,而继承也描述数据成员等。
【讨论】:
【参考方案5】:在 Java 中,两者密切相关。这是因为 Java 使用一种称为“动态分派”的方法调用技术。如果我有
public class A
public void draw() ...
public void spin() ...
public class B extends A
public void draw() ...
public void bad() ...
...
A testObject = new B();
testObject.draw(); // calls B's draw, polymorphic
testObject.spin(); // calls A's spin, inherited by B
testObject.bad(); // compiler error, you are manipulating this as an A
然后我们看到 B 从 A 继承了 spin
。但是,当我们尝试将对象当作 A 类型来操作时,我们仍然会得到 B 对 draw
的行为。 draw
行为是多态的。
在某些语言中,多态性和继承关系并不密切。例如,在 C++ 中,未声明为 virtual 的函数是继承的,但不会被动态调度,因此即使使用继承也不会获得多态行为。
在 javascript 中,每个函数调用都是动态调度的,并且您的类型很弱。这意味着您可以拥有一堆不相关的对象,每个对象都有自己的draw
,让一个函数遍历它们并调用该函数,每个对象都会表现得很好。您将拥有自己的多态绘图而不需要继承。
【讨论】:
【参考方案6】:多态性: 假设您在一家销售钢笔的公司工作。因此,您创建了一个名为“Pen”的非常好的类,它处理您需要了解的有关笔的所有信息。您编写各种用于计费、运输、创建发票的类,所有这些都使用 Pen 类。有一天老板过来说:“好消息!公司正在发展壮大,我们现在正在销售书籍和 CD!”不是好消息,因为现在您必须将使用 Pen 的每个班级更改为也使用 Book & CD。但是,如果您最初创建了一个名为“SellableProduct”的接口,而 Pen 实现了这个接口呢?然后,您可以编写所有运输、发票等类来使用该接口而不是 Pen。现在您所要做的就是创建一个名为 Book & CompactDisc 的新类,它实现了 SellableProduct 接口。由于多态性,所有其他类都可以继续工作而无需更改!有意义吗?
所以,这意味着使用继承,这是实现多态性的方法之一。
在一个类/接口中可以实现多态,但继承总是在 2 个或更多类/接口之间。继承总是符合“is-a”关系,但并不总是符合多态性(它可以同时符合“is-a”/“has-a”关系。
【讨论】:
【参考方案7】:继承更多是静态的东西(一个类扩展另一个类),而多态性是动态/运行时的东西(对象的行为根据其动态/运行时类型而不是其静态/声明类型)。
例如
// This assignment is possible because B extends A
A a = new B();
// polymorphic call/ access
a.foo();
-> 虽然 a 的静态/声明类型是 A,但实际的动态/运行时类型是 B,因此 a.foo() 将执行 B 中定义的 foo,而不是 A。
【讨论】:
【参考方案8】:多态是一种表达具有相似特征的对象类型之间的共同行为的方法。它还允许通过覆盖来创建这些特征的变体。继承是一种通过对象层次结构实现多态性的方法,其中对象表达关系和抽象行为。不过,这并不是实现多态性的唯一方法。原型是不同于继承的另一种表达多态性的方式。 JavaScript 是使用原型的语言的一个例子。我想还有其他方法。
【讨论】:
【参考方案9】:继承是一个与代码重用相关的概念。例如,如果我有一个父类说Animal
,它包含某些属性和方法(在这个例子中说makeNoise()
和sleep()
),我创建了两个子类Dog
和Cat
。由于狗和猫都以相同的方式睡觉(我会假设),因此无需在父类Animal
提供的Dog
和Cat
子类中的sleep()
方法中添加更多功能。然而,Dog
吠叫和Cat
喵喵叫,所以虽然Animal
类可能有一种制造噪音的方法,但狗和猫相对于彼此和其他动物会发出不同的噪音。因此,需要为它们的特定类型重新定义该行为。从而定义了多态性。希望这会有所帮助。
【讨论】:
【参考方案10】:Oracle 文档准确地引用了差异。
inheritance: 类从其所有超类继承字段和方法,无论是直接的还是间接的。 子类可以覆盖它继承的方法,也可以隐藏它继承的字段或方法。 (请注意,隐藏字段通常是不好的编程习惯。)
polymorphism:多态性是指生物学中的一个原理,其中一个有机体或物种可以有许多不同的形式或阶段。这个原则也可以应用于面向对象的编程和像 Java 语言这样的语言。 一个类的子类可以定义自己独特的行为,但共享父类的一些相同功能。
多态性不适用于字段。
相关帖子:
Polymorphism vs Overriding vs Overloading
【讨论】:
【参考方案11】:多态性是继承的效果。它只能发生在相互扩展的类中。它允许您在不知道类的确切类型的情况下调用类的方法。此外,多态性确实发生在运行时。
以Java多态为例:
继承允许派生类共享其基类的接口和代码。它发生在编译时。
例如,Java 平台中的所有类都是对象的后代(图片由 Oracle 提供):
了解更多关于Java inheritance和Java polymorphism的信息
【讨论】:
【参考方案12】:如果你使用 JAVA,就这么简单:
多态性使用继承的方法,但“覆盖”它们以执行不同的操作(或者如果您调用 super 则相同,因此从技术上讲不会是多态的)。
如果我错了,请纠正我。
【讨论】:
【参考方案13】:多态的主要目的:创建超类的引用变量并持有子类对象=>一个对象可以执行多种行为。
在继承中,子类继承超类的属性。
【讨论】:
【参考方案14】:继承是一种多态,实际上继承就是动态多态。因此,当您删除继承时,您将无法再覆盖。
【讨论】:
【参考方案15】:使用继承,实现在超类中定义——因此行为被继承。
class Animal
double location;
void move(double newLocation)
location = newLocation;
class Dog extends Animal;
使用多态性,实现是在子类中定义的——所以只有接口被继承。
interface Animal
void move(double newLocation);
class Dog implements Animal
double location;
void move(double newLocation)
location = newLocation;
【讨论】:
关于继承,如果您可以添加“......行为是继承的(假设我们没有覆盖子类中的任何内容)”,我将不胜感激。【参考方案16】:多态是通过Java
中的继承实现的。
├── Animal
└── (instances)
├── Cat
├── Hamster
├── Lion
└── Moose
├── interface-for-diet
│ ├── Carnivore
│ └── Herbivore
├── interface-for-habitat
│ ├── Pet
│ └── Wild
public class Animal
void breath()
;
public interface Carnivore
void loveMeat();
public interface Herbivore
void loveGreens();
public interface Pet
void liveInside();
public interface Wild
void liveOutside();
public class Hamster extends Animal implements Herbivore, Pet
@Override
public void liveInside()
System.out.println("I live in a cage and my neighbor is a Gerbil");
@Override
public void loveGreens()
System.out.println("I eat Carrots, Grapes, Tomatoes, and More");
public class Cat extends Animal implements Carnivore, Pet
@Override
public void liveInside()
System.out.println("I live in a cage and my neighbr is a Gerbil");
@Override
public void loveMeat()
System.out.println("I eat Tuna, Chicken, and More");
public class Moose extends Animal implements Herbivore, Wild
@Override
public void liveOutside()
System.out.println("I live in the forest");
@Override
public void loveGreens()
System.out.println("I eat grass");
public class Lion extends Animal implements Carnivore, Wild
@Override
public void liveOutside()
System.out.println("I live in the forest");
@Override
public void loveMeat()
System.out.println("I eat Moose");
Hamster
类从Animal
、Herbivore
和Pet
继承结构,以展示多态行为主义 家养宠物。
Cat
类从 Animal
、Carnivore
和 Pet
继承 结构,也表现出 多态行为主义 家养宠物。
【讨论】:
【参考方案17】:继承会导致多态性,因此不能像比较 Car 和它的 AC 那样将两者一起比较。
如果问题是简单地说定义继承和多态,那么从 Java 文档中挑选的定义是:
继承:面向对象的编程允许类从其他类继承常用的状态和行为。
多态性:一个类的子类可以定义自己独特的行为,但共享父类的一些相同功能。
【讨论】:
【参考方案18】:继承是指 A 类从其所有父级继承所有非静态受保护/公共方法/字段,直到 Object。
【讨论】:
以上是关于继承和多态之间的主要区别是啥?的主要内容,如果未能解决你的问题,请参考以下文章
StringTemplate 和 FreeMarker 之间的主要区别是啥?
Scala 和 Groovy 之间的主要区别是啥? [关闭]