【问题标题】:Java generics: capture cannot be applied to ObjectJava泛型:捕获不能应用于对象
【发布时间】:2014-06-06 02:39:19
【问题描述】:

我的代码有一个Map 的(消息)处理程序。我正在尝试使处理程序通用化(如接口处理程序所见)。如果没有泛型,处理程序都需要从 Object 转换为相应的类,这很好避免(但一切正常)。对于每个消息类(下面的Foo),我都有一个处理程序类。

如何将任何类型的类映射到任何类型的处理程序并使用“仅”Object 获取/调用? (不能限制handleMessage(Object)的参数)

请参阅下面的 MWE。

import java.util.*;
public class Logic
{   
    Map<Class<?>, Handler<?>> handlers = new HashMap<Class<?>, Handler<?>>();

    public void run()
    {   
        handlers.put(Foo.class, new FooHandler());
    }   

    public void handleMessage(Object msg)
    {   
        Handler<?> handler = handlers.get(msg.getClass());
        if (handler != null) {
            handler.execute(msg);
        }   
    }   

    private interface Handler<T>
    {   
        public void execute(T msg);
    }   

    private class FooHandler implements Handler<Foo>
    {   
        public void execute(Foo msg) {}
    }   

    private class Foo {}
}

这段代码产生:

Logic.java:16:无法将 Logic.Handler 中的执行(capture#x of ?)应用于(java.lang.Object) handler.execute(msg);

如何在保持 Handler 接口通用的同时修复它以使其正常工作?

【问题讨论】:

    标签: java generics


    【解决方案1】:

    您不能定义字段中键和值之间的关系,但可以使用访问器方法来强制执行它,前提是仅使用这些方法来访问映射。

    private final Map<Class, Handler> handlers = new HashMap<Class, Handler>();
    
    public <T> void addHandler(Class<T> clazz, Handler<T> handler) {
        handlers.put(clazz, handler);
    }
    
    @SuppressWarnings("unchecked")
    public <T> Handler<T> getHandler(Class<T> clazz) {
        return (Handler<T>) handlers.get(clazz);
    }
    
    @SuppressWarnings("unchecked")
    public <T> Handler<T> getHandlerFor(T t) {
        return getHandler((Class<T>) t.getClass());
    }
    
    public void run()    {
        addHandler(Foo.class, new FooHandler());
    }
    
    public <T> void handleMessage(T msg) {
        Handler<T> handler = getHandlerFor(msg);
        if (handler != null) {
            handler.execute(msg);
        }
    }
    

    【讨论】:

    • 您的回答让我输入了安全的异构容器、泛型类型的关系等等!我有点失望,没有包装方法和@SuppressWarnings 是不可能的:
    • 问题是您希望通用语法有多复杂。 ;) 它会检查对 addHandler、hetHandler 和 getHandlerFor 的调用是否正确。
    【解决方案2】:

    问题在于execute() 采用某种参数类型,比Object 更具体。

    但是,在您的handleMessage() 方法中,编译器不知道参数是什么类型。假设FooHandler 注册了Bar 类(这是可能的)。

    在这种情况下,handler.execute(msg); 实际上会导致使用Bar 参数调用FooHandler#execute(Foo),这将导致ClassCastException(除非Bar extends Foo)。因此编译器拒绝编译该代码。

    【讨论】:

      【解决方案3】:

      另一个不在这里但应该是的答案 - 删除所有泛型语法(即删除所有&lt;?&gt;)。然后解析器将恢复为 JDK1.4 语法,这一切都会正常工作。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2017-03-06
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2021-02-22
        • 1970-01-01
        相关资源
        最近更新 更多