83

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

4

7 回答 7

92

抽象类

抽象类是不被设计为实例化的类。抽象类可以没有实现、某些实现或全部实现。抽象类旨在允许其子类共享一个通用(默认)实现。抽象类的(伪编码)示例将是这样的

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关键字。这些或多或少是一个没有实现的抽象类。(关于常量、嵌套类、显式实现和访问修饰符有一些技巧,我不打算讨论。)虽然关于“无实现”的部分不再适合 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# 不允许对具有实现的类进行多重继承,但它们确实允许多个接口实现。Java 和 C# 使用接口来解决在允许多重继承的语言中发现的致命钻石问题(如果处理得当,这并不是那么致命)。

米信

mixin(有时称为特征)允许抽象类的多重继承。Mixin 没有多重继承所具有的可怕关联(由于 C++ 的疯狂),因此人们更愿意使用它们。它们具有完全相同的致命钻石死亡问题,但支持它们的语言具有比 C++ 更优雅的缓解方法,因此它们被认为更好。

Mixin 被誉为具有行为重用更灵活的接口和更强大的接口的接口。您会注意到所有这些都包含术语interface,指的是 Java 和 C# 关键字。Mixin 不是接口。它们是多重继承。用一个更漂亮的名字。

这并不是说 mixins 不好。多重继承也不错。C++ 解决多重继承的方式是每个人都在讨论的问题。

到疲倦的、旧的 Shape 示例

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#代码混合样式的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());
        }
    }
}
于 2013-03-24T22:56:47.287 回答
22

一般来说:

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

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

mixin是一个类,它旨在使某些功能在子类中的实现更容易,但它并非旨在供其自身使用。例如,假设我们有一个用于处理请求的对象的接口

interface RequestHandler {
  void handleRequest(Request request);
}

也许通过累积请求来缓冲请求直到我们有一些预定的数量然后刷新缓冲区是有用的。我们可以在不指定刷新行为的情况下使用mixin实现缓冲功能:

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 服务等,而无需每次都重写缓冲功能。这些请求处理程序可以简单地扩展BufferedRequestHandlerMixin和实现flushBuffer.

另一个很好的 mixin 示例是 Spring 中的众多支持类之一,即。休眠道支持

于 2009-05-27T23:05:04.423 回答
6

对 Java 的引用和提供 mixin 的抽象类的示例具有误导性。首先,Java 默认不支持“mixins”。在 Java 术语中,抽象类和 Mixins 变得令人困惑。

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

Josh Bloch 说 - “抽象类不能用于定义 mixins - 因为一个类不能有多个父类”(记住 Java 只允许一个“扩展”候选者)

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

于 2009-07-29T09:39:41.430 回答
6

由于许多人已经解释了定义和用法,我只想强调重点

界面:

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

抽象类:

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

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

我用一个小例子来结束差异。

Animal可以是抽象类。Cat并且Dog,扩展这个抽象类建立“ is a”关系。

is a动物

is a动物。

can实现Bark接口。然后是狗has a的吠叫能力。

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

Man可以not Animal实现Hunt接口。然后男人has a的狩猎能力。

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

混音:

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

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

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

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

相关的 SE 问题:

接口和抽象类有什么区别?

于 2016-01-08T10:33:49.330 回答
4

Joshua Bloch 在其有效的 Java 书籍中很好地定义了“混合”的含义。同一本书的摘录:

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

于 2014-05-08T04:13:40.717 回答
3

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

如果您想在所有实现抽象类的对象中创建通用功能,您将使用抽象类。遵循 OOP 的 DRY(不要重复自己)原则。

于 2009-05-27T23:01:37.610 回答
1

抽象类是一个并非所有成员都实现的类,它们留给继承者实现。它强制其继承者实现其抽象成员。抽象类不能被实例化,因此它们的构造函数不应该是公共的。]

这是 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
}
}
于 2009-05-27T23:33:23.100 回答