纯抽象类和接口之间的区别
本文关键字:区别 之间 接口 抽象类 | 更新日期: 2023-09-27 18:26:20
我正在和一位同事讨论,他坚持认为在Java和C#等语言中,从来没有任何理由使用纯抽象基类,因为它只是意味着你无法绕过缺乏多重继承的问题。
我觉得他错了,因为我一直认为,如果一个事物是名词,那么它就是一个宾语,如果它是一个动词,那么它就是一个接口。
例如,如果我想定义类型 Bird
,我想在不实现它的情况下强制执行方法 fly
,那么我会把它变成一个纯粹的抽象类。
如果我想定义一个类型Flies
,我会让它成为方法fly
的接口。
Bird
可能会实现Flies
.
我错了吗?
编辑:
我能给出的唯一可靠的论据来支持我的观点是,在未来的某个时候,设计可能需要改变,以便鸟类可以吃东西。如果所有的鸟都吃一样的东西,那么这需要添加到Bird
,使其非纯抽象。
如果Bird
是一个接口,那么这种更改将只是一场噩梦,因为我无法知道从其他基类继承的东西是否也实现了我的Bird
接口,所以我不能只是重构我的问题。
我至少可以想到一个很好的理由:你可以在以后扩展抽象类,而不会破坏向后兼容性: 假设一个类/接口
abstract class/interface Foo {
void foo();
}
如果我们使用一个界面,我们现在肯定知道没有办法向Foo添加额外的功能。这可能会导致类似 interface Foo2 implements Foo
.
另一方面,如果你有一个抽象类,你可以很容易地向它添加另一个方法,只要你提供一个基本的实现。
请注意,Java8 将允许接口做基本相同的事情 - 这对于希望更新其库以使用 lambda 的库编写者非常有用,而不必破坏与已编写的数百万行代码的兼容性。
除了您的第一次编辑,即一些未来的要求。一个可能的用例可以声明一个常量并在抽象类中初始化它。
import java.util.ArrayList;
import java.util.List;
public abstract class AbstractPure implements ISomeInterface {
public static final List<String> days = new ArrayList<String>();
static{
days.add("Monday");
days.add("Tuesday");
days.add("Wednesday");
days.add("Thursday");
days.add("Friday");
days.add("Saturday");
days.add("Sunday");
}
}
我对纯抽象类进行了一些搜索(自从我上次使用 C++ 以来已经有一段时间了(,我能找到的唯一用例是在 C++ 中定义接口(没有接口(。
所以我想说,如果你可以使用一个纯粹的抽象类,你不妨使用一个接口,就像你的朋友说的那样。
但是,我从未遇到过在 C# 中需要纯抽象类的情况,因此这可能是一个假设问题。
我会说接口和类都定义了对象。正如你自己所说,动词转到方法 - 它们是这些对象的属性。在您的 fly 示例中,我将声明一个接口FlyingCreature
并在其中定义一个方法 fly(因此您有像 Comparable
和 Serializable
这样的接口,而不是 Serialize
和 Compare
.你有方法 compareTo
(。还有关于你的另一个例子 - bird:如果你对鸟没有任何逻辑,那么你把它声明为接口。
这些接口用于声明不同类型的对象将具有的公共属性,它们按分类对它们进行分组。后记,利用您知道它们具有相同界面的事实,您将能够以相同的方式处理它们。
我认为纯抽象类没有任何好处,因为我已经说过抽象类和接口都声明对象。
假设你有一个插件系统,有多个插件类型,每个插件类型都实现一个不同的接口。
现在,假设您正在使用反射在运行时查找这些插件类。
由于它们是接口,一个插件可以是多种类型的插件。
如果使每个插件类型都成为抽象类,则可以控制继承层次结构,并且可以确保每个插件都是不同的类型。
所以纯抽象类和接口之间是有区别的。 这不是你需要使用的,但++、递归甚至对象也不是。
不久前我读过一些与此相关的内容。它基本上是说,如果这些项目链接在一起,那么使用一个抽象类。如果它描述了某物的属性,则使用接口。
因此,使用您的示例,鸟将是一个抽象类,因为您希望鹰或鹦鹉来自同一个地方。
如果你只是想要像"flyer"这样的东西,那么你会把它作为一个接口,因为鸟类、飞机和其他东西可能会实现这个接口,但它只有一个共同的属性。
另一件需要考虑的事情是,在某些时候,你可能想为你的鸟放一些具体的方法。毕竟,所有的鸟都以相同的方式飞行,所以你可能希望班级在鸟飞的时候打印"襟翼襟翼"。如果你使用一个接口,你需要在每个类上这样做(或者引入一个新的基类(,而如果你首先使用了一个抽象类,你只需要在一个地方添加行为。