有人可以向我解释抽象类,接口和 mixins 之间的区别吗?我以前在我的代码中使用了每个,但我不知道技术差异。
答案 0 :(得分:69)
抽象类是一个不是为实例化而设计的类。抽象类可以没有实现,某些实现或所有实现。抽象类旨在允许其子类共享一个公共(默认)实现。抽象类的(伪编码)示例将是这样的
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
关键字可以看作是界面概念的具体化。
回到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());
}
}
Java和C#不允许对具有实现的类进行多重继承,但它们允许多个接口实现。 Java和C#使用接口作为允许多重继承的语言中的Deadly Diamond of Death Problem的变通方法(如果处理得当,这种方法并不是那么致命)。
mixin(有时称为trait)允许抽象类的多重继承。 Mixins没有多重继承所带来的可怕关联(由于C ++疯狂),所以人们更习惯使用它们。它们具有相同的致命死亡钻石问题,但支持它们的语言比C ++具有更优雅的缓解方式,因此它们被认为更好。
Mixins被誉为具有behavioral reuse,more flexible接口和more powerful接口的接口。您将注意到所有这些都包含术语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());
}
}
您会注意到它与抽象类示例之间没有区别。
一个额外的花絮是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());
}
}
}
答案 1 :(得分:21)
一般来说:
接口是指定操作的合同,但没有任何实现。有些语言(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中的许多支持类之一,即。 HibernateDaoSupport
答案 2 :(得分:6)
引用Java并给出Abstract类的示例以提供mixin是误导性的。 首先,Java默认不支持“mixins”。在Java术语中,抽象类和Mixins变得令人困惑。
mixin是类可以实现的类型,除了它的“主类型”以指示它提供一些可选行为。用Java术语来说,一个例子是实现Serializable的业务价值对象。
Josh Bloch说 - “抽象类不能用于定义mixins - 因为一个类不能有多个父级”(记住Java只允许一个“扩展”候选者)
寻找像Scala和Ruby这样的语言来正确实现“mixin”的概念
答案 3 :(得分:3)
基本上,抽象类是具有一些具体实现的接口。接口只是一个没有实现细节的合同。
如果要在实现抽象类的所有对象中创建公共功能,则可以使用和抽象类。保持OOP的DRY(不要重复自己)原则。
答案 4 :(得分:3)
由于许多人已经解释了定义和用法,我想强调一些重点
界面:
has a
&#34;相关联。能力。抽象类:
在几个密切相关的类之间共享代码。它建立了&#34; is a
&#34;关系。
在相关类之间共享公共状态(状态可以在具体类中修改)
我用一个小例子来弥补差异。
Animal
可以是一个抽象类。 Cat
和Dog
,扩展此抽象类会建立&#34; is a
&#34;关系。
猫 is a
动物
狗 is a
动物。
Dog can
实施Bark
界面。然后是狗has a
Barking的能力。
Cat can
实施Hunt
界面。然后是Cat has a
狩猎的能力。
not Animal
的 人可以实现Hunt
界面。然后是has a
狩猎的能力。
人与动物(猫/狗)无关。但是Hunt接口可以为不相关的实体提供相同的功能。
<强> 密新: 强>
abstract class
和interface
。当您想要在许多不相关的类上强制新合同时,尤其有用,其中一些不得不重新定义新行为,其中一些应该坚持常见的实现。在Mixin中添加常用实现,并允许其他类在需要时重新定义合同方法如果我想宣布一个抽象类,我将遵循这两种方法中的一种。
将所有抽象方法移至interface
,我的抽象类实现该接口。
interface IHunt{
public void doHunting();
}
abstract class Animal implements IHunt{
}
class Cat extends Animal{
public void doHunting(){}
}
相关的SE问题:
What is the difference between an interface and abstract class?
答案 5 :(得分: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
}
}
答案 6 :(得分:1)
&#39; Mixin&#39;约书亚布洛赫在他有效的爪哇书中出色地定义了这本书。同一本书的摘录:
&#34; mixin是一种类型 一个类除了声明它提供的“主要类型”之外还可以实现 一些可选行为。例如,Comparable是一个mixin接口 允许类声明其实例是相对于其他实例进行的 可比对象。这样的界面被称为mixin,因为它允许 可选功能“混入”类型的主要功能。&#34;