动态调度(运行时多态性),带有重载方法,不使用instanceof

c90pui9n  于 2021-07-09  发布在  Java
关注(0)|答案(7)|浏览(330)

我想保存 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)
    }
}

谢谢你的帮助!

tvokkenx

tvokkenx1#

第一个考虑:你需要转换(向上投射) i 以及 jCurveArc 或者 Line ?
请看一下,例如:
在java中使用向上转换有什么必要?
如果你决定你真的需要向上投,不幸的是没有魔法蛋-你不能避免使用 instanceof 决定向上倾斜的班级。
你可以把责任委托给另一个类,但基本上你不能回避它。
对不起的!

0aydgbwb

0aydgbwb2#

改变 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
    }
}
ejk8hzay

ejk8hzay3#

如果你不想使用 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[] {};
        }
    }
bwleehnv

bwleehnv4#

我发现的一个解决方案是在 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)
    }
}
khbbv19g

khbbv19g5#

因为每个子类都必须知道其他子类(例如, 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) 方法。

laximzn5

laximzn56#

处理这种用例有两种方法:
1.实施多次调度:
从制造开始 Curve 一个接口,并添加 intersect 从而使它们成为合同的一部分。下一步,让 intersection(Curve c) 方法将调用委托给适当的重载形式(a、 k.a.访客模式)

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);

    }
}

附加:看看这个实现访问者模式的替代方法。
2.使直线和曲线符合同一界面(合同):
如果 Line 以及 Arc 坚持 Curve ,您的代码将不再需要 intersect 方法。如果我们这么说 Line 是一个 Curve 和一个 Arc 也是一个 Curve ,这两个类的接口应与 Curve (我所说的接口是指它们支持的操作列表)。如果这些类与 Curve ,这就是问题所在。本文介绍的方法 Curve 应该是 Line 以及 Arc 班级。
有几种策略可以消除子类在超类中不存在方法的需要:
如果与超类相比,子类需要额外的输入,则通过构造函数提供这些输入,而不是创建对这些输入进行操作的单独方法。
如果子类需要超类不支持的其他行为,请通过组合(读取策略模式)来支持此行为,而不是添加方法来支持其他行为。
一旦消除了在超类中不存在的子类中使用专用方法的需要,代码就会自动消除使用专用方法的需要 instanceof 或类型检查。这符合李斯科夫替代原理。

yizd12fk

yizd12fk7#

另一种选择是 isAssignableFrom 分类方法 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 方法,它是这么说的:
确定此类对象表示的类或接口是否与指定的类参数表示的类或接口相同,或是其超类或超接口。如果是,则返回true;否则返回false。如果这个类对象表示一个基元类型,如果指定的类参数就是这个类对象,那么这个方法返回true;否则返回false。

相关问题