Java 类似多态性的参数处理——简单的OO?

Java 类似多态性的参数处理——简单的OO?,java,polymorphism,overloading,Java,Polymorphism,Overloading,我肯定以前一定有人问过这个问题,但我似乎找不到类似的例子。我很了解多态性和方法重载,但下面是一个看似简单的场景,其中有一个我无法理解的解决方案: 假设我有一个基类和几个派生类。我将在这个例子中使用形状 base Shape derived Circle extends Shape derived LineSeg extends Shape 等等 现在,shape有一个名为intersect(other)的方法,该方法针对另一个形状进行测试,以查看它们是否相交。通过多态性,很容易看到Circle

我肯定以前一定有人问过这个问题,但我似乎找不到类似的例子。我很了解多态性和方法重载,但下面是一个看似简单的场景,其中有一个我无法理解的解决方案:

假设我有一个基类和几个派生类。我将在这个例子中使用形状

base Shape
derived Circle extends Shape
derived LineSeg extends Shape
等等

现在,shape有一个名为intersect(other)的方法,该方法针对另一个形状进行测试,以查看它们是否相交。通过多态性,很容易看到Circle、LineSeg等如何实现它们自己的“intersect”方法,通过方法重载,我可以轻松实现所有需要的组合。e、 g

Circle.intersect(LineSeg)
Circle.intersect(Circle)
LineSeg.intersect(Circle)
等等

到目前为止还不错

问题是,如果我保留形状的中心列表,我想这样做:

for some shape s
Foreach shape in Shapes
  if (s.intersect(shape)) - do something
目前,我不确定这是如何实现的,因为方法重载选择“intersect”方法来匹配基类型形状,而不是合适的参数类型。如果没有if-else链检查类型和向下浇铸,我如何进行此操作

顺便说一句,我正在使用Java,但我不确定该语言是否完全相关,因为它似乎是一个基本的设计问题。看起来很简单,我错过了什么

谢谢



下面已解决(谢谢!),请参见此处的详细信息。基本上,通过在派生类中调用适当的方法(访问者模式?)的回调,您可以使用“this”关键字调用适当的intersect方法,因为它具有所需的适当类型

我的第一个想法是访问者模式,几乎给每个形状两种方法,一种是我将调用的
intersect(Shape)
,另一种是每个形状类型的
doIntersect()
方法

它看起来像这样:

interface Shape {
    public abstract Intersection intersect(Shape other);

    public abstract Intersection doIntersect(Circle circle);

    public abstract Intersection doIntersect(LineSeg line);
}
class LineSeg implements Shape {
    @Override
    public Intersection intersect(Shape other) {
        return other.doIntersect(this);
    }

    Intersection doIntersect(Circle circle) {
        // Code to intersect with Circle
    }

    Intersection doIntersect(LineSeg other) {
       // Code to intersect with another Lineseg
    }
}

class Circle implements Shape {
    @Override
    public Intersection intersect(Shape other) {
        return other.doIntersect(this);
    }

    public Intersection doIntersect(Circle other) {
        // Code to intersect with another Circle
    }

    public Intersection doIntersect(LineSeg segment) {
        // Code to intersect with LineSeg
    }
}
您可能希望doIntersect方法是包私有的,或者选择不同的名称。

请参阅泛型Jdk5


对于Shapes中的每个shape

您的shape类必须是一个抽象类,这意味着它不需要实例化,只有派生类circle和lineseg具有实例。intersect方法在形状中应该是虚拟的,因此当您在所有形状上循环时,将调用每个形状的intersect方法

public abstract class Shape {

    boolean intersect(Shape s);
}

public class Circle extends Shape {

    boolean intersect(Shape s) {
        ...
        if( s instanceOf Circle ) { 
           .... // Circle intersects cicrcle
        } else if( s instanceOf Lineseg ) {
           .... // Circle intersects Lneseg
        } else {
          throw RuntimeException("Unrecognized shape");
        }

    }

}

public class Lineseg extends Shape {

    boolean intersect(Shape s) {
        ...
        if( s instanceOf Circle ) { 
           .... // Lineseg intersects circle
        } else if( s instanceOf Lineseg ) {
           .... // Lineseg intersects lineseg
        } else {
          throw RuntimeException("Unrecognized shape");
        }
    }

}

这太棒了,谢谢。快速轻松地解决了我的问题。我需要在基类中添加所有参数类型的可能性作为原型,强制子类处理任何可能性。另外一个改进:您应该能够命名所有方法
intersect()
,而不是命名特定的方法
doIntersect()
。据我所知,编译器首先选择最具体的重载。谢谢-我已经这样做了,但决定留下你的建议,因为它可能不那么令人困惑。我还尝试将它放在基类中,以便只需要一个,但是在基类方法中使用“this”具有基类类型,不幸的是(我知道为什么),对不起,我要求的答案没有进行if-else类型检查chain@user1922401但是您愿意为每个派生类中的每个派生类编写一个方法,这是一样的。只是这种方式是处理多态性的经典OO方式。它不同,因为通过访问者模式,您只有一个额外的间接寻址。在if-else链中,每次调用该方法时,最多有n次检查。此外,建议的解决方案稍微安全一些。如果我添加一个新类型,我必须将新类型的intersect头添加到基类中,否则它将无法编译,并且,它会强制所有其他形状类型来处理它-在您的解决方案中,我可能会意外地忘记它。虽然您确实建议在每个类中使用if-else,但这可能是centralized@user1922401您知道,if-else链无论如何都是由编译后的代码完成的,以选择要调用的正确方法。编程没有魔力。由于方法的
抽象性
性质,如果添加新类,则两种解决方案都不会编译,当方法是抽象的时,必须在任何新的派生类中实现它。在任何情况下,if-else都可以以`else{throw exception“unrecognized shape”}@ilomambo结束:如果其他条件相同,这就成了可读性问题。在我看来,与针对不同类型使用不同方法相比,这个大型if-else链的可读性较差。我同意你的观点,除非我们以某种方式处理1000种类型,否则性能差异将完全可以忽略。一些编程语言更直接地支持参数运行时类型的重载。例如,LISP支持多种方法,我想一些函数式编程语言也支持这种方法。在Java中,这通常被称为双重分派,GoF访问者模式是一种方式。不要挑剔您的设计,但您的继承有点错误。线段不是真正的形状。它们组成形状。根据形状,您可以检查多个线段中任意一个的交点,这两个线段组成了一个有问题的两个形状:)@C.Lang,很好,但是如果我将lineSeg重命名为“wall”呢?这就是它的基本用法,但称之为lineSeg可以更容易地在其他更复杂的形状中概念化使用它。您可以做最有意义的事情。对我来说这有点误导,因为形状有物理尺寸,而线条没有。如果你在想一个例子,它们是这样做的,那么最好用填充矩形来描述。在大多数情况下,墙甚至比矩形更具体。我可能会说:形状=>四边形=>矩形=>墙-形状有线条的地方。