欢迎光临散文网 会员登陆 & 注册

第 41 讲:面向对象编程(十三):接口的多态

2021-05-21 07:58 作者:SunnieShine  | 我要投稿

前面我们讲过了多态,不过是针对于类的。在我们讲了接口之后,接口实际上也可以多态。

Part 1 引例

假设我们邀请一群会吃饭的小伙伴参加比赛,然后角逐选出最强的一位参赛选手作为冠军。那么我们就需要书写一个方法,大概是这样的:

注意我们这里,需要传入一系列的会吃饭的小伙伴。如果我们在这里写的是 Animal[] 的话,因为有些小伙伴不一定“会吃饭”(有 Eat 方法),那么我们就无法通过 Animal[] 来完成这个任务。

最合适的办法,那肯定是用 IFoodie[] 来完成。返回值呢?第一名呗。那为什么不返回 Animal 类型而还是用 IFoodie 呢?并不是从 Animal 类派生的才可以实现 IFoodie 接口。猪笼草就不能参赛了么?猪笼草也会吃东西,对吧。可能你问我,猪笼草也不是动物了啊。你看我这个引例给的假设,我可没有说“只能是动物参赛”。

那么,总的来说,我们使用接口类型来表示“能做什么事情”的实例,来参与计算。

可是,我们怎么传参进去呢?还记得类的多态吗?类的多态是通过从某个类派生而产生的。那么,这里我们也可以这么做:

这就是所谓的接口的多态。我们使用实例化语句来产生一个实例,但用的是 IFoodie 接口来接收,因为它们都实现了 IFoodie 接口。

这个 pitcherPlant 是猪笼草。

Part 2 接口类型多态的用法

我们把这些类型都用接口类型来接收了,可这样做的好处是什么呢?

还记得 IFoodie 这个接口类型里有什么东西吗?是的,只有一个 Eat 方法。换句话说,如果我们将某个实例赋值给一个 IFoodie 类型(假设这种赋值是成功的的话),那么后续在使用这个变量的时候,你只能写出 .Eat() 的东西出来,别的都不行。因为这个接口只有这个成员。

假设我们这里的 IFoodie 接口里的 Eat 方法返回一个 int 类型结果,表示这个吃货在等同时间里吃了多少东西进去,那么我们取出最大值作为结果,然后把吃得最多的这个对象给返回出来。可以看到,我们只需要一个 Eat 方法,别的方法也用不上,所以直接这么书写就是合适的。

Part 3 接口的隐式和显式转换

和类的继承一致,接口虽然换了个说法,叫做“实现”,但是实际上本身还是继承的语法和机制。所以,它和类的强制和隐式转换也是一样的规则:

  • 如果从子类型转基接口的话,因为是一定可以转换成功的,因此是隐式转换的;

  • 如果从基接口转子类型的话,因为类型不一定成功转换,因此是使用强制转换的。

想必具体我就不用说明了吧。比如前面的 IFoodie 作为接收方,而等号右侧是那些类型的实体。因为这些类型都是实现了 IFoodie 类型的接口的,所以这样的赋值是隐式转换的。

3-1 isas,以及转换运算符

和类的多态一样,因为类可以通过别的类(父类)来表达出来,因此它可以达到多态的形式。那么,如果是“大转小”的话,需要用强制转换;而如果是“小转大”的话,直接是隐式转换。这里的“大”和“小”指的是这个类型可以适用的类型的范围。显然,抽象类用来给子类继承,那么抽象类的范围应该是更大的。

接口也是一样的。如果我们要把类型赋值给自己实现了的某个接口类型,那么自然是隐式转换的。因为接口类型可以表达的数据类型更多,毕竟非常多的类型都可以实现接口,但类本身只有一个。

那么反过来,如果要把接口转换回普通的类的话,就拿 IFoodie 来说,既然 DogCat 这些类型全都实现了 IFoodie 接口,那么反过来我们就无法确定,一个 IFoodie 接口真正表达的是什么数据,因此我们需要使用强制转换。如果转换失败,那么必然就会产生错误。此时我们依然需要使用 is 或者 as 运算符来判别其类型。

或者是

比如这样的形式。

3-2 无法对接口使用自定义类型转换器

如题,我们无法在一个类里书写类似这样的代码:

这样在 C# 里直接就是不允许的。你有想过为什么吗?原因其实很简单,因为类型本身是它实现的那些接口决定的。如果你自定义实现类型转换的话,就破坏了这种转换的体系架构。比如我 dog 对象是 Dog 这个类型的,它实现了 IRunnableIFoodie 或者别的什么接口,那么我们自然而然就知道,这些接口类型都是 dog 可以转换过去的接口类型;但是,我假设有一个 ICanSaySomething 的接口来表示“能说话”(注意接口名称前面的 I 一定是 interface 这个单词的缩写,而不是指的“我”),显然狗狗是不能说话的,因此无法对狗狗实现这个接口。

那么既然狗狗无法实现这个接口,就必然不可能允许狗狗往 ICanSaySomething 接口上转换。如果 C# 允许你自定义这样的转换的话,显然就破坏了 C# 面向对象的类型转换系统,因此会导致不稳定。

Part 4 没了?

是的,接口的多态基本上和类的多态是一样的写法,所以也没有什么特别需要讲的东西。

至此,面向对象的内容我们就介绍到这里。下一节我们讲开始新的篇章。


第 41 讲:面向对象编程(十三):接口的多态的评论 (共 条)

分享到微博请遵守国家法律