纯抽象类和接口之间的区别

我和一位同事讨论过,他坚持认为在Java和C#等语言中,没有任何理由使用Pure Abstract基类,因为它只是意味着你无法避免缺少多重inheritance。

我觉得他错了,因为我一直以为如果一个东西是名词那么它就是一个对象,如果它是一个动词那么它就是一个界面。

例如,如果我想定义类型Bird ,我想在不实现它的情况下强制执行方法fly ,那么我会将它设为纯抽象类。

如果我想定义一个类型的Flies ,我会把它作为方法fly的接口。

Bird可能会实施Flies

我错了吗?

编辑:

我能给予支持我观点的唯一可靠论点是,在未来的某个时刻,设计可能需要改变,以便鸟类可以吃。 如果所有的鸟都吃同样的话,那么就需要将它添加到Bird ,使其成为非纯抽象的。

如果Bird是一个接口,这个改变只会是一场噩梦,因为我不知道从其他基类inheritance的东西是否也实现了我的Bird接口,所以我不能只重构我的问题。

除了你的第一次编辑,即未来的一些要求。 一个可能的用例可以声明一个常量并在抽象类中初始化它。

 import java.util.ArrayList; import java.util.List; public abstract class AbstractPure implements ISomeInterface { public static final List days = new ArrayList(); static{ days.add("Monday"); days.add("Tuesday"); days.add("Wednesday"); days.add("Thursday"); days.add("Friday"); days.add("Saturday"); days.add("Sunday"); } } 

我至少可以想到一个很好的理由:您可以在以后扩展抽象类,而不会破坏向后兼容性:假设一个类/接口

 abstract class/interface Foo { void foo(); } 

如果我们使用接口,我们现在确定无法向Foo添加其他function。 这可能会导致interface Foo2 implements Foo类的东西interface Foo2 implements Foo

另一方面,如果您有一个抽象类,只要提供基本实现,就可以轻松地为其添加另一个方法。

请注意,Java8将允许接口基本上做同样的事情 – 这对于想要更新其库以使用lambdas而不必破坏与已编写的数百万行代码的兼容性的库编写者将是有用的。

我做了一些关于纯抽象类的搜索(自从我上次使用C ++以来已经有一段时间了),我能找到的唯一用例是用C ++定义接口(没有接口)。

所以我想说如果你可以使用一个纯粹的抽象类,你可以选择一个接口,就像你的朋友说的那样。

但是,我从来没有遇到过在C#中使用纯抽象类的需要,所以这可能是一个假设的问题。

我会说接口和类都定义了对象。 正如你自己所说的,动词转到方法 – 它们是这些对象的属性。 在你的fly的例子中,我将声明一个接口FlyingCreature并在其中定义一个方法fly(因此你有像ComparableSerializable这样的接口而不是SerializeCompare 。你有方法compareTo )。 另外关于你的另一个例子 – 鸟:如果你没有任何鸟的逻辑,那么你将它声明为接口。

接口用于声明不同类型的对象将具有的公共属性,它们按分类方式对它们进行分组。 后来,使用您知道它们具有相同界面的事实,您将能够以相同的方式处理它们。

我没有看到纯抽象类有任何好处,因为我已经说过抽象类和接口都声明了对象。

假设你有一个带有多个插件类型的插件系统,每个插件都实现了不同的接口。

现在,我们还说你正在使用reflection来在运行时找到这些插件类。

由于它们是接口,因此一个插件可以是多种类型的插件。

如果您将每个插件类型设置为抽象类,则可以控制inheritance层次结构并确保每个插件都是不同的类型。

因此纯抽象类和接口之间存在差异。 它不是你需要使用的,但也不是++,递归,甚至是对象。

我不久前读到了与此相关的内容。 它基本上说如果项目链接在一起,那么使用抽象类。 如果它描述了某些东西的属性,那么使用接口。

因此,使用你的例子,一只鸟将是一个抽象类,因为你想要一只鹰或一只鹦鹉来自同一个地方。

如果你只是想要像“传单”这样的东西,那么你就会把它变成一个界面,因为鸟类和飞机以及其他东西可能会实现界面,但它只有这一个属性,它们是共同的。

另一件需要考虑的事情是,在某些时候你可能想要为你的鸟实际设置一些具体的方法。 毕竟所有的鸟都以相同的方式飞行所以你可能希望class级在鸟儿飞行时打印“襟翼襟翼”。 如果您正在使用接口,则需要在每个类上执行此操作(或者引入新的基类),而如果您首先使用了抽象类,则只需在一个位置添加行为。