Javagenerics通配符问题

在使用Google Guava优秀的Multimap时,我遇到了Generics的一些问题。 我有一个类型Handler定义为这样

public interface Handler { void handle(T t); } 

在另一个类中,我定义了一个将String映射到Handlers集合的多图。

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

现在,当我尝试使用multimap时,我遇到了编译器错误。 我的第一次尝试看起来像这样:

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

这导致以下错误。

Type mismatch: cannot convert from Collection<Handler> to Collection<Handler>

之后,我试着像这样编码

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

不幸的是也失败了:

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

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

更新:

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

 public interface Handler { void handle(T t); Class getType(); } 

我可以这样写事件总线。

 public class EventBus { private Multimap<Class, Handler> multimap = ArrayListMultimap.create(); public  void subscribe(Handler 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来处理这个问题?

问题是类型可能不同:

 private Multimap> multimap = ArrayListMultimap.create(); 

不允许你向multimap添加任何东西,因为你不知道什么? 实际上代表着。 例如,您可以使用Multimap>并尝试添加Integer因为它们都实现了Serializable

编辑:其实上面的段落有点不对。 您应该能够向multimap添加处理程序,但由于处理程序的类型参数未知,因此您将无法使用处理程序,请参阅下文。

在你的doStuff1方法中,你定义了一个具体的参数T ,它可能是完全不同的东西。 因此编译器无法确定此赋值是否正确: Collection> collection = multimap.get(s);T真的是你从multimap得到的处理程序的类型吗? – 编译器不知道)。

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

您有几种方法可以解决这个问题,每种方法都有其自身的缺点:

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

如果你定义它会更好:

 private Multimap> multimap = ArrayListMultimap.create(); 

更新:解释您的问题。

当你有类似的东西..

 private Multimap> multimap; 

这意味着multimap可以接受任何Handler ,其中N Extends Serializable 。 让我们考虑它将包含Handler类型的值和Handler类型的值。 FooBar是无关的,不会相互延伸。

在函数中,你想使用一个类型来表示Handler的所有可能值的类型Handler Handler ,你试图表达一个同时FooBar的类型,但是没有这样的类型。

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

我稍微修改了你的更新代码。 现在它没有@SuppressWarnings。

当然,我做了一些假设。

但我希望它会有所帮助。

 public interface Handler { //void handle(T t); // It seems you won't need dependency of T in this method implementation. // So, you can use generic method here.  void handle(S s); Class getType(); } 

修改过的EventBus。

 public class EventBus { //private Multimap, 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, Handler> multimap = ArrayListMultimap.create(); //public  void subscribe(Handler 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 handler) { multimap.put(handler.getType(), handler); } //@SuppressWarnings({ "rawtypes", "unchecked" }) public void publish(Event event) { //Collection> collection = multimap.get(event.getClass()); // The bounded wildcard again. Collection> collection = multimap.get(event.getClass()); //for (Handler handler : collection) { for (Handler handler : collection) { handler.handle(event); } } } 

还有一些代码,只是为了完成这个例子。

 public class Main { public static void main(String[] args) { EventBus bus = new EventBus(); bus.subscribe(new Handler () { public  void handle(S s) { System.out.println(s); } public Class getType() { return MyEvent.class; } }); bus.publish(new MyEvent()); } } class MyEvent implements Event { // Event implementation ... } 

程序输出如下所示:

 MyEvent@12276af2 

两部分答案。 Firts,你的方法“消耗”对象,所以你不能使用“扩展”……你需要使用“超级”(PECS:Producer Extends,Consumer Super!)。

在不更改处理程序的情况下,这会在没有警告的情况下编译:

 private Multimap> multimap = ArrayListMultimap.create(); public void doStuff1(String s, Serializable t) { Collection> collection = multimap.get(s); for (Handler handler : collection) { handler.handle(t); } } 

这样,您可以定义从字符串到至少使用Serializable的处理程序的多图。

其次,我经常使用与你的构造类似的东西:

 Map, Handler> 

并且处理程序返回的是Class的使用者。 主要问题是,当你知道更多时,你无法“添加到generics类型”……如果需要,总是声明一个新变量可以将@SuppressWarning放在声明中:

 @SuppressWarnings("unchecked") Handler myHandler = (Handler) getHandler(); 

这仅在您投射整个generics类型时才有效。 如果给你Handler并且你知道你拥有的是Handler>,那么你可以通过原始类型来实现它的唯一方法:

 Handler myLessSpecifiedHandler = getHandler(); @SuppressWarnings("unchecked") Handler> myHandler = (Handler>) (Handler) myLessSpecifiedHandler; 

如果不这样做,则会收到错误而不是警告……

是的,仿制药有点乱……: – /