如果我们采用下面的代码:
Shape p1 = new Square();
Square c1;
if(p1 instanceof Square) {
c1 = (Square) p1;
}
更喜欢多态性是什么意思instanceof
,顺便说一句,为什么它更好?
编辑: 我明白什么是多态性;我缺少的是如何使用它而不是instanceof
.
如果我们采用下面的代码:
Shape p1 = new Square();
Square c1;
if(p1 instanceof Square) {
c1 = (Square) p1;
}
更喜欢多态性是什么意思instanceof
,顺便说一句,为什么它更好?
编辑: 我明白什么是多态性;我缺少的是如何使用它而不是instanceof
.
if...else...(或 switch,或 Visitor)与多态性之间的主要区别在于模块化。有所谓的开闭原则,基本上就是说,当你在现有程序中添加新功能时,你对现有代码所做的更改越少越好(因为每次更改都需要一些工作,并且可能会引入错误)。所以让我们比较一下变化的数量:
添加新方法(例如,您有paint() 和getArea(),让我们添加getCircumference()):使用if-else 解决方案您只需更改一个文件- 将包含新方法的文件。使用多态性,您必须更改 Shape 类的所有实现。
添加一种新的形状(您有 Square、Circle - 让我们添加 Triangle):使用 if-else 解决方案,您必须使用 if-else 检查所有现有类并为 Triangle 添加新的 if 分支;对于多态性,您所拥有的只是添加一个新类并在其中实现所有必需的方法。
所以 if...else... 或多态性:它取决于模块化。如果您预计以后会添加许多新的子类,请使用多态性;如果您希望稍后会添加许多新方法,请使用 if...else...,并且在类中仅放置最“基本”的方法,例如访问器。或者换句话说:当你期望有很多 if...else... 分支时,你应该使用多态性,当你期望这样的分支很少时,就留在 if...else...
另外:当您期望很少的 if...else... 分支,但在很多地方,您应该考虑使用访问者模式封装这个 if...else... 或者只是为每个分支创建一个带有单独案例的枚举.
这个想法是你不应该关心你正在处理什么样的形状。例如,如果 Shape 定义了一个抽象的 draw() 方法,那么三角形、正方形和任何其他扩展 Shape 的东西也将具有相同的方法。
多态性的一个简单定义是“将不同类型视为相同”,即使用相同的接口。
在一个理想的世界里,我们不想担心我们正在处理的对象是什么具体类型,只需要一个更通用的类型的接口,在它的接口中涵盖所有使用场景。
Shape p1 = new Square();
Shape p2 = new Triangle();
p1.draw();
p2.draw();
在这段代码中,我们直接在 p1 和 p2 上调用 Shape.draw()。我们不关心实现类做了什么,只关心接口(或抽象父类)定义了什么。
编辑:关于问题中的示例,通常建议通过尽可能封装行为来避免这种代码模式。使用 instanceof 可以被认为是代码异味,因为每当您添加新类时,您都必须更新所有条件。
考虑以下
abstract class Shape {
public abstract int getEdgesNumber();
}
class Square extends Shape {
public int getEdgesNumber(){
return 4;
}
}
class Circle extends Shape {
public int getEdgesNumber(){
return 1; //not so sure it counts as one but for the example is fine ^^'
}
}
Shape square = new Square();
int squareEdgesNumber = square.getEdgesNumber();
Shape circle = new Circle();
int circleEdgesNumber = circle.getEdgesNumber();
ASquare
和 aCircle
都实现了该getEdgesNumber()
方法,您只需根据特定的实现调用它并获取结果。
你不需要知道你是在处理 aSquare
还是 a Circle
,你只需调用你需要的方法并依赖于对象的底层实现。
另外,看看文档是如何解释它的。
这并不是一个很好的例子,但这就是您的代码的样子。
Square c1 = new Square();
Shape p1 = c1;
(当然,考虑到 Square 扩展了 Shape )
好多了不是吗?
至于“为什么更好”,其他答案给出了一些要点。
多态性使您可以根据某物的类型来更改某物的行为。不知道如何用你的例子来解释它,因为如果出于某种原因它是一个 Square 很重要,你可以直接将它分配给一个 Square。有时您需要子类化,因为它可能具有其他行为等,但请考虑以下示例:
class Shape
{
abstract void draw();
}
class Square extends Shape
{
void draw()
{
// square drawing goes here
}
}
这里的 draw 方法是多态性的一个例子,因为我们有一个基类 Shape,它说现在所有的形状都如何绘制自己,但只有 Square 知道如何绘制 Square。
我假设“Shape”是一个接口,而“Square”是该接口的一个实现。
现在,如果您需要调用为 Shape 接口声明的方法(典型示例是 Shape.getArea()),您不应该关心它是 Square 还是其他东西,而是调用该函数。
有些人认为 有时间和地点instanceof
,并且访问者模式并不总是完整且适当的替代品。其他一些人嘶嘶作响,皱着眉头。冲洗,重复。
我认为您的示例可以通过尝试做一些有意义的事情(例如绘图或计算边等)来改进,因为 OOP 哲学将从根本上避免您在示例中说明的情况。例如,OOP 设计要么声明c1
为 aShape
而不是 a Square
,要么简单地使用p1
变量。
顺便说一句,如果您遇到 c1 如果不是 Square 则为 null 或设置为 p1 如果是的话,则存在类似的“as”运算符,我很喜欢。
Shape p1 = (Math.random()>0.5) ? new Square() : new Circle();
Square c1 = p1 as Square;
// c1 is null or not, depending on p1's type.
在我看来,这并不比 OO 多instanceof
,但话又说回来,我真的不认为instanceof
是天生的“非 OO”。