Getting the distance between various classes of shapes
Quick Comments

Your Distance Interface is weird. It does not represent a distance, but an object having the ability to provide the distance between it, and some other object sharing the same interface.

The Shape interface is weird. The distance(int) and distance(int, int) are hacky and should go. They rely on implementation details of Point and Line.

The Shape should be an interface. It is currently a void Abstract class with no added value, it just prevents proper inheritance.

It looks weird to me to return ints for distances, given the propension of most metrics to return doubles due to frequent square roots being applied. However, I don't know your metrics so I'll stick with it.



Implementation using double-dispatch

If the number of classes is rather low, you could implement double-dispatch. Make the Shape interface have a distance(Shape) and every distanceTo(Foo), distanceTo(Bar), distanceTo(Fuzz) etc. like so:

public interface Shape {
    int distance(Shape shape);
    int distanceTo(Foo foo);
    int distanceTo(Bar bar);
    int distanceTo(Fuzz fuzz);
}


This way all Shape implementations will look like the following:

public class Foo implements Shape {
    public int distanceTo(Shape shape) {
        // You can copy-paste this method body on all other classes
        return shape.distanceTo(this); // This will call the strongly-typed methods below
    }
    public int distanceTo(Foo foo){
        // Specific implementation
    }
    public int distanceTo(Bar bar){
        // Specific implementation
    }
    public int distanceTo(Fuzz fuzz){
        // Specific implementation
    }
}


Here's how to use it:

Foo foo = new Foo();
Bar bar = new Bar();
int foobar = foo.distance(bar);
int foofoo = foo.distance(foo);


Very simple with just 3 classes. Can become a nightmare with 10+ classes.

You have to realize, that for every pair of Shapes, you'll need a dedicated distance method implementation. This means the number of distance(T t, U u) methods will grow rapidly.

From here on I will assume your network of Shape objects is sufficiently complex to warrant a dedicated structure to handle this in an externalized way.



The Problem

Basically, this boils down to the following:


  Given miscelaneous Objects, how to make it possible for any pair of Objects to interact (the interaction depends on the nature &amp; implementation of those objects, and is not known in advance) and return something.


Obviously the interaction between two entities cannot be entirely contained in the entities themselves. 

If it was, each Entity would have to know its distance to any other entity type (including unknown &amp; unforeseen future Shape implementations), which means it would know every other Entity existence, and implementation details, and would have a dedicated distance implementation for each. That would:


Break Encapsulation - one Entity would have to know the detailed implementation of the other to perform the calculation)
Break extensibility - Adding an Foo entity would require adding a distance(Foo)method to every other Object


All those sins were commited in the double-dispatch implementation above.
So now we know the interaction implementation has to be externalized.



How to build an extensible externalized interaction function

Let's have a centralized distance functor (I'll make it a Singleton using an Enum, I assume you only have one set of interacting Shape implementations):

public enum DistanceEvaluator {
    INSTANCE;
    public final int distance(Shape shape1, Shape shape2){
         // Do something magic here
    }
}


Is this usable enough? I think you could very easily add the following default method (thanks, Java 8) to your Shape Interface:

public interface Shape {
    default int distance (Shape other){
        return DistanceEvaluator.INSTANCE.distance(this, other);
    }
}


Now aShape can calculate its distance to an other... provided the DistanceEvaluator holds an implementation for that particular pair of classes.



Enriching the DistanceEvaluator with implementations

A) You could implement everything in the DistanceEvaluator.

Then you just distribute the implementation using ìnstanceof and casting.
Advantages: The class combines all implementations, so can make good reuse of helper intermediate functions.
Disadvantage: the class could become enormous. 

This should be used if the number of pairs is rather small with synergies (points, lines, segments, polygons).

B) You could use instanceofand cast the first argument (of type Foo)

Then simply forward the second argument (type Bar) to a FooDistanceCalculator class that knows how to compute distance to any Bar class.
Advantage: Implementations are grouped in a package, yet distributed between several classes.
Disadvantage: how to tell if distance(Foo, Bar) or distance(Bar, Foo) exists?

This should be used if the number of pairs becomes important and requires a (simple) structure.

C) You could make a completely dynamic mapping of distance functions.

Such mapping could be enriched at runtime, as a bonus.

Use the following Evaluator :

import java.util.HashMap;
import java.util.Map;
import java.util.function.BiFunction;

public enum DistanceEvaluator {

    INSTANCE;

    /** Holds distance implementations */
    private final Map&lt;Class&lt;? extends  Shape&gt;, Map&lt;Class&lt;? extends Shape&gt;, BiFunction&lt;? extends Shape, ? extends Shape, Integer&gt;&gt;&gt; MAP = new HashMap&lt;&gt;();

    /** When a class has no matching map of distance functions */
    private final Map&lt;Class&lt;? extends Shape&gt;, BiFunction&lt;? extends  Shape, ? extends  Shape, Integer&gt;&gt; MISSING = new HashMap&lt;&gt;();

    /** When a pair has no matching distance function */
    private final BiFunction&lt;? extends  Shape, ? extends  Shape, Integer&gt; NO_FUNCTION = new BiFunction&lt;Shape,Shape, Integer&gt;() {
        @Override
        public Integer apply(Shape t, Shape u) {
            throw new IllegalArgumentException("Shapes " + t + " and " + u + " cannot calculate their respective distance");
        }
    };


    /**
     * Computes the distance between any pair of Shapes.
     * @param shape1 a shape
     * @param shape2 another shape
     * @throws IllegalArgumentException if the pair has no matching distance implementation
     * @return the distance between the two shapes
     */
    public &lt;T extends Shape, U extends Shape&gt; int distance(T shape1, U shape2) {
        @SuppressWarnings("unchecked")
        Class&lt;T&gt; shape1class = (Class&lt;T&gt;) shape1.getClass();
        @SuppressWarnings("unchecked")
        Class&lt;U&gt; shape2class = (Class&lt;U&gt;) shape2.getClass();
        @SuppressWarnings("unchecked")
        BiFunction&lt;T,  U, Integer&gt; func = (BiFunction&lt;T, U, Integer&gt;) MAP.getOrDefault(shape1class, MISSING).getOrDefault(shape2class, NO_FUNCTION);
        return func.apply(shape1, shape2);
    }

    /** Register a distance function for a pair of {@link Shape} sub-classes. 
     * Note the function will be registered for both class pairs (a, b) and (b, a)
     * @param class1 first {@link Shape} Class
     * @param class2 second {@link Shape} Class
     * @param biFunction distance {@link BiFunction}
     */
    public &lt;T extends Shape, U extends Shape&gt; void register(Class&lt;T&gt; class1, Class&lt;U&gt; class2, BiFunction&lt;T, U, Integer&gt; biFunction) {
        Map&lt;Class&lt;? extends Shape&gt;, BiFunction&lt;? extends Shape, ? extends Shape, Integer&gt;&gt; intermediate1 = MAP.get(class1);
        if(intermediate1 == null){
            intermediate1 = new HashMap&lt;&gt;();
            MAP.put(class1, intermediate1);
        }
        intermediate1.put(class2, biFunction);

        // Symmetric
        Map&lt;Class&lt;? extends Shape&gt;, BiFunction&lt;? extends Shape, ? extends Shape, Integer&gt;&gt; intermediate2 = MAP.get(class2);
        if(intermediate2 == null){
            intermediate2 = new HashMap&lt;&gt;();
            MAP.put(class2, intermediate2);
        }
        intermediate2.put(class1, biFunction);
    }
}


Here are a few Shape implementations. Note those implementations inject their required distance evaluators when their classes are loaded.

A Point class:

public class Point implements Shape{
    static {
        DistanceEvaluator.INSTANCE.register(Point.class, Point.class, new BiFunction&lt;Point, Point, Integer&gt;() {
            @Override
            public Integer apply(Point t, Point u) {
                return 0; // Calculations here
            }
        });
        DistanceEvaluator.INSTANCE.register(Point.class, Line.class, new BiFunction&lt;Point, Line, Integer&gt;() {
            @Override
            public Integer apply(Point t, Line u) {
                return 0; // Calculations here
            }
        });
    }
    // Class implementation details, up to you
}


A Line class:

public class Line implements Shape {
    static {
        DistanceEvaluator.INSTANCE.register(Line.class, Line.class, new BiFunction&lt;Line, Line, Integer&gt;() {
            @Override
            public Integer apply(Line t, Line u) {
                return 0; // Calculations here
            }
        });
    }
    // Class implementation details, up to you
}


With this scheme, it becomes possible to not implement all distance functions. This might or might not be a good feature for you.