我无法理解何时使用接口而不是抽象类,反之亦然。另外,我很困惑何时用另一个接口扩展一个接口。很抱歉这篇文章很长,但这很令人困惑。
创建形状似乎是一个流行的起点。假设我们想要一种建模 2D 形状的方法。我们知道每个形状都会有一个区域。以下两种实现之间有什么区别:
带接口:
public interface Shape {
public double area();
}
public class Square implements Shape{
private int length = 5;
public Square(){...}
public double area()
return length * length;
}
}
带有抽象类:
abstract class Shape {
abstract public double area();
}
public class Square extends Shape {
private length = 5;
public Square(){...}
public double area(){
return length * length;
}
我知道抽象类允许您定义实例变量并允许您提供方法实现,而接口不能做这些事情。但在这种情况下,这两个实现似乎是相同的。那么使用任何一个都可以吗?
但是现在说我们要描述不同类型的三角形。我们可以有等腰、锐角和直角三角形。对我来说,在这种情况下使用类继承是有意义的。使用“IS-A”定义:直角三角形“IS-A”三角形。三角形“IS-A”形状。此外,抽象类应该定义所有子类中通用的行为和属性,所以这是完美的:
带抽象类
abstract Triangle extends Shape {
private final int sides = 3;
}
class RightTriangle extends Triangle {
private int base = 4;
private int height = 5;
public RightTriangle(){...}
public double area() {
return .5 * base * height
}
}
我们也可以使用接口来做到这一点,Triangle 和 Shape 就是接口。但是,与类继承不同(使用“IS-A”关系来定义什么应该是子类),我不确定如何使用接口。我看到两种方法:
第一种方式:
public interface Triangle {
public final int sides = 3;
}
public class RightTriangle implements Triangle, Shape {
private int base = 4;
private int height = 5;
public RightTriangle(){}
public double area(){
return .5 * height * base;
}
}
第二种方式:
public interface Triangle extends Shape {
public final int sides = 3;
}
public class RightTriangle implements Triangle {
....
public double area(){
return .5 * height * base;
}
}
在我看来,这两种方式都有效。但是你什么时候会使用一种方式而不是另一种方式?使用接口而不是抽象类来表示不同的三角形有什么好处吗?即使我们将形状的描述复杂化,使用接口与抽象类看起来仍然是等价的。
接口的一个关键组件是它可以定义可以在不相关的类之间共享的行为。所以一个接口 Flyable 将出现在飞机类和鸟类中。因此,在这种情况下,显然首选接口方法。
此外,为了构建扩展另一个接口的令人困惑的接口:在决定什么应该是接口时,什么时候应该忽略“IS-A”关系?举个例子:LINK。
为什么“VeryBadVampire”应该是一个类而“Vampire”应该是一个接口?'VeryBadVampire' IS-A'Vampire',所以我的理解是'Vampire' 应该是一个超类(可能是抽象类)。“吸血鬼”类可以实现“致命”以保持其致命行为。此外,一个“吸血鬼”是一个“怪物”,所以“怪物”也应该是一个类。“Vampire”类还可以实现一个名为“Dangerous”的接口来保持其危险行为。如果我们希望创建一个名为“BigRat”的新怪物,它是危险但不致命的,那么我们可以创建一个扩展“Monster”并实现“Dangerous”的“BigRat”类。
上述内容不会与使用“吸血鬼”作为接口(在链接中描述)获得相同的输出吗?我看到的唯一区别是使用类继承并保留“IS-A”关系消除了很多混乱。然而这并没有被遵循。这样做有什么好处?
即使你想让怪物分享吸血鬼的行为,也总是可以重新定义对象的表示方式。如果我们想要一种名为“VeryMildVampire”的新型吸血鬼怪物,并且我们想要创建一个名为“Chupacabra”的类似吸血鬼的怪物,我们可以这样做:
“Vampire”类扩展“Monster”实现“Dangerous”、“Lethal”、“BloodSuckable”
“VeryMildVampire”类扩展“Vampire”类
“Chupacabra”类扩展“Monster”实现“BloodSuckable”
但我们也可以这样做:
'VeryMildVampire' 扩展了 'Monster' 工具危险、致命、吸血鬼
'Chupacabra' 扩展了 'Monster' 工具危险、吸血鬼
第二种方法在这里创建一个“Vampiric”接口,以便我们可以更轻松地定义一个相关的怪物,而不是创建一堆定义吸血鬼行为的接口(如第一个示例中)。但这打破了 IS-A 关系。所以我很困惑...