Search code examples
javaguiceguice-3

How to make Guice MapBinder really typesafe?


Here is how my StatusMapper interface looks like:

public interface StatusMapper<T extends Throwable> {
    Status map(final T exception);
}

And here is my MapBinder:

    TypeLiteral<Class<? extends Throwable>> exceptionType = new TypeLiteral<Class<? extends Throwable>>() { };
    TypeLiteral<StatusMapper<? extends Throwable>> mapperType = new TypeLiteral<StatusMapper<? extends Throwable>>() { };

    MapBinder<Class<? extends Throwable>, StatusMapper<? extends Throwable>> exceptionBinder = MapBinder.newMapBinder(binder(), exceptionType, mapperType);

    exceptionBinder.addBinding(IOException.class).to(IOExceptionMapper.class);
    exceptionBinder.addBinding(SQLException.class).to(SQLExceptionMapper.class);
    ...

This is how one of these ExceptionMappers looks like: (simplified)

public class IOExceptionMapper implements StatusMapper<IOException> {
    @SuppressWarnings("unused")
    private static final Logger logger = LoggerFactory.getLogger(IOExceptionMapper.class);

    @Override
    public Status map(final IOException exception) {
        return new Status(100);
    }
}

This works fine so far but I have to take care that IOException binds to IOExceptionMapper. If I bind exceptionBinder.addBinding(IOException.class).to(SQLExceptionMapper.class); the typechecker (compiler) does not complain but it breaks the whole app - any hints?

[Update] According to The111's answer I created ExceptionBinder

public class ExceptionBinder {
    private final MapBinder<Class<? extends Throwable>, StatusMapper<? extends Throwable>> exceptionBinder;

    public ExceptionBinder(final Binder binder) {
        final TypeLiteral<Class<? extends Throwable>> exceptionType;
        final TypeLiteral<StatusMapper<? extends Throwable>> mapperType;

        exceptionType = new TypeLiteral<Class<? extends Throwable>>() {};
        mapperType = new TypeLiteral<StatusMapper<? extends Throwable>>() {};

        exceptionBinder = MapBinder.newMapBinder(binder, exceptionType, mapperType);
    }

    public <T extends Throwable> void bind(Class<T> exceptionClass, Class<? extends StatusMapper<T>> mapperClass) {
        exceptionBinder.addBinding(exceptionClass).to(mapperClass);
    }
}

And this is how my Guice-Module looks like:

final ExceptionBinder eb = new ExceptionBinder(binder());
eb.bind(IOException.class,IOExceptionMapper.class);
eb.bind(SQLException.class,SQLExceptionMapper.class);

Solution

  • Your question seems possibly related to this one: Java map with values limited by key's type parameter

    What if you wrapped the Guice MapBinder in your own TypeSafeMapBinder and gave that class a method like:

    void addToBinder(Class<T extends Throwable> eClass,
                     Class<? extends StatusMapper<T>> mClass) {
        getWrappedBinder().addBinding(eClass, mClass);
    }
    

    I haven't tested this so please let me know your results.