0

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?

gbag
  • 101
  • 4
  • 1
    In any case, don't switch on the class name. What if you have a `MountainBicycle` which is a subclass of `Bicycle`? – RealSkeptic Nov 01 '15 at 13:17
  • You're right! I didn't think of this. Well then, this would become a lot of `if` and `else if` statements of the form `if(Bicycle.class.isAssignableFrom(v.getClass()))`. – gbag Nov 01 '15 at 13:23
  • 1
    You mean `if v instanceof Bicycle`... – RealSkeptic Nov 01 '15 at 13:26
  • Ah, of course! I initially thought subclasses would not be part of `instanceof`. – gbag Nov 01 '15 at 13:32
  • Anyway, regarding your actual question, I doubt you'll be able to get a good expert answer, because usually solutions for such situations depend on the actual, practical use case, not "toy classes" like `Vehicle`,`Car`,`Bicycle`, or toy methods that do nothing but print. If you post a real-world problem it would be easier to answer (though even then it could just end up as "opinion-based"). – RealSkeptic Nov 01 '15 at 13:36
  • (1) What's your actual purpose for doing this? (2) Maybe you should just use Groovy in dynamic mode. – chrylis -cautiouslyoptimistic- Nov 01 '15 at 13:41
  • I don't think *curried* and *uncurried* are appropriate terms, here. – jub0bs Nov 27 '15 at 22:31

1 Answers1

0

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.

gbag
  • 101
  • 4