I want to save Objects of Arc
and Line
in one ArrayList, then get the intersection of both. The question is how can I cast i
and j
to its original class. I know that instanceof
works but that would be the dirtiest method.
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)
}
}
Thanks for your help!
Runtime polymorphism, also known as the Dynamic Method Dispatch, is a process that resolves a call to an overridden method at runtime. The process involves the use of the reference variable of a superclass to call for an overridden method.
Dynamic method dispatch is the mechanism by which a call to an overridden method is resolved at run time, rather than compile time.
Polymorphism in Java has two types: Runtime polymorphism (dynamic binding) and Compile time polymorphism (static binding). Method overriding is an example of dynamic polymorphism, while method overloading is an example of static polymorphism.
method overriding is an example of run time/dynamic polymorphism because method binding between method call and method definition happens at run time and it depends on the object of the class (object created at runtime and goes to the heap).
There are two approaches to tackling such a use-case :
1. Implement Multiple dispatch :
Start by making Curve
an interface and add the two overloaded versions of intersect
to this interface, thus making them a part of the contract. Next, have the intersection(Curve c)
method in each of the sub-classes delegate the call to the appropriate overloaded form. (a.k.a The 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];
}
}
You can then call your intersection
method in the Intersection
class without needing any explicit casts :
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 : Take a look at this alternate approach to implementing the Visitor pattern.
2. Make line and curve adhere to the same interface (contract) :
If Line
and Arc
adhere to the interface of Curve
, your code will no longer need the overloaded versions of the intersect
method. If we say that a Line
is a Curve
and an Arc
is also a Curve
, both these classes should have the same interface as Curve
(by interface I mean the list of operations they support). If these classes don't have the same interface as Curve
, this is where the problem area lies. The methods present in Curve
should be the only methods that should be required by the Line
and Arc
classes.
There are several strategies to eliminate the need for sub-classes to have methods not present in the superclass :
Once you eliminate the need to have specialized methods in the subclass not present in the superclass, your code automatically eliminates the need to have instanceof
or type checks. This is in-line with the Liskov substitution principle.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With