Search code examples
javavisitor-patterndouble-dispatch

Double Dispatch when having a method that accepts Object class


I have an Implementation of a Mapper class which accepts Object as parameter in one of the map(Object object) functions. The rest map(T t) functions accept Integer or Class etc.

When I try to pass an int it gets Autoboxed into Integer and invokes the map(Object object) instead of the map(Integer integer).

I have done some research for Double Dispatch and saw that I could use the Visitor Pattern. But this does not apply in my case because I do not pass custom Objects that I could have them implement an interface with accept().

The aforementioned method accepts every object.

Is there any kind of solution for Double Dispatching with Java objects when you have a method that accepts Object as well?

public BaseMatcher map(Object object) {
        return something();
    }

    public BaseMatcher map(Integer integer) {
        return somethingOther();
    }

    public BaseMatcher map(Class<? extends Exception> klass) {
        return somethingOtherOther();
    }

The call on these map() functions, would be as follows: foo(Object object) { mapper.map(object); } which leads to map(Object object) being invoked.


Solution

  • All the compiler knows about your objects is that they're objects, i.e. instance of Object. So map(Object) is being called.

    If your map method needs to do something different based on the type of the object being passed, then it should get the concrete type of the object, and act accordingly (using instanceof, getClass(), or whataver).

    The alternative is indeed to use polymorphism. But to do that, the caller will have to provide a collection of Mappable, instead of just a collection of objects:

    private interface Mappable {
        BaseMatcher mapMe(Mapper mapper);
    }
    
    public class IntegerMappable {
        private final Integer value;
    
        public IntegerMappable(Integer value) {
            this.value = value;
        }
    
        @Override
        public BaseMatcher mapMe(Mapper mapper) {
            return mapper.map(this.value);
        }
    }
    

    And when you want to map objects, you wrap them into the appropriate Mappable (or use a lambda):

    List<Mappable> mappables = new ArrayList<>();
    mappables.add(new IntegerMappable(2));
    mappables.add(new StringMappable("hello");
    mappables.add(mapper -> mapper.map(42));