抽象类与接口与混合


Answers:


83

抽象类

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

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(有时称为特征)允许抽象类的多重继承。Mixins没有多重继承所具有的可怕关联(由于C ++的疯狂),因此人们更习惯使用它们。它们具有完全相同的“致命问题致命钻石”,但是支持它们的语言比C ++具有更优雅的缓解方法,因此人们认为它们更好。

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

这并不是说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。您可以使用接口上的扩展方法来实现。这是带有real(!)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());
        }
    }
}

2
gh,我讨厌更改已接受的答案,但这是最好的答案。
Sasha Chedygov 2013年

7
需要明确的是:Mixin \ Traits是在不使用继承的情况下扩展类功能(例如,添加方法)的方法。s头表示他们是同一回事,但有一些重要的区别。1.混入可能是有状态的,特点是无状态的2混入使用隐解决冲突,性状使用明确的解决冲突(更多 stackoverflow.com/questions/925609/mixins-vs-traits
伊戈尔Wojda

22

一般来说:

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

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

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

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中的许多支持类之一,即。HibernateDaoSupport


太好了,这正是我所需要的,谢谢。但是,有一件事:您能否确切说明mixins如何“简化子类中某些行为的实现”?
2009年

让我知道示例是否有帮助。
保罗·莫里

2
等一下,也许我丢失了一些东西,但是BufferedRequestHandlerMixin不是抽象类吗?这与Mixin有何不同?
mR_fr0g

mR_fr0g,是的,BufferedRequestHandlerMixin被实现为抽象类。在Java中使用抽象类来实现mixins是很常见的,因为abstract关键字指出该类是为重用而设计的,并不意味着该类将被自己使用(显然,它也阻止了您自己使用它)。
Paul Morie 09年

“抽象类是指定至少一个没有实现的操作的类”:对于.NET来说不是很正确。例如,以下是c#中的有效类定义:“公共抽象类Foo {}”
Darin Dimitrov,2009年

6

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

混合是一种类,除了其“主要类型”以外,还可以实现以指示它提供了一些可选的行为。用Java术语来说,一个示例是实现Serializable的业务价值对象。

乔什·布洛赫(Josh Bloch)说-“抽象类不能用于定义mixin-因为一个类不能有多个父类”(请记住Java仅允许一个“扩展”候选者)

寻找诸如Scala和Ruby之类的语言来适当实现“ mixin”概念


还有JavaScript,这是使用混合概念最广泛使用的语言。
Gaurav Ramanan

1
在Java 8版本中,接口中的默认方法提供了混合功能。
Ravindra babu

5

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

接口:

  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的狩猎能力。

男人,谁是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问题:

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


3

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

如果要在实现抽象类的所有对象之间创建通用功能,则可以使用abstract类。遵守OOP的DRY(不要重复自己)原则。


3
不仅如此。例如,抽象类可以定义抽象的受保护方法,而接口则不能。您可以实现任意数量的接口,但只能扩展一个抽象类(除非您的语言具有多重继承)。
09年

是的,肯定有比我所描述的更多的内容(对于@musicfreak,您应该进行更多研究,因为有关此主题的内容很多)。因为他说他/她想要一些技术含量较低的东西,所以只是想对OP进行快速定义。
乔恩·埃里克森

+1使其简单易懂。另外,n3rd,感谢您的信息。
2009年

3

约书亚·布洛赫(Joshua Bloch)在其有效的Java书中很好地定义了“ Mixin”的含义。摘自同一本书:

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


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
}
}
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.