我想在一个ArrayList中保存Arc
和Line
的对象,然后获取两者的交集。问题是如何将i
和j
投射到其原始班级。我知道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)
}
}
感谢您的帮助!
答案 0 :(得分:2)
解决这种用例有两种方法:
<强> 1。实施Multiple dispatch :
首先使Curve
成为一个接口,并将intersect
的两个重载版本添加到此接口,从而使它们成为合同的一部分。接下来,让每个子类中的intersection(Curve c)
方法将调用委托给适当的重载表单。 (a.k.a Visitor pattern)
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);
}
}
其他内容:请参阅this替代方法来实施访客模式。
<强> 2。使线条和曲线遵循相同的界面(合同):
如果Line
和Arc
符合Curve
的界面,则您的代码将不再需要intersect
方法的重载版本。如果我们说Line
是Curve
而Arc
也是Curve
,则这两个类应该具有与{{>> 相同的接口 {1}}(通过界面我指的是他们支持的操作列表)。如果这些类与Curve
没有相同的接口,那么这就是问题所在的地方。 Curve
中提供的方法应该是Curve
和Line
类应该要求的唯一方法。
有几种策略可以消除子类中超类中不存在方法的需要:
一旦消除了在超类中不存在的子类中使用专门方法的需要,您的代码就会自动消除对Arc
或类型检查的需要。这与Liskov substitution principle一致。
答案 1 :(得分:1)
另一种方法是使用类isAssignableFrom
的{{1}}方法。以下是一个例子:
Class
Here's 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,这就是它所说的:
确定此Class对象所表示的类或接口 与...相同或者是超类或超级接口 由指定的Class参数表示的类或接口。它 如果是,则返回true;否则返回false。如果是这个Class对象 表示基本类型,如果指定,此方法返回true Class参数正是这个Class对象;否则它会返回 假的。
答案 2 :(得分:1)
由于每个子类都必须知道其他子类(例如,Arc
必须知道Line
类才能实现Arc
和{{ 1}}交集),使用Line
没有错。
在每个子类中,您可以覆盖基类的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)
}
}
方法中的任何内容。
答案 3 :(得分:0)
首先考虑:您是否需要将i
和j
从Curve
转换为Arc
或Line
?
请看一下,例如:
What's the need to use Upcasting in java?
如果你确定你确实需要向上播种,不幸的是没有神奇的蛋 - 你无法避免使用instanceof
来决定将该课改为上课。
你可以将责任委托给另一个班级,但基本上你无法避免。
抱歉!
答案 4 :(得分: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
}
}
答案 5 :(得分:0)
好的,我发现一个解决方案是在Curve
中使用抽象方法,在子类中使用if-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)
}
}
答案 6 :(得分: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[] {};
}
}