javapolymorphismoverloadingdouble-dispatchadhoc-polymorphism

Polymorphic uncurried method calls (adhoc polymorphism) in Java


Let me start with an example.

Say I have an abstract Vehicle class.

public abstract class Vehicle {

    public Vehicle() {}

    public abstract void ride();

}

And classes Car and Bicycle that inherit from this abstract class.

public class Car extends Vehicle {

    public Car() {}

    @Override
    public void ride() {
        System.out.println("Riding the car.");
    }

}

public class Bicycle extends Vehicle {

    public Bicycle() {}

    @Override
    public void ride() {
        System.out.println("Riding the bicycle.");
    }

}

When I apply the ride() method to an object of type Vehicle whose actual type can only be determined at runtime, the JVM will apply the correct version of ride().

That is, in a curried method call of the sort v.ride(), polymorphism works the expected way.

But what if I have an external implementation in form of a method that only accepts a subtype of Vehicle as an argument? So, what if I have repair(Bicycle b) and repair(Car c) methods? The uncurried polymorphic method call repair(v) won't work.

Example:

import java.util.ArrayList;
import java.util.List;

public class Main {

private static void playWithVehicle() {
    List<Vehicle> garage = new ArrayList<Vehicle>();
    garage.add(new Car());
    garage.add(new Car());
    garage.add(new Bicycle());
    garage.forEach((v) -> v.ride()); // Works.
    garage.forEach((v) -> {
        /* This would be nice to have.
        repair(v.castToRuntimeType());
        */

        // This is an ugly solution, but the obvious way I can think of.
        switch (v.getClass().getName()) {
        case "Bicycle":
            repair((Bicycle) v);
            break;
        case "Car":
            repair((Car) v);
            break;

        default:
            break;
        }
    });
}

private static void repair(Bicycle b) {
    System.out.println("Repairing the bicycle.");
}

private static void repair(Car c) {
    System.out.println("Repairing the car.");
}

public static void main(String[] args) {
    playWithVehicle();
}

}

I have to check for the class name and downcast. Is there a better solution to this?


Edit: My actual purpose is that I'm traversing an abstract syntax tree and I happened to notice that I want double dispatch.

Ast is an abstract class from which actual AST nodes like Assign, MethodCall, or ReturnStmt inherit. body is a polymorphic list of Asts.

Code snippet:

List<Ast> body;

body.parallelStream().forEach((ast) -> {
    // This one won't work.
    visit(ast);
    // This one will work.
    if (ast instanceof Assign) {
        visit((Assign) ast);
    } else if (ast instance of MethodCall) {
        visit((MethodCall) ast);
    } else if (ast instance of ReturnStmt) {
        visit((ReturnStmt) ast);
    }
    // etc. for other AST nodes
});

private void visit(Assign ast) {

}

private void visit(MethodCall ast) {

}

private void visit(ReturnStmt ast) {

}

My only possibilities of achieving double dispatch is either checking the class and downcasting or properly implementing the visitor pattern, right?


Solution

  • Answer: There is no multiple dispatch in Java and it can be simulated by instanceof or by the visitor pattern.

    See here: Java method overloading + double dispatch

    See also here: https://en.wikipedia.org/wiki/Multiple_dispatch#Examples_of_emulating_multiple_dispatch

    On a sidenote, exactly this is possible in C# with dynamic calls: How to build double dispatch using extensions

    And this is also possible in the many languages that are compiled to JVM bytecode, e.g. Groovy was mentioned.