5

我想将Arc和的对象保存Line在一个 ArrayList 中,然后得到两者的交集。问题是我如何才能投射ij它的原始类。我知道这instanceof行得通,但那将是最肮脏的方法。

public class Intersection {
    public static boolean intersect(ArrayList<Curve> list1, ArrayList<Curve> list2) {
        for (Curve i : list1) {
            for (Curve j : list2) {
                if (i.intersection(j).length > 0) 
                    return true;
            }
        }
        return false;
    }
}

public abstract class Curve {
    public Point[] intersection(Curve c) {
        return new Point[] {};
    }
}

public class Line extends Curve {
    public Point[] intersection(Line l) {
        // returns intersection Point of this and l
    }

    public Point[] intersection(Arc a) {
        // returns intersection Point(s)
    }
}

public class Arc extends Curve {
    public Point[] intersection(Line l) {
        // return intersection Point(s) of this and l
    }

    public Point[] intersection(Arc a) {
        // returns intersection Point(s)
    }
}

谢谢你的帮助!

4

7 回答 7

2

有两种方法可以处理这种用例:


1.实现多分派

首先创建Curve一个接口并将两个重载版本添加intersect到该接口,从而使它们成为合同的一部分。接下来,让intersection(Curve c)每个子类中的方法将调用委托给适当的重载表单。(又名访客模式

interface class Curve {
    public Point[] intersection(Curve c);

    public Point[] intersection(Line l);

    public Point[] intersection(Arc c);

}

class Line extends Curve {
    
    public Point[] intersection(Curve c) {
        return c.intersection(this);
    }
    
    @Override
    public Point[] intersection(Line l) {
        System.out.println("line interesection with line");
        return new Point[0];
    }

    @Override
    public Point[] intersection(Arc c) {
        System.out.println("line intersection with arc");
        return new Point[0];
    }

}

class Arc extends Curve {
    
    public Point[] intersection(Curve c) {
        return c.intersection(this);
    }
    @Override
    public Point[] intersection(Line l) {
        System.out.println("arc interesection with line");
        return new Point[0];
    }

    @Override
    public Point[] intersection(Arc c) {
        System.out.println("arc interesection with arc");
        return new Point[0];
    }
}

然后,您可以在类中调用您的intersection方法,Intersection而无需任何显式强制转换:

public class Intersection {
    public static boolean intersect(ArrayList<Curve> list1,
            ArrayList<Curve> list2) {
        for (Curve i : list1) {
            for (Curve j : list2) {
                if (i.intersection(j).length > 0)
                    return true;
            }
        }
        return false;
    }
    
    public static void main(String[] args) {
        Curve line1 = new Line();
        Curve arc1 = new Arc();
        Curve line2 = new Line();
        Curve arc2 = new Arc();
        
        ArrayList<Curve> list1 = new ArrayList<>();
        ArrayList<Curve> list2 = new ArrayList<>();
        list1.add(line1);
        list1.add(arc1);
        list2.add(line2);
        list2.add(arc2);
        
        Intersection.intersect(list1, list2);
    
    }
}

Extras:看看这种实现访问者模式的替代方法。


2. 使直线和曲线遵守同一个界面(契约)

如果LineArc坚持 的接口Curve,您的代码将不再需要该intersect方法的重载版本。如果我们说 a Line是 aCurve并且 anArc也是 a Curve,那么这两个类都应该具有相同的接口(我所说Curve的接口是指它们支持的操作列表)。如果这些类没有与 相同的接口Curve,这就是问题所在。中存在的方法应该是和类Curve应该需要的唯一方法。LineArc

有几种策略可以消除子类具有超类中不存在的方法的需要:

  • 如果与超类相比,子类需要额外的输入,请通过构造函数提供这些输入,而不是创建对这些输入进行操作的单独方法。
  • 如果子类需要超类不支持的其他行为,请通过组合(阅读策略模式)支持此行为,而不是添加方法来支持其他行为。

一旦您消除了在子类中使用超类中不存在的专用方法的需要,您的代码就会自动消除对进行instanceof检查或类型检查的需要。这符合Liskov 替换原则


于 2017-10-31T16:10:06.660 回答
1

另一种isAssignableFrom方法是使用 Class 的方法Class。下面是一个例子:

Exception e = new Exception();
RuntimeException rte = new RuntimeException();
System.out.println(e.getClass().isAssignableFrom(RuntimeException.class));
System.out.println(rte.getClass().isAssignableFrom(Exception.class));
System.out.println(rte.getClass().isAssignableFrom(RuntimeException.class));

这是方法的javadoc,isAssignableFrom这就是它所说的:

确定此 Class 对象表示的类或接口是否与指定的 Class 参数表示的类或接口相同,或者是其超类或超接口。如果是,则返回 true;否则返回false。如果此 Class 对象表示原始类型,则如果指定的 Class 参数正是此 Class 对象,则此方法返回 true;否则返回false。

于 2017-10-31T14:55:45.160 回答
1

由于每个子类都必须知道其他子类(例如,Arc必须知道Line该类才能实现ArcLine交集),所以使用instanceof.

在每个子类中,您可以覆盖基类的public Point[] intersection(Curve c)方法并将实现分派给重载方法之一。

例如:

public class Arc extends Curve {    
    @Override
    public Point[] intersection(Curve c) {
        if (c instanceof Line)
            return instersection ((Line) c);
        else if (c instanceof Arc)
            return intersection ((Arc) c);
        else
            return an empty array or null, or throw some exception
    }

    public Point[] intersection(Line l) {
        // return intersection Point(s) of this and l
    }

    public Point[] intersection(Arc a) {
        // returns intersection Point(s)
    }
}

这样您就不必更改public static boolean intersect(ArrayList<Curve> list1, ArrayList<Curve> list2)方法中的任何内容。

于 2017-10-31T15:07:21.370 回答
0

首先考虑:您是否需要转换(向上转换)ijfrom Curveto Arcor Line

例如,请看这里:

在java中使用Upcasting有什么需要?

如果您决定确实需要向上转换,不幸的是没有神奇的彩蛋 - 您无法避免使用instanceof来决定向上转换的类。

您可以将责任委托给另一个班级,但基本上您无法避免。

对不起!

于 2017-10-31T14:53:33.650 回答
0

换个Curve界面。保持ArrayList<Curve>不变,而是将您的intersection方法提取到一个单独的类中,并让它在Curves.

您需要在instanceof那里使用检查,但由于使用继承,您的设计会更简洁一些。

public interface Curve {
...
}

public class Line extends Curve {
...
}

public class Arc extends Curve {
...
}

public class IntersectionUtility {
    public static boolean intersects(ArrayList<Curve> list1, ArrayList<Curve> list2) {
        for (Curve i : list1) {
            for (Curve j : list2) {
                if (i.intersection(j).length > 0) 
                    return true;
            }
        }
        return false;
    }

   public Point[] intersection(Curve a, Curve b) {
      if (a.instanceof(Line.class)) {
        if (b.instanceof(Line.class)) {
           return findIntersection((Line) a, (Line) b); // two Lines
        } else {
           return findIntersection((Line) a, (Arc) b); // a Line and an Arc
        }
      } else {
        if (b.instanceof(Line.class)) {
           return findIntersection((Line) b, (Arc) a); // a Line and an Arc
        } else {
           return findIntersection((Arc) a, (Arc) b); // two Arcs
        }
      }
   }

    public Point[] findIntersection(Line a, Line b) {
        // returns intersection Point of two Lines
    }

    public Point[] findIntersection(Arc a, Arc b) {
        // returns intersection Point(s) of two Arcs
    }

    public Point[] findIntersection(Line a, Arc b) {
        // returns intersection Point(s) of an Line and an Arc
    }
}
于 2017-10-31T15:10:56.877 回答
0

好的,所以我发现的一个解决方案是在子类中使用抽象方法Curveif-else链。但是我对这个解决方案并不满意。

public abstract class Curve {
    public abstract Point[] intersection(Curve c);
}

public class Line extends Curve {
    public Point[] intersection(Curve c) {
        if (c instanceof Line) {
            return this.intersection((Line) c);
        } else if (c instanceof Arc) {
            return this.intersection((Arc) c);
        }
    }

    private Point[] intersection(Line l) {
        // returns intersection Point of this and l
    }

    private Point[] intersection(Arc a) {
        // returns intersection Point(s)
    }
}
于 2017-10-31T15:11:07.090 回答
0

如果您不想使用,instanceof那么替代方法是使用组合来获取类型。以下方法不会使用instanceof,只会使用首选Class.cast操作:

public static class Intersection {

        public static boolean intersect(ArrayList<Curve> list1, ArrayList<Curve> list2) {            
            for (Curve i : list1) {                
                Optional<Line> il = i.get(Line.class);
                Optional<Arc> ia = i.get(Arc.class);

                for (Curve j : list2) {
                    Optional<Line> jl = j.get(Line.class);
                    Optional<Arc> ja = j.get(Arc.class);

                    Point[] intersection = null;

                    if ( il.isPresent() ){
                        if ( jl.isPresent() ) intersection = il.get().intersection( jl.get() );
                        else if ( ja.isPresent() ) intersection = il.get().intersection( ja.get() );
                    }else if ( ia.isPresent() ){
                        if ( jl.isPresent() ) intersection = ia.get().intersection( jl.get() );
                        else if ( ja.isPresent() ) intersection = ia.get().intersection( ja.get() );
                    }    

                    if ( intersection != null && intersection.length > 0 ) return true;
                }
            }
            return false;
        }
    }

    public static abstract class Curve {

        public abstract <T extends Curve> Optional<T> get(Class<T> clazz);

    }

    public static class Line extends Curve {

        public <T extends Curve> Optional<T> get(Class<T> clazz){
            return clazz.equals(Line.class) ? Optional.of( clazz.cast(this) ) : Optional.empty();
        }

        public Point[] intersection(Line l) {
            return new Point[] {};
        }

        public Point[] intersection(Arc a) {
            return new Point[] {};
        }
    }

    public static class Arc extends Curve {

        public <T extends Curve> Optional<T> get(Class<T> clazz){
            return clazz.equals(Arc.class) ? Optional.of( clazz.cast(this) ) : Optional.empty();
        }

        public Point[] intersection(Line l) {
            return new Point[] {};
        }

        public Point[] intersection(Arc a) {
            return new Point[] {};
        }
    } 
于 2017-10-31T18:10:45.243 回答