代码之家  ›  专栏  ›  技术社区  ›  JDelage

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

  •  12
  • JDelage  · 技术社区  · 15 年前

    如果我们采用以下代码:

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

    喜欢多态性意味着什么? instanceof 顺便说一下,为什么会更好呢?

    编辑: 我知道什么是多态性;我缺少的是如何使用它,而不是 运算符 .

    7 回复  |  直到 15 年前
        1
  •  11
  •   iirekm    15 年前

    如果…否则…(或switch或visitor),多态性之间是模块化的。有一个所谓的开放-关闭原则,这基本上意味着,当你向一个现有的程序添加一个新特性时,你对现有代码所做的更改越少越好(因为每一个更改都需要一些工作,并且可能引入错误)。因此,让我们比较一下变化的数量:

    • 添加一个新方法(例如,您有paint()和getArea(),让我们添加getCircumReference()):使用if-else解决方案,您只需更改一个文件—将包含新方法的文件。对于多态性,您必须更改形状类的所有实现。

    • 添加一种新的形状(您有正方形,圆形-让我们添加三角形):使用if-else解决方案,您必须使用if-else检查所有现有类,并为三角形添加一个新的if分支;使用polymporphism,您只需添加一个新类并在其中实现所有必需的方法。

    所以如果…否则…或者说多态性:这取决于模块化。如果希望稍后添加许多新的子类,请使用多态性;如果希望稍后添加许多新方法,请使用if…else…,并且在类中只放置最“基本”的方法,如访问器。或者换言之:当你期望有很多如果…否则…分支,你更应该使用多态性,当你期望很少这样的分支时,只需留下if…else…

    另外:当你期望很少,如果…否则…分支,但是在很多地方,如果……否则,您应该考虑封装这个。使用Visitor模式,或者只为每个分支创建一个具有单独大小写的枚举。

        2
  •  3
  •   Ian Gilham    15 年前

    这个想法是,你不必关心你要处理的是什么样的形状。例如,如果shape定义了一个abstract draw()方法,那么三角形、正方形和任何其他扩展形状的方法也将相同。

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

    在一个理想的世界中,我们不需要担心我们要处理的对象的具体类型,只需要一个更通用的类型的接口,它涵盖了接口中的所有使用场景。

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

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

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

        3
  •  2
  •   Alberto Zaccagni    15 年前

    考虑以下内容

    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();
    

    一 Square 和A Circle 两者都实现了 getEdgesNumber() 方法,您只需调用它并根据特定的实现获取结果。

    你不需要知道你是否在处理 正方形 或者用 圆圈 ,您只需调用所需的方法并依赖对象的底层实现。

    另外,看看 how the docs are explaining it 。

        4
  •  1
  •   charisis    15 年前

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

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

    (考虑到正方形当然会延伸形状)

    好多了,不是吗?

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

        5
  •  0
  •   willcodejavaforfood    15 年前

    多态性允许您根据某个类型更改某个对象的行为。不知道如何用你的例子来解释它,因为你可以直接把它赋给一个正方形,如果因为某种原因它是一个正方形很重要的话。有时您需要子类化,因为它可能具有其他行为等,但请考虑以下示例:

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

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

        6
  •  0
  •   G B    15 年前

    我假设“shape”是一个接口,“square”是该接口的一个实现。

    现在,如果您需要调用为shape接口声明的方法(典型的例子是shape.getArea()),您不应该关心它是方形的还是其他的,并调用该函数。

        7
  •  0
  •   aaaidan    14 年前

    Some people 相信有一个时间和地点 instanceof 而且访客模式不是 总是 一个完整和适当的替代品。有些人发出嘶嘶声,满脸怒容。冲洗,重复。

    我想你的例子可以通过尝试做点什么来改进 有意义的 (例如绘图、计数边等),因为OOP哲学从根本上避免了您在示例中说明的情况。例如,OOP设计可以声明 c1 作为一个 Shape 而不是 Square 或者简单地使用 p1 变量。

    作为旁白,如果你在c1为空的情况下,如果它不是一个正方形,或者设置为p1,如果它是,一个类似的“a s”操作符存在,我是它的粉丝。

    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”。