【问题标题】:How does one use polymorphism instead of instanceof? (And why?)如何使用多态性而不是 instanceof? (为什么?)
【发布时间】:2011-05-10 17:17:57
【问题描述】:

如果我们采用下面的代码:

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

多态性优于instanceof 是什么意思,顺便说一句,为什么它更好?

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

【问题讨论】:

    标签: java polymorphism instanceof


    【解决方案1】:

    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... 或者只是用一个单独的案例制作一个枚举每个分支。

    【讨论】:

    • 认为这回答了我的问题。它至少给出了一些理由,说明为什么有人会支持instanceof 的多态性。非常感谢!
    • 我只想补充一点,多态性不仅可以通过使用继承从类扩展来实现,还可以通过实现接口来实现,在这种情况下,只有所有类都使用新方法实现该接口是当您必须修改所有这些时,但如果不是所有这些都使用或者与关系无关,那么它不会影响所有类,因此如果可能的话使用多态使用接口而不是继承。
    【解决方案2】:

    我们的想法是,您不必关心您处理的是哪种形状。例如,如果 Shape 定义了一个抽象的 draw() 方法,那么三角形、正方形和任何其他扩展 Shape 的东西也将具有相同的方法。

    多态性的一个简单定义是“将不同类型视为相同”,即使用相同的接口。

    在理想的世界里,我们不想担心我们正在处理的对象是什么特定类型的,只需要关心其接口中涵盖所有使用场景的更通用类型的接口。

    Shape p1 = new Square();
    Shape p2 = new Triangle();
    p1.draw();
    p2.draw();
    

    在这段代码中,我们直接在 p1 和 p2 上调用 Shape.draw()。我们不关心实现类做了什么,只关心接口(或抽象父类)定义了什么。

    编辑:关于问题中的示例,通常建议通过尽可能封装行为来避免这种代码模式。使用 instanceof 可能会被视为代码异味,因为您必须在添加新类时更新所有条件。

    【讨论】:

      【解决方案3】:

      考虑以下

      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,您只需调用所需的方法并依赖对象的底层实现即可。

      另外,看看how the docs are explaining it

      【讨论】:

      • 我觉得这有点误导。我宁愿把 Shape 看作一个界面。
      • @GB:很有趣,我在写它的时候也有同样的问题,你能说为什么你会使用接口而不是抽象类吗?
      • 一个界面可能会也可能不会更可取,但它似乎不会误导我。
      • 我认为接口更适合描述行为(显示为 getEdgesNumber),而抽象类更适合结构和成员。也许这是一个任意或不相关的区别,但它对我有帮助
      • 因为Java“缺乏”多重继承,您可能希望在一个类中实现多个接口。当我对大多数函数都有一个通用实现时,我会使用抽象类,并且通常会保护我的抽象方法(例如:检查先决条件的“执行”方法,然后调用受保护的“executeInternal”)。
      【解决方案4】:

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

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

      (当然,鉴于 Square 扩展了 Shape)

      好多了不是吗?

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

      【讨论】:

      • 也许问题中的示例已被更改,但我认为您的观点是正确的。
      【解决方案5】:

      多态性让您可以根据事物的类型来改变事物的行为。不知道如何用你的例子来解释它,因为如果出于某种原因它是一个 Square 很重要,你可以直接将它分配给一个 Square。有时您需要子类化,因为它可能具有其他行为等,但请考虑以下示例:

      class Shape
      {
          abstract void draw();
      }
      
      class Square extends Shape
      {
          void draw()
          {
              // square drawing goes here
          }
      }
      

      这里的draw方法是多态的一个例子,因为我们有一个基类Shape,它说现在所有的形状都如何绘制自己,但只有Square知道如何绘制Square。

      【讨论】:

        【解决方案6】:

        我假设“Shape”是一个接口,而“Square”是该接口的实现。

        现在,如果您需要调用为 Shape 接口声明的方法(典型示例是 Shape.getArea()),您不应该关心它是 Square 还是其他东西,而是调用该函数。

        【讨论】:

          【解决方案7】:

          Some people 相信instanceof 是有时间和地点的,而访问者模式并不总是完全和适当地替代它。其他一些人嘶嘶作响,皱着眉头。冲洗,重复。

          我认为您的示例可以通过尝试做一些有意义的(例如绘制或数边等)来改进,因为 OOP 哲学将从根本上避免您在示例中说明的情况。例如,OOP 设计要么将 c1 声明为 Shape 而不是 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.
          

          在我看来,它不比 instanceof 更 OO,但我也不认为 instanceof 天生就是“非 OO”。

          【讨论】:

            猜你喜欢
            • 2012-03-15
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2013-09-05
            • 2014-12-01
            • 2016-10-10
            相关资源
            最近更新 更多