Java 泛型通配符问题

2022-09-03 15:59:44

在使用Google Guava出色的Multimap时,我遇到了一些通用问题。我有一个类型处理程序,定义如下

public interface Handler<T extends Serializable> {
    void handle(T t);
} 

在另一个类中,我定义了一个多映射,它将字符串映射到处理程序的集合。

private Multimap<String, Handler<? extends Serializable>> multimap = 
    ArrayListMultimap.create();

现在,当我尝试使用多映射执行操作时,我收到编译器错误。我的第一次尝试看起来像这样:

public <T extends Serializable> void doStuff1(String s, T t)  {
    Collection<Handler<T>> collection = multimap.get(s);
    for (Handler<T> handler : collection) {
        handler.handle(t);
    }
}

这导致了以下错误。

Type mismatch: cannot convert from Collection<Handler<? extends Serializable>> to Collection<Handler<T>>

之后,我试图像这样编码

public void doStuff2(String s, Serializable serializable)  {
    Collection<Handler<? extends Serializable>> collection = multimap.get(s);
    for (Handler<? extends Serializable> handler : collection) {
        handler.handle(serializable); 
    }
}

不幸的是,这也失败了:

The method handle(capture#1-of ? extends Serializable) in the type Handler<capture#1-of ? extends Serializable> is not applicable for the arguments (Serializable)

任何帮助将不胜感激。谢谢。

更新:

我设法解决这个问题的唯一方法是禁止编译器警告。给定以下处理程序:

public interface Handler<T extends Event> {
    void handle(T t);

    Class<T> getType();
}

我可以这样编写事件总线。

public class EventBus {

    private Multimap<Class<?>, Handler<?>> multimap = ArrayListMultimap.create();

    public <T extends Event> void subscribe(Handler<T> handler) {
        multimap.put(handler.getType(), handler);
    }

    @SuppressWarnings({ "rawtypes", "unchecked" })
    public void publish(Event event)  {
        Collection<Handler<?>> collection = multimap.get(event.getClass());
        for (Handler handler : collection) {
            handler.handle(event);
        }
    }
}

我想没有办法用更少的甚至没有@SuppressWarnings来处理这个问题?


答案 1

问题是类型可能不同:

private Multimap<String, Handler<? extends Serializable>> multimap = 
ArrayListMultimap.create();

不允许你向多地图添加任何东西,因为你不知道实际上代表什么。例如,您可以有一个 并尝试添加一个,因为两者都实现了 .?Multimap<String, Handler<String>>IntegerSerializable

编辑:实际上上面的段落有点不对劲。您应该能够将处理程序添加到多映射中,但由于处理程序的类型参数未知,因此您将无法使用这些处理程序,请参阅下文。

在你的方法中,你定义了一个具体的参数,它可能是完全不同的东西。因此,编译器无法确定此赋值是否正确:(您实际上是从多映射中获取的处理程序的类型吗? - 编译器不知道)。doStuff1TCollection<Handler<T>> collection = multimap.get(s);T

你的第二种方法确实得到了正确的赋值,但是该方法将不起作用,因为你传入的可以是任何东西(,,其他东西),编译器仍然不知道处理程序的类型是否与此匹配(想象它是一个,你传递一个)。handle()SerializableStringIntegerHandler<Number>StringdoStuff2

您有几种替代方法来解决此问题,每种方法都有其自身的缺点:

  1. 只需使用 ,这将允许您将任何对象传递给处理程序Multimap<String, Handler<Serializable>>Serializable
  2. 使用具体类型,例如 ,这会将您限制为仅字符串处理程序Multimap<String, Handler<String>>
  3. 在运行时获取处理程序的类型参数并进行强制转换,如果操作不正确,则可能容易出错

答案 2

如果您定义:

private Multimap<String, Handler<Serializable>> multimap = 
    ArrayListMultimap.create();

更新:解释您的问题。

当你有类似..的东西

private Multimap<String, Handler<? extends Serializable>> multimap;

这意味着多地图可以接受任何位置。让我们考虑它将包含一个 type 的值和一个 type 的值。 并且是不相关的,并且不会相互延伸。Handler<N>N Extends SerializableHandler<Foo>Handler<Bar>FooBar

在函数中,当要使用类型来表示 的所有可能值的类型时,您正在尝试表示同时与 的类型,但没有这样的类型。Handler<? extends Serializable>FooBar

这解释了编译器的问题。现在删除这个“-1”,如果你认为我是正确的,请为我的答案投票。


推荐