How can I avoid using the original type and use generics instead when the type is unknown?

I have a generic interface for a handler:

public interface EventHandler<T> {

     boolean handleEvent(T message);
}

      

You can implement this interface and process T message

. You have to pass an instance of your implementation to an instance EventDispatcher

. The class internally stores several instances of EventHandler

various types.

At some point, the event is triggered and the instance EventDispatcher

calls the appropriate method EventHandler

handleEvent()

and sends a message that is the same type as EventHandler

. The problem is, I don't know which type exactly, but I'm pretty sure I'm going T message

to an instance EventHandler

that has the same "T".

The way I am making this call is using raw types and it works:

EventHandler handler = getHandler();
Object message = getMessage();
handler.handleEvent(message);

      

but I get the warning "Unchecked call to" handleEvent (T) "as a member of the original type" EventHandler ".

Can generics be used to avoid warning?

One possible solution I was thinking about is creating a generic wrapper method:

private <T> boolean handleEventGeneric(EventHandler<T> handler, T message) {
    return handler.handleEvent(message);
}

      

and use it:

EventHandler handler = getHandler();
Object message = getMessage();
handleEventGeneric(action.eventHandler, message);

      

But I don't like the fact that I have to create a wrapper method. Is there a more elegant solution?

+3


source to share


2 answers


There is no way to remove this warning (or at least not without @SuppressWarning

). The only way to get rid of it is to do 2 things:

  • Provide proof of type code: so basically, unless the calling code is using a raw type, you are type safe, or the calling code is using rawtypes, and it is a mistake if the code is ultimately not type safe
  • Add @SuppressWarning("unchecked")

    to your code.

The warning is there so that you can easily identify where your type safety weakness is in your code. If you use annotation correctly, your code will be safe and you are sure you will not get nasty ClassCastException

, unless you add @SuppressWarning("unchecked")

in a place where you are not really all sure that this type is safe.



See demo code to illustrate my point:

import java.util.HashMap;
import java.util.Map;

public class TestGenerics {

    private Map<Class<?>, EventHandler<?>> handlers = new HashMap<Class<?>, TestGenerics.EventHandler<?>>();

    public interface EventHandler<T> {

        boolean handleEvent(T message);
    }

    // Here you force the invoker to provide the correct type of event handler
    // with the given type of klass
    // If he wants to make this fail, then he will have to use rawtype
    public <T> void registerHandler(Class<T> klass, EventHandler<T> handler) {
        handlers.put(klass, handler);
    }

    public <T> void handle(T message) {
        @SuppressWarnings("unchecked") // Here you can add this annotation since you are forcing any invoker to provide a correct EventHandler
        EventHandler<T> handler = (EventHandler<T>) handlers.get(message.getClass());
        if (handler != null) {
            handler.handleEvent(message);
        }
    }

    public static void main(String[] args) {
        TestGenerics test = new TestGenerics();
        test.registerHandler(Long.class, new EventHandler<Long>() {
            @Override
            public boolean handleEvent(Long message) {
                System.out.println("Received a long " + message);
                return true;
            }
        });
        // Here I use raw type but this also means that I created a weak spot in
        // terms of type safety
        EventHandler handler2 = new EventHandler<String>() {
            @Override
            public boolean handleEvent(String message) {
                System.out.println("THis will never print " + message);
                return false;
            }
        };
        test.registerHandler(Integer.class, handler2); // This is where the
                                                        // problem comes from
        test.handle(3L); // OK
        test.handle(1); // ClassCastException

    }

}

      

+1


source


You can do it:

EventHandler<Object> handler = getHandler();
Object message = getMessage();
handler.handleEvent(message);

      



It is not safe, but no less secure than yours now.

-1


source







All Articles