Language agnostic 为什么在多态性中同时使用基类和接口?

Language agnostic 为什么在多态性中同时使用基类和接口?,language-agnostic,Language Agnostic,在多态性的C示例中,有一个Cat类继承了一个名为AnimalBase的类和一个名为IAnimal的接口 有关链接是: 我的问题是,为什么同时使用基类和接口?为什么不是一个呢?我认为实现多态性只需要一个抽象类 谢谢当您想要重用行为时,会使用基类 当您想要控制类如何与其他对象交互时,将使用接口。它以精确的方式定义交互 根据我的经验,您想要控制类如何交互的次数比您想要重用行为的次数要少。当您想要重用行为时,可以使用基类 当您想要控制类如何与其他对象交互时,将使用接口。它以精确的方式定义交互 根据我的经

在多态性的C示例中,有一个Cat类继承了一个名为AnimalBase的类和一个名为IAnimal的接口

有关链接是:

我的问题是,为什么同时使用基类和接口?为什么不是一个呢?我认为实现多态性只需要一个抽象类


谢谢

当您想要重用行为时,会使用基类

当您想要控制类如何与其他对象交互时,将使用接口。它以精确的方式定义交互


根据我的经验,您想要控制类如何交互的次数比您想要重用行为的次数要少。

当您想要重用行为时,可以使用基类

当您想要控制类如何与其他对象交互时,将使用接口。它以精确的方式定义交互


根据我的经验,你想要控制类如何交互的次数比你想要重用行为的次数要少。

拥有一个抽象类可以让你以一种通用的方式实现一些/大部分成员。拥有一个接口意味着,当您想将抽象基类用于多态性时,您不局限于仅使用该基类


我不认为两者都有任何矛盾。

拥有一个抽象类可以让您以通用的方式实现部分/大部分成员。拥有一个接口意味着,当您想将抽象基类用于多态性时,您不局限于仅使用该基类


我不认为两者都有任何矛盾。

接口为您提供了跨类heirachy的多态行为的能力。缺点是不能直接继承默认实现。使用类多态性,您只能在类继承中获得多态行为,但您可以继承公共/默认行为。通过插入和提供接口,您可以提供一个接口的契约,但可以获得继承的简单实现好处,同时还允许其他人在基类限制之外支持契约。

接口为您提供跨类继承的多态行为的能力。缺点是不能直接继承默认实现。使用类多态性,您只能在类继承中获得多态行为,但您可以继承公共/默认行为。通过插入和提供接口,您可以提供一个接口契约,但可以获得继承的简单实现好处,同时还允许其他人在基类限制之外支持该契约。

基类和接口实际上有很多不相关的用途。基类的主要目的是让继承类能够导入一些公共功能。接口的主要目的是让其他类能够问这个问题,这个对象是否支持interface X?

基类和接口实际上有很多不相关的用途。基类的主要目的是让继承类能够导入一些公共功能。接口的主要用途是让其他类能够提出这样的问题,这个对象是否支持接口X?

因此我想我可以继承一个动物类,用于常见行为,如行走、睡眠等,然后为狮子提供一个专门的接口,它将包含特定的行为和属性-比如动物是危险的,吃人的,等等。

所以我想我可以继承一个普通行为的动物类,比如行走、睡眠等,然后为狮子提供一个专门的接口,可能,它将包含特定的行为和属性-比如动物是危险的,吃人等等。

接口强制执行行为。声明为实现指定接口的任何类都必须实现具有接口中声明的签名的成员。。i、 他们必须公开指定的行为。。。他们不必以相同的方式实现行为,但他们不能实现相同的行为。。。i、 鸟类和蠕虫都可以移动,因此它们都必须实现能够移动的行为,并指定它们都必须执行此操作。。。 他们如何做到这一点是实施的功能

基类用于实现的重用

这就是为什么接口的命名约定建议在IEnumerable、ICanMove、ICanLog等中使用I[动词/副词]


您可以使用基类将公共实现放在一个地方。如果抽象基类在任何成员中都没有实现,那么它的功能与接口相同,接口不能让实现强制执行行为。声明为实现指定接口的任何类都必须实现具有decla签名的成员 界面上显示红色。。i、 他们必须公开指定的行为。。。他们不必以相同的方式实现行为,但他们不能实现相同的行为。。。i、 鸟类和蠕虫都可以移动,因此它们都必须实现能够移动的行为,并指定它们都必须执行此操作。。。 他们如何做到这一点是实施的功能

基类用于实现的重用

这就是为什么接口的命名约定建议在IEnumerable、ICanMove、ICanLog等中使用I[动词/副词]


您可以使用基类将公共实现放在一个地方。如果抽象基类在任何成员中都没有实现,那么它的功能与接口相同,接口不能有实现。从基类继承允许您继承行为,而实现接口只允许您指定交互是绝对正确的

但更重要的是,接口允许静态类型语言继续支持多态性。一个面向对象的纯粹主义者会坚持认为一种语言应该提供继承、封装、模块化和多态性,以便成为一种功能齐全的面向对象语言。在Smalltalk这样的动态类型或duck类型的语言中,多态性是微不足道的;然而,在Java或C这样的静态类型语言中,多态性并不是微不足道的事实,表面上它似乎与强类型的概念不一致

让我示范一下:

在像Smalltalk这样的动态类型或duck类型的语言中,所有变量都是对对象的引用。所以,在Smalltalk中,我可以做到:

|anAnimal|    
anAnimal := Pig new.
anAnimal makeNoise.

anAnimal := Cow new.
anAnimal makeNoise.
该守则:

声明一个名为anAnimal的局部变量注意,我们没有指定变量的类型-所有变量都是对对象的引用,不多也不少。 创建名为Pig的类的新实例 将Duck的新实例分配给变量anAnimal。 向清管器发送消息makeNoise。 使用cow重复整个过程,但将其指定给与Pig完全相同的变量。 同样的Java代码看起来像这样,假设Duck和Cow是Animal的子类:

Animal anAnimal = new Pig();
duck.makeNoise();

anAnimal = new Cow();
cow.makeNoise();
在我们介绍蔬菜课之前,一切都很好。蔬菜和动物有一些相同的行为,但不是全部。例如,动物和蔬菜都可以生长,但很明显,蔬菜不会发出噪音,动物也无法收获

在Smalltalk中,我们可以这样写:

|aFarmObject|
aFarmObject := Cow new.
aFarmObject grow.
aFarmObject makeNoise.

aFarmObject := Corn new.
aFarmObject grow.
aFarmObject harvest.
这在Smalltalk中非常有效,因为如果它像鸭子一样走路,它就是鸭子类型的,并且像鸭子一样嘎嘎叫——它是鸭子。在这种情况下,当向对象发送消息时,将在接收方的方法列表上执行查找,如果找到匹配的方法,则调用该方法。如果不是,则会抛出某种类型的NoSuchMethodError异常,但这一切都是在运行时完成的

但是在Java这种静态类型语言中,我们可以为变量指定什么类型?玉米需要从蔬菜中遗传来支持生长,但不能从动物中遗传,因为它不会发出噪音。牛需要从动物身上继承来支持制造噪音,但不能从蔬菜身上继承,因为它不应该实现收割。看起来我们需要多重继承——从多个类继承的能力。但事实证明,这是一个相当困难的语言特性,因为当多个并行超类实现相同的方法时,会弹出所有的边缘情况,等等

随之而来的是界面

如果我们把动物和蔬菜分类,每一类都实现可生长,我们就可以宣布我们的牛是动物,我们的玉米是蔬菜。我们还可以宣布动物和蔬菜都是可种植的。这让我们写下这篇文章来发展一切:

List<Growable> list = new ArrayList<Growable>();
list.add(new Cow());
list.add(new Corn());
list.add(new Pig());

for(Growable g : list) {
   g.grow();
}
静态类型语言提供了更好的契约式编程,因为它们在编译时会捕获以下两种错误:

Animal farmObject = new Corn();  // Compiler error: Corn cannot be cast to Animal.
farmObject makeNoise();
-

因此……总结一下:

接口实现允许您指定对象可以在交互中做什么,类继承允许您指定在接口实现中应该如何做

接口在不牺牲编译器类型检查的情况下,为我们提供了真正多态性的许多好处


从基类继承允许您继承行为,而实现接口只允许您指定交互的语句是绝对正确的

但更重要的是,接口允许静态类型语言继续支持多态性。一个面向对象的纯粹主义者会坚持认为一种语言应该提供继承、封装、模块化和多态性,以便成为一种功能齐全的面向对象语言。在Smalltalk这样的动态类型或duck类型的语言中,多态性是微不足道的;然而,在Java或C等静态类型语言中,p olymorphism远不是微不足道的事实,表面上它似乎与强类型的概念不一致

让我示范一下:

在像Smalltalk这样的动态类型或duck类型的语言中,所有变量都是对对象的引用。所以,在Smalltalk中,我可以做到:

|anAnimal|    
anAnimal := Pig new.
anAnimal makeNoise.

anAnimal := Cow new.
anAnimal makeNoise.
该守则:

声明一个名为anAnimal的局部变量注意,我们没有指定变量的类型-所有变量都是对对象的引用,不多也不少。 创建名为Pig的类的新实例 将Duck的新实例分配给变量anAnimal。 向清管器发送消息makeNoise。 使用cow重复整个过程,但将其指定给与Pig完全相同的变量。 同样的Java代码看起来像这样,假设Duck和Cow是Animal的子类:

Animal anAnimal = new Pig();
duck.makeNoise();

anAnimal = new Cow();
cow.makeNoise();
在我们介绍蔬菜课之前,一切都很好。蔬菜和动物有一些相同的行为,但不是全部。例如,动物和蔬菜都可以生长,但很明显,蔬菜不会发出噪音,动物也无法收获

在Smalltalk中,我们可以这样写:

|aFarmObject|
aFarmObject := Cow new.
aFarmObject grow.
aFarmObject makeNoise.

aFarmObject := Corn new.
aFarmObject grow.
aFarmObject harvest.
这在Smalltalk中非常有效,因为如果它像鸭子一样走路,它就是鸭子类型的,并且像鸭子一样嘎嘎叫——它是鸭子。在这种情况下,当向对象发送消息时,将在接收方的方法列表上执行查找,如果找到匹配的方法,则调用该方法。如果不是,则会抛出某种类型的NoSuchMethodError异常,但这一切都是在运行时完成的

但是在Java这种静态类型语言中,我们可以为变量指定什么类型?玉米需要从蔬菜中遗传来支持生长,但不能从动物中遗传,因为它不会发出噪音。牛需要从动物身上继承来支持制造噪音,但不能从蔬菜身上继承,因为它不应该实现收割。看起来我们需要多重继承——从多个类继承的能力。但事实证明,这是一个相当困难的语言特性,因为当多个并行超类实现相同的方法时,会弹出所有的边缘情况,等等

随之而来的是界面

如果我们把动物和蔬菜分类,每一类都实现可生长,我们就可以宣布我们的牛是动物,我们的玉米是蔬菜。我们还可以宣布动物和蔬菜都是可种植的。这让我们写下这篇文章来发展一切:

List<Growable> list = new ArrayList<Growable>();
list.add(new Cow());
list.add(new Corn());
list.add(new Pig());

for(Growable g : list) {
   g.grow();
}
静态类型语言提供了更好的契约式编程,因为它们在编译时会捕获以下两种错误:

Animal farmObject = new Corn();  // Compiler error: Corn cannot be cast to Animal.
farmObject makeNoise();
-

因此……总结一下:

接口实现允许您指定对象可以在交互中做什么,类继承允许您指定在接口实现中应该如何做

接口在不牺牲编译器类型检查的情况下,为我们提供了真正多态性的许多好处


虽然我理解你的意思,但我决不会这样说。接口强制执行行为。基类用于实现的重用。。。这就是为什么接口的命名约定建议使用I[动词/副词],如IEnumerable、ICanMove、ICanLog等。在这方面,交互、行为是等价的术语。这些类所做的事情是由接口定义的。虽然我理解你的观点,但我永远不会这样说。接口强制执行行为。基类用于实现的重用。。。这就是为什么接口的命名约定建议使用I[动词/副词],如IEnumerable、ICanMove、ICanLog等。在这方面,交互、行为是等价的术语。虽然我不同意你的评论,但我没有投你反对票。一个只有公共方法的抽象类和一个实现了相同方法的接口有什么区别?主要是在一个继承模型中,抽象类阻止继承者继承其他任何东西,而接口却没有。我真的想问问我的下一个投票者,他们认为接口是用来做什么的。。。你认为他们的目的是作为某种实现指南吗?我有一种隐秘的怀疑,许多人投反对票只是为了让他们的答案保持在那里。。。或者仅仅因为他们不喜欢你的名字……虽然我不同意你的评论,但我没有投你反对票。一个只有公共方法的抽象类和一个实现了相同方法的接口有什么区别?主要是在一个继承模型中,抽象类阻止继承者继承其他任何东西,而接口却没有。我真的想问问我的下一个投票者,他们认为接口是用来做什么的。。。你认为他们的目的是作为某种实现指南吗?我有一种隐秘的怀疑,许多人投反对票只是为了让他们的答案保持在那里。。。或者只是因为他们不喜欢你的名字。。。