首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >问答首页 >为什么在多态性中同时使用基类和接口?

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

Stack Overflow用户
提问于 2008-12-18 13:12:14
回答 7查看 770关注 0票数 1

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

有问题的链接是:http://en.wikipedia.org/wiki/Polymorphism_in_object-oriented_programming

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

谢谢

EN

回答 7

Stack Overflow用户

回答已采纳

发布于 2008-12-18 14:10:26

“从基类继承允许你继承行为,而实现一个接口只允许你指定交互”这句话是绝对正确的。

但更重要的是,接口允许静态类型语言继续支持多态性。面向对象的纯化论者会坚持认为,一种语言应该提供继承、封装、模块化和多态性,以便成为一种功能齐全的面向对象语言。在动态类型或鸭子类型的语言(如Smalltalk )中,多态性是微不足道的;然而,在静态类型语言(如Java或C# )中,多态性远非微不足道(实际上,从表面上看,它似乎与强类型的概念不一致)。

让我来演示一下:

在动态类型(或鸭子类型)语言(如Smalltalk)中,所有变量都是对对象的引用(仅此而已)。所以,在Smalltalk中,我可以这样做:

代码语言:javascript
运行
AI代码解释
复制
|anAnimal|    
anAnimal := Pig new.
anAnimal makeNoise.

anAnimal := Cow new.
anAnimal makeNoise.

该代码:

  1. 声明了一个名为anAnimal的本地变量(请注意,我们没有指定变量的类型-所有变量都是对对象的引用,不多也不少。)
  2. 创建了一个名为“anAnimal”的类的新实例。
  3. 将该新实例分配给变量,并将消息makeNoise分配给猪。
  4. 使用奶牛重复整个过程,但将其分配给与猪完全相同的变量。<代码>H212<代码>G213

相同的Java代码看起来像这样(假设Duck和Cow是Animal的子类:

代码语言:javascript
运行
AI代码解释
复制
Animal anAnimal = new Pig();
duck.makeNoise();

anAnimal = new Cow();
cow.makeNoise();

这一切都很好,直到我们介绍类蔬菜。蔬菜和动物有一些相同的行为,但不是全部。例如,动物和蔬菜可能都能生长,但很明显蔬菜不会发出噪音,动物也不能收获。

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

代码语言:javascript
运行
AI代码解释
复制
|aFarmObject|
aFarmObject := Cow new.
aFarmObject grow.
aFarmObject makeNoise.

aFarmObject := Corn new.
aFarmObject grow.
aFarmObject harvest.

这在Smalltalk中工作得很好,因为它是鸭子类型的(如果它走起来像鸭子,叫起来像鸭子-它就是鸭子)。在这种情况下,当消息被发送到对象时,将在接收方的方法列表上执行查找,如果找到匹配的方法,则调用该方法。如果不是,就会抛出某种NoSuchMethodError异常--但这一切都是在运行时完成的。

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

随之而来的是接口...

如果我们创建Animal和declare类,每个类都实现Growable,我们就可以声明我们的Cow是Animal,我们的玉米是蔬菜。我们也可以声明动物和蔬菜都是可生长的。这让我们可以写这段代码来增长一切:

代码语言:javascript
运行
AI代码解释
复制
List<Growable> list = new ArrayList<Growable>();
list.add(new Cow());
list.add(new Corn());
list.add(new Pig());

for(Growable g : list) {
   g.grow();
}

它让我们这样做,制造动物的声音:

代码语言:javascript
运行
AI代码解释
复制
List<Animal> list = new ArrayList<Animal>();
list.add(new Cow());
list.add(new Pig());
for(Animal a : list) {
  a.makeNoise();
}

鸭型语言的最大优点是您获得了非常好的多态性:类提供行为所需做的全部工作就是提供方法(还有其他权衡,但这是讨论类型时最重要的权衡)。只要每个人都表现得很好,并且只发送与定义的方法匹配的消息,一切都是好的。缺点是下面的那种错误直到运行时才会被捕获:

代码语言:javascript
运行
AI代码解释
复制
|aFarmObject|
aFarmObject := Corn new.
aFarmObject makeNoise. // No compiler error - not checked until runtime.

静态类型的语言提供了更好的“契约式编程”,因为它们将在编译时捕获以下两种错误:

代码语言:javascript
运行
AI代码解释
复制
Animal farmObject = new Corn();  // Compiler error: Corn cannot be cast to Animal.
farmObject makeNoise();

--

代码语言:javascript
运行
AI代码解释
复制
Animal farmObject = new Cow();
farmObject.harvest(); // Compiler error: Animal doesn't have the harvest message.

So....to总结:

  1. 接口实现允许您指定对象可以做的事情种类(交互),而类继承允许您指定应该如何完成操作(implementation).
  2. Interfaces为我们提供了许多“真实”多态性的好处,而无需牺牲编译器类型检查。
票数 7
EN

Stack Overflow用户

发布于 2008-12-18 13:15:19

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

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

根据我的经验,当你想要重用行为的时候,你想要控制类如何交互的次数就相形见绌了。

票数 7
EN

Stack Overflow用户

发布于 2008-12-18 13:15:51

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

我看不出两者兼得有什么矛盾。

票数 3
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/379282

复制
相关文章
Java--多态性之抽象类和接口
上一篇:多态性之上溯造型和下溯造型 抽象类(abstract)可以含有一个或多个抽象方法(只有方法的定义而没有方法的实现),也可以不含有抽象方法。如果想从一个抽象类继承,而且新类要实例化对象,则必须实现抽象类的所有抽象方法。否则,新类也是抽象类。 接口(interface)可以理解为纯抽象类。它完全禁止方法的实现。接口中的数据成员(字段)默认为static和final, 成员函数(方法)默认为public。所以在实现一个接口时,来自接口的方法必须也是public。 实现按一个接口要使用 implements
SuperHeroes
2018/05/22
5530
接口和多态性
如果你又加了一个百度外卖,那么eat函数中又要new 一个BaiDu() ,给开发带来麻烦。我们希望的是,如果代码要扩展了,那么代码要尽最大可能的进行很小的改动就能实现。
ShenduCC
2018/08/01
3150
Python中的接口协议和抽象基类
Python语言是没有interface关键字的,这也是动态类型语言的特点之一。Python的接口指的是类实现或继承的公开属性,包括数据或方法。比如Sequence的正式接口如下图所示:
dongfanger
2021/09/28
2K0
golang中的类和接口的使用
类使用:实现一个people中有一个sayhi的方法调用功能,代码如下: type People struct { //.. } func (p *People) SayHi() { fmt.Println("************************* say hi !!") } func (this *LoginController) Get() { p := new(People) p.SayHi() this.TplName = "login.html" } 接口使用:实现上面
磊哥
2018/05/08
1.1K0
基类和派生类
  在面向对象设计中,被定义为包含所有实体共性的class类型,被称为“基类”。-百度百科
全栈程序员站长
2022/09/20
1.1K0
[Vue 3] 为什么需要同时使用Ref和Reactive
在使用 Options API 工作时声明响应性数据是直截了当的。data 选项内的所有内容都会自动变为响应性,并在模板中可用。唯一需要注意的是,要将data设为一个函数,以防止在所有组件实例之间共享状态。
前端小智@大迁世界
2023/08/16
4310
项目实战中如何使用抽象类和接口
虽然方法可在基类中声明为抽象成员,但是!!如果都从一个基类派生,会用掉唯一的基类机会,(什么意思呢:也就是C#的单继承特性了),所以,什么都往基类里面加,就会显得特别臃肿,且不通用。
WeiMLing
2019/08/23
9230
项目实战中如何使用抽象类和接口
C++中虚基类
如果一个派生类有多个直接基类,而这些直接基类又有一个共同的基类,则在最终的派生类中会保留该间接共同基类数据成员的多份同名成员。 C++提供虚基类(virtual base class)的方法,使得在继承间接共同基类时只保留一份成员。
卡尔曼和玻尔兹曼谁曼
2019/01/22
6960
C++中虚基类
在 Dart 中更好地使用类和 mixin
Dart 是一门“纯”面向对象的编程语言,其中所有的对象都是类的实例。但是 Dart 并不要求所有代码都定义在一个类中。我们可以在一个类的外面定义顶级变量、常量、函数 —— 就像面向过程语言那样。正式因为这样,Dart 的编码会有些特殊的建议。
玖柒的小窝
2021/11/28
2.6K0
C# 在基类定义好方法让子类继承接口就能实现
在 C# 里面,接口的定义只需要类里面存在和接口声明相同的方法或属性就可以,而存在的方法或属性是在子类定义的还是基类里面定义的都无所谓。也就是在基类里面写好了方法,但不继承接口,等子类继承接口的时候就不需要子类实现方法。通过这样的方法可以在基类里面添加一些辅助方法,而这些方法默认不给子类开启,除非子类继承了接口
林德熙
2022/08/04
6640
Java--多态性之内部类和匿名类
上一篇:多态性之抽象类和接口 为什么使用内部类: 每个内部类都可以独立地继承自一个(接口的)实现,所以无论外围类是否已经继承了一个(接口的)实现,对内部类都没有影响。 如果没有内部类提供的、可以继承多个具体的或抽象的类的能力,一些设计与编程问题就很难解决。从这个角度看,内部类使得多重继承的解决方案变得更加完整。接口解决了部分问题,而内部类有效地实现了”多重继承“。也就是说,内部类允许继承多个非接口类型(类或抽象类)。 定义在一个类内部的类被称为内部类。内部类拥有对封装类所有元素的访问权限,因为内部类的对象默
SuperHeroes
2018/05/22
6480
使用concurrently模块-同时启动react项目和mock模拟接口
上一节前面在react项目里面,添加了mock模拟接口,我们知道,启动react项目的命令是npm start,启动模拟接口的命令 是json-server mock/db.js,但是同在react项目的根目录底下,该怎么办?如何才能实现一个命令能够同事启动两个服务?
王小婷
2019/11/21
1.4K0
基类View
在 Class-based views 源码解析 #1 中我们从宏观层面讨论了 Django 类视图的类继承结构以及命名规律。接下来我们要深入各个具体的类视图,探索其具体的代码实现。本节将分析 base.py 中最重要的的一个类,它也是所有类视图的基类 View 。 之前我们说过,尽管类视图看上去类的种类繁多,但每个类都是各司其职的,且从类的命名就可以很容易地看出这个类的功能。大致可分为如下三个大的功能块,分别由三个类提供对应的方法: 处理 HTTP 请求。根据 HTTP 请求方法的不同做出相应处理。例如同
追梦人物
2018/04/17
9550
在Java 中安全使用接口引用
我使用Java 开发过很多项目,这其中包括一些Web 应用和Android 客户端应用。作为Android 开发人员,Java 就像我们的母语一样,但Android 世界是多元化的,并不是只有Java 才能用来写Android 程序,Kotlin 和Groovy 同样优秀,并且有着大量的粉丝。
程序亦非猿
2019/08/16
1.9K0
在Java 中安全使用接口引用
Photo by Joseph Maxim Reskp on Unsplash 我使用Java 开发过很多项目,这其中包括一些Web 应用和Android 客户端应用。作为Android 开发人员,J
小鄧子
2019/03/05
2K0
php中接口、抽象类以及接口和抽象类区别详解
在php中接口抽象类、Final、Static几个我们用到的相当的简单特别是大型网站架构时都会有用到了,今天我们来看一篇关于php中抽象类、Final、Static的例子。
PHP学习网
2022/08/03
4790
集合中接口和类的特点总结
Collection下有三个子接口,分别是List. Queue和Set, List和Queue中可以存储有序且重复的数据,Set中存储的数据是无序且不允许重复。
Breeze.
2022/04/23
4180
Java中抽象类和接口定义
* 当多个类中出现相同功能,但是功能主提体不同。 这时可以进行向上抽取。这时,只抽取功能定义,而不抽取功能主体。
用户7886150
2020/12/04
5760
C# 继承 基类和派生类基类的初始化C# 多重继承
继承是面向对象程序设计中最重要的概念之一。继承允许我们根据一个类来定义另一个类,这使得创建和维护应用程序变得更容易。同时也有利于重用代码和节省开发时间。
酱紫安
2020/07/24
4.8K0
Golang面向对象编程之继承&虚基类【组合&接口】
Golang里面没有像C++一样有继承相关的概念,但是我们却可以实现继承相关的用法,这就要用到struct、interface这两个结构。
Allen.Wu
2019/12/12
1.9K0

相似问题

同时继承基类和接口

22

使用接口和类的多态性

44

为什么在EJB类上同时使用@LocalBean和接口?

10

在基类和派生类中实现接口

30

OOP接口和基类

45
添加站长 进交流群

领取专属 10元无门槛券

AI混元助手 在线答疑

扫码加入开发者社群
关注 腾讯云开发者公众号

洞察 腾讯核心技术

剖析业界实践案例

扫码关注腾讯云开发者公众号
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档