【问题标题】:Accept superclass as parameter but use subclass in method接受超类作为参数,但在方法中使用子类
【发布时间】:2019-02-08 21:08:39
【问题描述】:

提前感谢您的回答。 我可能没有正确形成问题所需的所有知识,但在这里: 我有一堆扩展 Shape 超类(圆、矩形、点)的类。 我有一个静态类 Geometry,它有一个方法可以检查不同形状(圆形、矩形、三角形)之间的交集。 在“几何”静态类中,我实现了“相交”方法来接受任何形状组合

  public static boolean intersects(Rectangle rect, Point point) {
    return rectIntersectsPoint(rect, point);
  }
  public static boolean intersects(Circle circle, Point point) {
    return circleIntersectsPoint(circle, point);
  }
  public static boolean intersects(Circle circle, Rectangle rect) {
    return circleIntersectsRect(circle, rect);
  }

每个案例还有更多。 现在,在我的程序中,我有一个方法必须检查交集并且必须接受 Shape 的任何子类:

  List<Point> query(Shape shape) {
    if (Geo.intersects(this.b, shape)) {
      return this.found;
    }
   ......
  }

我是这样使用它的:

Rectangle rect = new Rectangle(10, 10, 200, 300);
found = qt.query(rect );

但我收到一条错误消息,提示“函数相交需要像 (Rectangle, Circle) 这样的参数”。

所以我将一个 Rectangle 对象传递给 query() 方法,它运行良好,因为我可以接受“Shape”的任何子类。但是当我在方法中使用它时,它并没有保持它的“矩形”类型,而是变成了一个“形状”对象。

我想过为每个子类设置查询方法,但这样我就会一遍又一遍地使用相同的代码。

【问题讨论】:

  • object 仍然是 Rectangle,但 变量Shape
  • 好的,那么我怎样才能接受任何 Shape 子类型,但仍然保留它,因为它是在使用方法时传递的?
  • 我唯一能想到的就是将您的方法签名更改为:&lt;T extends Shape&gt; List&lt;Point&gt; query(T shape) - 但是,我觉得编译器仍然会抱怨类型不匹配。更好的选择可能是创建一个接受两个Shapes 的intersects 方法,确定它们在运行时是什么Shapes,然后从那里处理它们。
  • 尝试使用
  • @heraphim 不一定;一种选择是创建一个Map,它将子类映射到确定交集的方法。另一种方法是修改您的子类并让它们自己处理具有不同形状的交叉点。

标签: java class generics polymorphism


【解决方案1】:

多态性允许您为对象的实际类型调用适当的方法,而不管声明的引用类型如何。

双重分派允许您为两种对象类型调用适当的方法。

这是一个草图(未编译或测试):

public abstract class Shape {
    public abstract boolean intersects( Shape that );

    public abstract boolean intersects(Rectangle that);
    public abstract boolean intersects(Circle that);
}

public class Circle extends Shape {
    public boolean intersects( Shape that ) {
        return that.intersects( this );   // 'this' is of type Circle
    }

    public boolean intersects( Rectangle that ) { ... }
    public boolean intersects( Circle that ) { ... }
}

public class Rectangle extends Shape {
    public boolean intersects( Shape that ) {
        return that.intersects( this );   // 'this' is of type Rectangle
    }

    public boolean intersects( Rectangle that ) { ... }
    public boolean intersects( Circle that ) { ... }
}

您可以通过定义一对中的一个来调用另一个来避免冗余——或者让这两个方法调用一个公共实现。前者的一个例子是:

public class Rectangle {
    ...
    public boolean intersects( Circle that ) {
       return that.intersects( this ); 
    }

现在,假设您将一个圆形和一个矩形传递给下面的方法。

public boolean intersects( Shape shape1, Shape shape2 ) {
    return shape1.intersects( shape2 );
}

在编译时,编译器不知道会调用什么方法。但在运行时,Circle.intersects(Shape) 方法将被调用。它将调用Rectangle.intersects( Circle )

【讨论】:

    【解决方案2】:

    您可以将修改后的visitor pattern 用于嵌套访问者。为简洁起见,我将使用 2 个形状进行演示:

    abstract class Shape {
        abstract <V extends Visitor> V accept(V v);
    }
    
    class Circle extends Shape {
        @Override
        <V extends Visitor> V accept(V v) {
            v.visit(this);
            return v;
        }
    }
    
    class Rectangle extends Shape {
        @Override
        <V extends Visitor> V accept(V v) {
            v.visit(this);
            return v;
        }
    }
    
    interface Visitor {
        void visit(Circle c);
        void visit(Rectangle r);
    }
    
    static boolean intersects(Shape s1, Shape s2) {
        return s1.accept(new Visitor() {
            boolean intersects;
    
            @Override
            public void visit(Circle left) {
                s2.accept(new Visitor() {
                    @Override
                    public void visit(Circle right) {
                        intersects = circleIntersectsCircle(left, right);
                    }
    
                    @Override
                    public void visit(Rectangle right) {
                        intersects = circleIntersectsRect(left, right);
                    }
                });
            }
    
            @Override
            public void visit(Rectangle left) {
                s2.accept(new Visitor() {
                    @Override
                    public void visit(Circle right) {
                        intersects = circleIntersectsRect(right, left);
                    }
    
                    @Override
                    public void visit(Rectangle right) {
                        intersects = rectIntersectsRect(left, right);
                    }
                });
            }
        }).intersects;
    }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2021-04-12
      • 2012-06-18
      相关资源
      最近更新 更多