抽象类与接口与mixins类

有人可以向我解释抽象类接口mixins之间的区别吗? 我以前在我的代码中使用过,但我不知道技术上的差异。

这些答案大部分都没有描述所有三个,所以我会加我自己的。

抽象类

抽象类是不是被devise为实例化的类。 抽象类可以没有实现,一些实现或全部实现。 抽象类旨在允许其子类共享一个通用(默认)实现。 抽象类的(伪代码)例子就是这样的

abstract class Shape { def abstract area(); // abstract (unimplemented method) def outline_width() = { return 1; } // default implementation } 

一个子类可能看起来像

 class Rectangle extends Shape { int height = width = 5; def override area() = { return height * width; } // implements abstract method // no need to override outline_width(), but may do so if needed } 

可能的用法

 def main() = { Shape[] shapes = { new Rectangle(), new Oval() }; foreach (s in shapes) { print("area: " + s.area() + ", outline width: " + s.outline_width()); } } 

如果一个子类不覆盖未实现的方法,它也是一个抽象类。

接口

在一般的计算机科学术语中,接口是暴露给客户端的程序的一部分。 公共类和成员是接口的例子。

Java和C#有一个特殊的interface关键字。 这些或多或less是一个没有实现的抽象类。 (有关常量,嵌套类,显式实现和访问修饰符的技巧,我不打算进入。)虽然关于“没有实现”的部分不再适合在Java中,他们添加了默认方法。 interface关键字可以看作是接口概念的具体化。

回到形状的例子

 interface Shape { def area(); // implicitly abstract so no need for abstract keyword def outline_width(); // cannot implement any methods } class Rectangle implements Shape { int height = width = 5; def override area() = { return height * width; } def override outline_width() = { return 1; } // every method in interface must be implemented } def main() = { Shape[] shapes = { new Rectangle(), new Oval() }; foreach (s in shapes) { print("area: " + s.area() + ", outline width: " + s.outline_width()); } } 

Java和C#不允许实现多个类的inheritance,但它们允许多个接口的实现。 Java和C#使用接口作为在允许多inheritance的语言中发现的死亡致命钻石问题的解决方法(如果处理得当,这不是那么致命)。

混入

mixin(有时称为trait)允许抽象类的多重inheritance。 Mixins没有多重inheritance的可怕关联(由于C ++的疯狂),所以人们使用它们更加舒适。 他们有着与死亡问题完全一致的致命钻石,但支持他们的语言比C ++有更多优雅的缓解方式,所以他们被看作是上帝从高处传下来的“纯粹的真棒”(Unadulterated Awesomeness TM)

Mixin被誉为与行为重用 , 更灵活的接口和更强大的接口的接口。 你会注意到所有这些都有他们的术语interface ,指的是Java和C#关键字。 Mixin不是接口。 他们是多重inheritance。 用更漂亮的名字

(免责声明:这并不是说mixin是不好的,多重inheritance也不错,C ++解决多重inheritance的方式就是每个人都在做的事情,人们把它重新命名为与恐怖分离,这可能是一件好事那就是C ++。
免责声明:我爱C ++。 请不要以此作为任何语言的判断。 他们都像我珍贵的小雪花,我只想把每一个都拿在手里唱Kumbaya。)

在疲惫的,老的形状的例子

 mixin Shape { def abstract area(); def outline_width() = { return 1; } } class Rectangle with Shape { int height = width = 5; def override area() = { return height * width; } } def main() = { Shape[] shapes = { new Rectangle(), new Oval() }; foreach (s in shapes) { print("area: " + s.area() + ", outline width: " + s.outline_width()); } } 

你会注意到这和抽象类的例子没有区别。

另外一个好消息是C#从3.0版本开始支持mixins。 你可以在接口上使用扩展方法来完成。 这里是真实(!)C#代码mixin风格的Shape示例

 interface Shape { int Area(); } static class ShapeExtensions { public static int OutlineWidth(this Shape s) { return 1; } } class Rectangle : Shape { int height = 5; int width = 5; public int Area() { return height * width; } } class Program { static void Main() { Shape[] shapes = new Shape[]{ new Rectangle(), new Oval() }; foreach (var s in shapes) { Console.Write("area: " + s.Area() + ", outline width: " + s.OutlineWidth()); } } } 

一般来说:

接口是指定操作的合同,但没有任何实现。 一些语言(Java,C#)已经支持接口,而在其他接口中则描述了一个像C ++中的纯虚拟类一样的约定。

抽象类是一个类,它至less指定了一个操作而没有实现。 抽象类也可以提供一些实现的部分。 同样,一些语言已经支持将标记类标记为抽象类,并且在其他语言中是隐含的。 例如,在C ++中,定义纯虚拟方法的类是抽象的。

mixin是一个类,它被devise成使子类中的特定function的实现更容易,但是它并不是被devise用来自己使用的。 例如,假设我们有一个处理请求的对象的接口

 interface RequestHandler { void handleRequest(Request request); } 

也许通过累积它们来缓冲这些请求是有用的,直到我们有了一些预定的数字,然后刷新缓冲区。 我们可以用mixin实现缓冲function,而不需要指定刷新行为:

 abstract class BufferedRequestHandlerMixin implements RequestHandler { List<Request> buffer = new List<Request>(); void handleRequest(Request request) { buffer.add(request); if (buffer.size == BUFFER_FLUSH_SIZE) { flushBuffer(buffer); buffer.clear(); } } abstract void flushBuffer(List<Request> buffer); } 

这样,我们很容易编写一个请求处理程序,将请求写入磁盘,调用Web服务等,而不必每次重写缓冲function。 这些请求处理程序可以简单地扩展BufferedRequestHandlerMixin并实现flushBuffer

另一个很好的例子是Spring中的许多支持类之一, HibernateDaoSupport 。

参考Java并给出提供mixin的抽象类的例子是有误导性的。 首先,Java默认不支持“mixins”。 在Java中,抽象类和Mixins变得混乱。

mixin是类可以实现的types,除了它的“主types”,以表明它提供了一些可选的行为。 用Java术语来讲,一个例子就是你的业务价值对象实现Serializable。

Josh Bloch说:“抽象类不能用来定义混合类,因为一个类不能有多个父类”(记住,Java只允许一个“扩展”候选)

寻找像Scala和Ruby这样的语言来适当实现“mixin”的概念

基本上抽象类是一些具体实现的接口。 一个接口只是一个没有实现细节的合约。

如果要在所有实现抽象类的对象中创build通用function,则可以使用抽象类。 保持OOP的干(不要重复自己)的原则。

抽象类是不是所有成员都被实现的类,它们被留给inheritance者来实现。它迫使它的inheritance者实现它的抽象成员。 抽象类不能实例化,因此它们的构造函数不应该公开。]

这是C#中的一个例子:

  public abstract class Employee { protected Employee(){} public abstract double CalculateSalary(WorkingInfo workingInfo);//no implementation each type of employee should define its salary calculation method. } public class PartTimeEmployee:Employee { private double _workingRate; public Employee(double workingRate) { _workingRate=workingRate; } public override double CalculateSalary(WorkingInfo workingInfo) { return workingInfo.Hours*_workingRate; } 

}

接口是一个由类实现的契约,它只是声明一个实现类的成员的签名,它本身没有实现。我们通常使用接口来实现多态,并且将依赖类解耦。

这是C#中的一个例子:

 public interface IShape { int X{get;} int Y{get;} void Draw(); } public class Circle:IShape { public int X{get;set;} public int Y{get;set;} public void Draw() { //Draw a circle } } public class Rectangle:IShape { public int X{get;set;} public int Y{get;set;} public void Draw() { //Draw a rectangle } } 

“Mixin”的含义在Joshua Bloch的有效Java书中得到了很好的定义。 同一本书的摘录:

mixin是一个类,除了”主types“之外,类还可以声明它提供了一些可选的行为,例如,Comparable是一个mixin接口,允许类声明它的实例相对于其他相互比较的对象,这样的接口被称为mixin,因为它允许可选的function被“混合”到types的主要function中。

由于许多人已经解释了这些定义和用法,我只想强调一下重要的一点

接口:

  1. 要定义合同(最好是无状态 – 我的意思是没有变数)
  2. 连接无关的类与“ has a ”的能力。
  3. 声明公共常量variables(不可变状态)

抽象类:

  1. 在几个密切相关的类中共享代码。 它确立了“ is a ”的关系。

  2. 在相关类之间共享状态(可以在具体类中修改状态)

我用一个小例子来弥补差异。

Animal可以是一个抽象的类。 Dog ,延伸这个抽象类,build立“ is a ”关系。

is a动物

is a动物。

can实现Bark界面。 那么狗has a吠叫has a能力。

Cat can实现Hunt接口。 然后猫has a狩猎has a能力。

not Animal可以实现Hunt接口。 那么曼has a狩猎has a能力。

人与动物(猫/狗)是无关的。 但是Hunt接口可以为不相关的实体提供相同的能力。

混入:

  1. 如果你想要abstract classinterface 。 当你想在许多不相关的类上强制一个新的合同时,特别有用,其中一些不必要的重新定义新的行为,其中一些应该坚持共同的实现。 在Mixin中添加通用实现,并允许其他类根据需要重新定义合同方法

如果我想宣布一个抽象类,我将遵循这两种方法之一。

  1. 将所有的抽象方法移动到interface ,我的抽象类实现该接口。

     interface IHunt{ public void doHunting(); } abstract class Animal implements IHunt{ } class Cat extends Animal{ public void doHunting(){} } 

相关的SE问题:

界面和抽象类有什么区别?