Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Dynamic dispatch (runtime-polymorphism) with overloaded methods without using instanceof

Tags:

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!

like image 996
McTi Avatar asked Oct 31 '17 14:10

McTi


People also ask

What is runtime polymorphism or dynamic method dispatch?

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.

What is dynamic method dispatch show with an example?

Dynamic method dispatch is the mechanism by which a call to an overridden method is resolved at run time, rather than compile time.

What type of polymorphism is method overloading?

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.

Why method overriding is runtime 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).


1 Answers

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 :

  • If a subclass requires additional inputs compared to the superclass, provide these inputs via the constructor rather than creating seperate methods that operate on these inputs.
  • If a subclass requires additional behavior not supported by the superclass, support this behavior via composition (read Strategy pattern) rather than adding methods to support additional behavior.

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.


like image 112
Chetan Kinger Avatar answered Sep 20 '22 12:09

Chetan Kinger