抽象类与接口与mixins

Sas*_*gov 68 oop abstract-class terminology interface mixins

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

Eva*_*Eva 75

抽象类

抽象类是一个不是为实例化而设计的类.抽象类可以没有实现,某些实现或所有实现.抽象类旨在允许其子类共享一个公共(默认)实现.抽象类的(伪编码)示例将是这样的

abstract class Shape {
    def abstract area();  // abstract (unimplemented method)
    def outline_width() = { return 1; }  // default implementation
}
Run Code Online (Sandbox Code Playgroud)

子类可能看起来像

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
}
Run Code Online (Sandbox Code Playgroud)

可能的用法

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

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

接口

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

Java和C#有一个特殊的interface关键字.这些或多或少是一个没有实现的抽象类.(关于常量,嵌套类,显式实现和访问修饰符的诡计我不打算进入.)虽然关于"无实现"的部分不再适合Java,但它们添加了默认方法.的interface关键字可以被看作是所述接口概念的具体化.

回到Shape示例

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());
    }
}
Run Code Online (Sandbox Code Playgroud)

Java和C#不允许使用实现进行多重继承,但它们允许多个接口实现.Java和C#使用接口作为死亡钻石死亡问题的解决方法在允许多重继承的语言中找到(如果处理得当,这并不是那么致命).

混入

mixin(有时称为trait)允许抽象类的多重继承.Mixins没有多重继承所带来的可怕关联(由于C++疯狂),所以人们更习惯使用它们.它们具有完全相同的死亡钻石死亡问题,但支持它们的语言比C++具有更优雅的缓解方式,因此它们被认为更好.

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

这并不是说mixin很糟糕.多重继承也不错.C++解决多重继承的方式是每个人都能解决的问题.

关于累旧的形状例子

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());
    }
}
Run Code Online (Sandbox Code Playgroud)

您会注意到这与抽象类示例之间没有区别.

一个额外的花絮是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());
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

  • 需要说明的是:Mixin\Traits是在不使用继承的情况下扩展类功能的方法(例如添加方法).answear认为它们是相同的,但存在一些重要的差异.1. Mixins可能是statefull,traits是无状态2.Mixins使用隐式冲突解决,traits使用显式冲突解决(更多http://stackoverflow.com/questions/925609/mixins-vs-traits) (6认同)
  • gh,我讨厌更改已接受的答案,但这是最好的答案。 (2认同)

Pau*_*rie 21

一般来说:

一个接口是指定操作的合同,但没有任何实现.有些语言(Java,C#)具有对接口的支持,而在其他语言中,"接口"描述了一种类似于C++中的纯虚拟类的约定.

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

一个混入的,其目的是使执行特定功能的子类更容易,但它没有设计成单独使用的类.例如,假设我们有一个处理请求的对象的接口

interface RequestHandler {
  void handleRequest(Request request);
}
Run Code Online (Sandbox Code Playgroud)

也许通过累积它们来缓冲请求是有用的,直到我们有一些预定的数字,然后刷新缓冲区.我们可以使用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);
}
Run Code Online (Sandbox Code Playgroud)

这样,我们就可以轻松编写请求处理程序,将请求写入磁盘,调用Web服务等,而无需每次都重写缓冲功能.这些请求处理程序可以简单地扩展BufferedRequestHandlerMixin和实现flushBuffer.

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

  • 等等,也许我遗漏了一些东西,但 BufferedRequestHandlerMixin 不是一个抽象类吗?这与 Mixin 有何不同? (2认同)

小智 6

引用Java并给出Abstract类的例子来提供mixin是误导性的.首先,Java默认不支持"mixins".在Java术语中,抽象类和Mixins变得令人困惑.

mixin是类可以实现的类型,除了它的"主类型"以指示它提供一些可选行为.用Java术语来说,一个例子是实现Serializable的业务价值对象.

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

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


Rav*_*abu 6

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

界面:

  1. 定义合同(最好是无状态的——我的意思是没有变量)
  2. 将不相关的类与“ has a”功能联系起来。
  3. 声明公共常量变量(不可变状态)

抽象类:

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

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

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

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

is a动物

is a动物。

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

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

,谁是not Animal,可以实现Hunt接口。然后是Manhas 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(){}
    }
    
    Run Code Online (Sandbox Code Playgroud)

相关 SE 问题:

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