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
( T
真的是你从multimap得到的处理程序的类型吗? – 编译器不知道)。
你的第二种方法确实得到了正确的赋值,但是handle()
方法不起作用,因为你传入了Serializable
,它可以是任何东西( String
, Integer
,其他东西),编译器仍然不知道处理程序的类型匹配(想象它是一个Handler
,你将一个String
传递给doStuff2
)。
您有几种方法可以解决这个问题,每种方法都有其自身的缺点:
- 只需使用
Multimap
,它允许您将任何> Serializable
对象传递给处理程序 - 使用具体类型,例如
Multimap
,这将仅限于字符串处理程序> - 在运行时获取处理程序的type参数并进行强制转换,如果不正确,可能会出错
如果你定义它会更好:
private Multimap> multimap = ArrayListMultimap.create();
更新:解释您的问题。
当你有类似的东西..
private Multimap> multimap;
这意味着multimap可以接受任何Handler
,其中N Extends Serializable
。 让我们考虑它将包含Handler
类型的值和Handler
类型的值。 Foo
和Bar
是无关的,不会相互延伸。
在函数中,你想使用一个类型来表示Handler extends Serializable>
的所有可能值的类型Handler extends Serializable>
Handler extends Serializable>
,你试图表达一个同时Foo
和Bar
的类型,但是没有这样的类型。
这解释了您的编译器问题。 现在删除这个“-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 extends Event>> 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 extends Event> 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 extends Event> 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 super Serializable> 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;
如果不这样做,则会收到错误而不是警告……
是的,仿制药有点乱……: – /