[英]Java Generics Wildcards Question
在使用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来处理这个问题?
如果你定义它会更好:
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>
的所有可能值的类型Handler<? extends Serializable>
Handler<? extends Serializable>
,你试图表达一个同时Foo
和Bar
的类型,但是没有这样的类型。
这解释了编译器的问题。 现在删除这个“-1”并投票给我答案,如果你认为我是正确的。
问题是类型可能不同:
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
, Integer
,别的东西),编译器仍然不知道处理程序的类型匹配(想象它是一个Handler<Number>
,你将一个String
传递给doStuff2
)。
您有几种方法可以解决这个问题,每种方法都有其自身的缺点:
Multimap<String, Handler<Serializable>>
,它允许您将任何Serializable
对象传递给处理程序 Multimap<String, Handler<String>>
,这将仅限于字符串处理程序 我稍微修改了你的更新代码。 现在它没有@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
两部分答案。 Firts,你的方法“消耗”对象,所以你不能使用“扩展”......你需要使用“超级”(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>,你可以通过原始类型来实现它的唯一方法:
Handler<List> myLessSpecifiedHandler = getHandler();
@SuppressWarnings("unchecked")
Handler<List<String>> myHandler = (Handler<List<String>>) (Handler) myLessSpecifiedHandler;
如果不这样做,则会收到错误而不是警告......
是的,仿制药有点乱......: - /
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.