繁体   English   中英

Java泛型通配符问题

[英]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>类型的值。 FooBar是无关的,不会相互延伸。

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

这解释了编译器的问题。 现在删除这个“-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 ,它可以是任何东西( StringInteger ,别的东西),编译器仍然不知道处理程序的类型匹配(想象它是一个Handler<Number> ,你将一个String传递给doStuff2 )。

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

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

我稍微修改了你的更新代码。 现在它没有@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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM