如何使用多态而不是instanceof? (为什么?)

如果我们采用以下代码:

Shape p1 = new Square(); Square c1; if(p1 instanceof Square) { c1 = (Square) p1; } 

将多态性与instanceof什么意义,顺便说一句,为什么它更好?

编辑:我理解多态是什么; 我缺少的是如何使用它而不是instanceof

if … else …(或switch,或者访问者)和多态之间的主要区别是模块性。 有一种所谓的开放闭合原则,它基本上意味着,当您向现有程序添加新function时,您在现有代码中所做的更改越少越好(因为每次更改都需要一些工作,并且可能会引入错误)。 那么让我们比较一下变化的数量:

  • 添加一个新方法(例如你有paint()和getArea(),让我们添加getCircumference()):使用if-else解决方案你只需要改变一个文件 – 包含新方法的文件。 使用多态,您必须更改Shape类的所有实现。

  • 添加一种新的Shape(你有Square,Circle – 让我们添加Triangle):使用if-else解决方案,你必须使用if-else查看所有现有的类,并为Triangle添加一个新的if分支; 使用多态性,您只需要添加一个新类并在其中实现所有必需的方法。

所以,如果……别…或多态:它取决于模块性。 如果您希望以后添加许多新子类,请使用多态; 如果您希望以后添加许多新方法,请使用if … else …,并在类中只放置最基本的方法,如访问器。 或者换句话说:当你期望有很多if … else …分支时,你应该使用多态,当你期望很少这样的分支时,只要留下来,如果……其他……

另外:当你期望很少if … else …分支,但在很多地方,你应该考虑封装这个if … else … with Visitor模式或只是为每个分支制作一个单独的case的枚举。

这个想法是你不应该关心你正在处理什么样的形状。 例如,如果Shape定义了一个抽象的draw()方法,那么Triangles,Squares和其他任何扩展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(); 

SquareCircle都实现了getEdgesNumber()方法,您只需调用它并根据特定实现获取结果。

您不需要知道您是在处理Square还是使用Circle ,只需调用所需的方法并依赖于对象的底层实现。

另外,看看文档如何解释它 。

这不是一个很好的例子,但这就是你的代码的样子。

 Square c1 = new Square(); Shape p1 = c1; 

(鉴于Square当然延伸了形状)

不是更好吗?

至于“为什么它更好”,其他答案给出了一些重要的观点。

多态性允许您根据它的类型更改某些内容的行为。 不知道如何用你的例子解释它,因为你可以直接将它分配给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还是其他东西,并调用该函数。

有些人认为有一个时间和地点,并且访客模式并不总是一个完整和适当的替代品。 其他一些人嘶嘶作响。 冲洗,重复。

我认为你的例子可以通过尝试做一些有意义的事情(例如绘图或计算边等)来改进,因为OOP哲学将从根本上避免你在你的例子中说明的情况。 例如,OOP设计要么将c1声明为Shape而不是Square ,要么简单地使用p1变量。

顺便说一下,如果你的情况是c1为null,如果它不是Square,或者设置为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. 

在我看来,它并不比instanceof更好,但是我再也不认为instanceof是非“非OO”。