Java 泛型通配符问题
在使用 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);
}
}
这导致了以下错误。
类型不匹配:无法从 Collection
之后,我尝试像这样编码
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 Serialized) in the type
处理程序
任何帮助将不胜感激。谢谢。
更新:
我设法解决此问题的唯一方法是抑制编译器警告。给定以下处理程序:
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 来处理这个问题?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

发布评论
评论(4)
如果您定义以下内容,效果会更好:
private Multimap<String, Handler<Serializable>> multimap =
ArrayListMultimap.create();
更新:问题的解释。
当您有类似 .. 之类的内容时,
private Multimap<String, Handler<? extends Serializable>> multimap;
这意味着 multimap 可以接受任何 Handler
其中 N 扩展可序列化
。我们假设它将包含一个 Handler
类型的值和一个 Handler
类型的值。 Foo
和 Bar
不相关,也不相互扩展。
当您在函数中想要使用类型来表示 Handler
,你试图表达一个同时是 Foo
和 Bar
的类型,但是不存在这样的类型。
这解释了你的编译器问题。现在删除这个“-1”,如果您认为我是正确的,请投票给我的答案。
分两部分回答。首先,你的方法“消耗”对象,所以你不能使用“扩展”......你需要使用“超级”(PECS:生产者扩展,消费者超级!)。
在不更改处理程序的情况下,编译时不会对我发出警告:
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);
}
}
通过这种方式,您可以定义从字符串到至少消耗 Serialized 的处理程序的多重映射。
其次,我经常使用与您的构造类似的东西:
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;
如果你不这样做,你会得到一个错误而不是警告......
是的,泛型有点混乱..:-/
我稍微修改了你更新的代码。
现在它可以在没有@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
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
问题是类型可能不同:
不允许您向多重映射添加任何内容,因为您不知道
?
实际代表什么。例如,您可以拥有一个 Multimap="">> 并尝试添加一个Integer
,因为两者都实现了Serialized
。编辑:其实上面这段话有一点错误。您应该能够将处理程序添加到多重映射,但由于处理程序的类型参数未知,因此您将无法使用处理程序,请参见下文。
在您的 doStuff1 方法中,您定义了一个具体参数 T,它可能是完全不同的东西。因此,编译器无法确定此分配是否正确:Collection> collection = multimap.get(s); (
T
真的是从多重映射中获得的处理程序的类型吗? - 编译器不知道)。您的第二种方法确实得到了正确的分配,但是
handle()
方法不起作用,因为您传入了一个Serialized
,它可以是任何东西 (String< /code>、
Integer
、其他),编译器仍然不知道处理程序的类型是否与该类型匹配(想象它是一个Handler
并且您传递了 <代码>字符串到doStuff2
)。您有多种替代方法可以解决这个问题,每种方法都有其自身的缺点:
Multimap="">>
,它允许您传递任何Serialized
对象到处理程序Multimap="">>
,这将限制您只能使用字符串处理The problem is that the types might be different:
wouldn't allow you to add anything to the multimap, since you don't know what
?
actually stands for. You could for example have aMultimap<String, Handler<String>>
and try to add anInteger
because both implementSerializable
.Edit: Actually the above paragraph is slightly wrong. You should be able to add handlers to the multimap, but since the type parameters of the handlers are not known, you wouldn't be able to use the handlers, see below.
In your
doStuff1
method you define a concrete parameterT
which might be something completely different. Thus the compiler can't determine if this assignment would be correct:Collection<Handler<T>> collection = multimap.get(s);
(isT
really the type of the handler you get from the multimap? - The compiler doesn't know).Your second approach does get the assignment right, however the
handle()
method won't work, since you pass in aSerializable
which could be anything (String
,Integer
, something else) and the compiler still doesn't know if the handler's type matches that (imagine it's aHandler<Number>
and you pass aString
todoStuff2
).You have several alternatives to fix that, each with it's own drawbacks:
Multimap<String, Handler<Serializable>>
, which would allow you to pass anySerializable
object to the handlerMultimap<String, Handler<String>>
, which would limit you to string handlers only