使用Google Guava优秀的Multimap时,我在Generics方面遇到了一些问题。我有一个类型Handler定义为
public interface Handler<T extends Serializable> {
void handle(T t);
}
在另一个类中,我定义了一个将String映射到Handlers集合的多图。
private Multimap<String, Handler<? extends Serializable>> multimap =
ArrayListMultimap.create();
现在,当我尝试使用multimap时,我遇到了编译错误。我的第一次尝试看起来像这样:
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来处理这个问题?
答案 0 :(得分:2)
如果你定义:
,它会更好private Multimap<String, Handler<Serializable>> multimap =
ArrayListMultimap.create();
更新:解释您的问题。
当你有类似的东西时。
private Multimap<String, Handler<? extends Serializable>> multimap;
这意味着multimap可以接受任何Handler<N>
N Extends Serializable
。我们假设它将包含Handler<Foo>
类型的值和类型Handler<Bar>
的值。 Foo
和Bar
不相关,不会相互延伸。
在函数中,您希望使用类型来表示Handler<? extends Serializable>
的所有可能值的类型时,您试图表达同时Foo
和{{ 1}},但没有这样的类型。
这解释了编译器的问题。现在删除这个“-1”并投票给我答案,如果你认为我是正确的。
答案 1 :(得分:2)
问题是类型可能不同:
private Multimap<String, Handler<? extends Serializable>> multimap =
ArrayListMultimap.create();
不允许您向multimap添加任何内容,因为您不知道?
实际代表什么。例如,您可以使用Multimap<String, Handler<String>>
并尝试添加Integer
,因为它们都是Serializable
。
编辑:实际上上段略有错误。您应该能够向multimap添加处理程序,但由于处理程序的类型参数未知,因此您将无法使用处理程序,请参阅下文。
在doStuff1
方法中,您定义了一个具体参数T
,这可能是完全不同的东西。因此,编译器无法确定此赋值是否正确:Collection<Handler<T>> collection = multimap.get(s);
(T
实际上是从multimap获取的处理程序的类型吗? - 编译器不知道。)
您的第二种方法确实可以使分配正确,但handle()
方法不起作用,因为您传入的Serializable
可能是任何内容(String
,{{1}而且,其他东西)并且编译器仍然不知道处理程序的类型是否匹配(假设它是Integer
并且您将Handler<Number>
传递给String
)。
你有几种方法可以解决这个问题,每种方法都有自己的缺点:
doStuff2
,即可将任何Multimap<String, Handler<Serializable>>
对象传递给处理程序Serializable
,仅限于字符串处理程序答案 2 :(得分:0)
我稍微修改了你的更新代码。 现在它没有@SuppressWarnings。
当然,我做了一些假设。
但我希望它会有所帮助。
public interface Handler<T extends Event> {
//void handle(T t);
// It seems you won't need dependency of T in this method implementation.
// So, you can use generic method here.
<S extends Event> void handle(S s);
Class<T> getType();
}
修改了EventBus。
public class EventBus {
//private Multimap<Class<?>, Handler<?>> multimap = ArrayListMultimap.create();
// It seems you don't need anything except Event here.
// So. Logicaly more correct to use the bounded wildcard.
private Multimap<Class<? extends Event>, Handler<? extends Event>>
multimap = ArrayListMultimap.create();
//public <T extends Event> void subscribe(Handler<T> handler) {
// You don't need to use generic method here.
// Because T is never used in method implementation.
// Wildcard fits simpler.
public void subscribe(Handler<? extends Event> handler) {
multimap.put(handler.getType(), handler);
}
//@SuppressWarnings({ "rawtypes", "unchecked" })
public void publish(Event event) {
//Collection<Handler<?>> collection = multimap.get(event.getClass());
// The bounded wildcard again.
Collection<Handler<? extends Event>>
collection = multimap.get(event.getClass());
//for (Handler handler : collection) {
for (Handler<? extends Event> handler : collection) {
handler.handle(event);
}
}
}
还有一些代码,只是为了完成这个例子。
public class Main {
public static void main(String[] args) {
EventBus bus = new EventBus();
bus.subscribe(new Handler<MyEvent> () {
public <S extends Event> void handle(S s) {
System.out.println(s);
}
public Class<MyEvent> getType() {
return MyEvent.class;
}
});
bus.publish(new MyEvent());
}
}
class MyEvent implements Event {
// Event implementation ...
}
程序输出如下所示:
MyEvent@12276af2
答案 3 :(得分:0)
两部分答案。第一,你的方法“消耗”对象,所以你不能使用“扩展”......你需要使用“超级”(PECS:Producer Extends,Consumer Super!)。
在不更改处理程序的情况下,我会在没有警告的情况下编译:
private Multimap<String, Handler<? super Serializable>> multimap = ArrayListMultimap.create();
public void doStuff1(String s, Serializable t) {
Collection<Handler<? super Serializable>> collection = multimap.get(s);
for (Handler<? super Serializable> handler : collection) {
handler.handle(t);
}
}
这样,您可以定义从字符串到至少使用Serializable的处理程序的多图。
其次,我经常使用与你的构造类似的东西:
Map<Class<?>, Handler<?>>
并且处理程序返回的是Class的使用者。主要问题是,当你知道更多时,你无法“添加到泛型类型”......如果需要,总是声明一个新变量可以将@SuppressWarning放在声明中:
@SuppressWarnings("unchecked")
Handler<String> myHandler = (Handler<String>) getHandler();
这仅适用于您正在构建整个泛型类型。如果给你Handler并且你知道你拥有的是Handler&gt;,你可以通过原始类型来实现它的唯一方法:
Handler<List> myLessSpecifiedHandler = getHandler();
@SuppressWarnings("unchecked")
Handler<List<String>> myHandler = (Handler<List<String>>) (Handler) myLessSpecifiedHandler;
如果不这样做,则会收到错误而不是警告......
是的,仿制药有点乱......: - /