如何修复此泛型示例

时间:2015-10-21 12:48:10

标签: java generics

我有一个泛型问题,我创建了一个简化的例子。

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

public class Test {
    public interface Handler<T> {
        public String getType();

        public T getInitMessage();

        public void process(T message);
    }

    public static class MessageType1 {
        // Message type 1
    }

    public static class MessageType2 {
        // Message type 2
    }

    public static void main(String[] args) {
        Handler<MessageType1> messageType1Handler = new Handler<MessageType1>() {
            @Override
            public String getType() {
                return "messageType1";
            }

            @Override
            public MessageType1 getInitMessage() {
                return new MessageType1();
            }

            @Override
            public void process(MessageType1 message) {
                System.out.println(message);
            }
        };

        Handler<MessageType2> messageType2Handler = new Handler<MessageType2>() {
            @Override
            public String getType() {
                return "messageType2";
            }

            @Override
            public MessageType2 getInitMessage() {
                return new MessageType2();
            }

            @Override
            public void process(MessageType2 message) {
                System.out.println(message);
            }
        };

        Map<String, Handler<?>> handlers = new HashMap<String, Handler<?>>();
        handlers.put(messageType1Handler.getType(), messageType1Handler);
        handlers.put(messageType2Handler.getType(), messageType2Handler);

        String type = "messageType1"; // something valid

        Handler<?> handler = handlers.get(type);
        handler.process(handler.getInitMessage());
    }
}

它说:

Test.Handler类型中的方法进程(捕获#4-of?)不适用于参数(捕获#5-of?)

我应该如何修改我的代码以保留一组处理程序,它应该能够使用适当的参数调用这些处理程序的方法?

2 个答案:

答案 0 :(得分:4)

编译器抱怨的是,它无法在从getInitMessage()收到的未知类型的邮件与handler.process()接受的邮件类型之间建立连接。

解决这个问题的方法是使用静态方法显式地将两个类型参数绑定在一起:

private static <T> void process(Handler<T> handler) {
    handler.process(handler.getInitMessage());
}

然后您调用此方法而不是handler.process(handler.getInitMessage());

如果我们明确地将消息分配给变量,编译器不会自己解决这个问题的原因会更好:

Handler<?> handler = handlers.get(type);
? message = handler.getInitMessage();
handler.process(message);

现在,? message不是有效的语法,但我必须使用Object,因为这是MessageType1MessageType2共有的最具体的超类,然后就是没有任何方法handler.process(Object)的问题。

即使我们认为编译器会知道message不是Object而是capture of ?,但问题是它不能将capture of ?视为同一个?键入Handler<?>中的Handler<?> handler = handlers.get(type); ? message = handler.getInitMessage(); handler = handlers.get(anotherType); // added this handler.process(message); - 因为如果确实如此,则会编译:

?

如果您允许自己认为?引用的是同一类型,则无法阻止我仅使用静态类型检查。

通过使用上面建议的静态方法,我们在静态方法的持续时间内绑定{{1}}的含义,以便编译器可以将其视为相同的类型。我可以每次使用不同的类型参数输入静态方法,但它不能在内部更改 - 编译器将确保在静态方法内部。

答案 1 :(得分:0)

一个简单(并且不可否认的优雅)解决方案是改变process()的原型:

public void process(Object message);

Jiri的回答(优雅)提出的另一个不优雅的解决方案是直接参数化main()public static <T> void main(String[] args)。然后,您可以像以前一样呼叫process()

Handler<T> handler = (Handler<T>) handlers.get(type);
handler.process(handler.getInitMessage());

此解决方案会给您一个例外,因为它涉及潜在的不安全转换。由于界面的定义方式使得强制转换始终是安全的,因此您可以使用@SuppressWarnings注释,如下所示:

@SuppressWarnings("unchecked") Handler<T> handler = (Handler<T>) handlers.get(type);

这两种解决方案都是为您提供选项的解决方法。请使用Jiri的答案。

顺便说一句,您还应将您的邮件类型重新声明为static,以避免编译错误:public static class MessageType1public static class MessageType2