Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Generics friendly Type Handler map

I'm trying to make a "registry" of handlers that return a specific type

public class HandlerRegistry {
   Map<Class<?>, Handler<?>> handlers;

   <T> void setHandler(Class<T> type, Handler<? extends T> handler) {
      handlers.put(type, handler);
   }

   <T> T handle(Class<T> type, HandlerArgument arg) {
      Handler<? extends T> handler = getHandler(type);

      return handler.handle(arg);
   } 

   <T> Handler<? extends T> getHandler(Class<T> type) {
      // warning produced here "uses unchecked or unsafe operations."
      return (Handler<? extends T>)handlers.get(type);
   }
}

I know that this particular cast will never fail at runtime, but other than using @SuppressWarnings("unchecked") is there a way to tell the compiler that this is indeed safe?

I'm using Java 8 specifically, if there is a more elegant way to do what I want in 8.

like image 967
Daniel Avatar asked Apr 24 '15 01:04

Daniel


1 Answers

Generally, an unchecked operation is unavoidable in this case. The best thing you can do is to isolate the unchecked operation and guard it with a safe test. One proven solution is to wrap the instance in a holder instance which encapsulates the unchecked operation and enforces the pre-check:

class HandlerRegistry {
    private static class Holder<T> {
        private final Class<T>   type;
        private final Handler<? extends T> handler;
        Holder(Class<T> c, Handler<? extends T> h) {
            type=Objects.requireNonNull(c);
            handler=h;
        }
        <U> Holder<U> as(Class<U> expected) {
            if(type!=expected)
                throw new ClassCastException();
            @SuppressWarnings("unchecked") Holder<U> h=(Holder)this;
            return h;
        }
        public Handler<? extends T> getHandler() {
            return handler;
        }
    }
    Map<Class<?>, Holder<?>> handlers;

    <T> void setHandler(Class<T> type, Handler<? extends T> handler) {
        handlers.put(type, new Holder<>(type, handler));
    }
    <T> T handle(Class<T> type, HandlerArgument arg) {
        return getHandler(type).handle(arg);
    }
    <T> Handler<? extends T> getHandler(Class<T> type) {
        return handlers.get(type).as(type).getHandler();
    }
}
like image 109
Holger Avatar answered Oct 19 '22 22:10

Holger