我想把Arc
和Line
的对象保存在一个ArrayList中,然后得到两者的交集。问题是如何将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)
}
}
谢谢你的帮助!
有两种方法可以处理这样的用例:
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:看看这个实现Visitor模式的替代方法。
2.使直线和曲线遵守同一界面(合同):
如果Line
和Arc
坚持Curve
的接口,那么您的代码将不再需要intersect
方法的重载版本。如果我们说Line
是Curve
,Arc
也是Curve
,那么这两个类都应该具有与Curve
相同的接口(我所说的接口是指它们支持的操作列表)。如果这些类没有与Curve
相同的接口,那么这就是问题所在。Curve
中存在的方法应该是Line
和Arc
类所需的唯一方法。
有几种策略可以消除子类具有超类中不存在的方法的需要:
- 如果与超类相比,子类需要额外的输入,请通过构造函数提供这些输入,而不是创建单独的方法来操作这些输入
- 如果子类需要超类不支持的额外行为,请通过组合(读取策略模式)来支持此行为,而不是添加方法来支持额外行为
一旦您消除了在子类中使用超类中不存在的专用方法的需要,您的代码就会自动消除instanceof
或类型检查的需要。这符合利斯科夫替代原理。
由于每个子类都必须知道其他子类(例如,Arc
必须知道Line
类才能实现Arc
和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)
}
}
这样,您就不必更改public static boolean intersect(ArrayList<Curve> list1, ArrayList<Curve> list2)
方法中的任何内容。
另一种选择是使用Class
类的isAssignableFrom
方法。下面是一个例子:
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));
这是isAssignableFrom
方法的javadoc,它是这样说的:
确定该class对象表示的类或接口与类或由指定的class参数表示的接口。它如果是,则返回true;否则返回false。如果此Class对象表示基元类型,如果指定Class参数正是此Class对象;否则返回false。
首先要考虑的是:是否需要将i
和j
从Curve
转换为Arc
或Line
?
例如,请看一下这里:
什么';是否需要在java中使用Upcasting?
如果你决定你真的需要升级,不幸的是没有魔法蛋——你无法避免使用instanceof
来决定要升级到的类。
你可以将责任委托给另一个类,但基本上你无法避免
对不起!
好的,所以我发现的一个解决方案是在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)
}
}
将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
}
}
如果您不想使用instanceof
,那么另一种选择是使用composition来获取类型。以下方法不会使用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[] {};
}
}