Java 泛型通配符问题

发布于 2024-11-07 01:03:03 字数 2131 浏览 1 评论 0 原文

在使用 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> 到 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 来处理这个问题?

I'm facing some problems with Generics when using Google Guava's excellent Multimap. I have a type Handler defined as such

public interface Handler<T extends Serializable> {
    void handle(T t);
} 

In another class I've defined a multimap that maps a String to a collection of Handlers.

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

Now when I try to do stuff with the multimap, I'm getting compiler errors. My first attempt looked like this:

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);
    }
}

which resulted in the following error.

Type mismatch: cannot convert from Collection<Handler<? extends Serializable>>
to Collection<Handler<T>>

Afterwards, I tried to code it like this

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

which unfortunately failed as well:

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

Any help would be greatly appreciated. Thanks.

Update:

The only way I have managed to fix this is by suppressing compiler warnings. Given the following handler:

public interface Handler<T extends Event> {
    void handle(T t);

    Class<T> getType();
}

I can write the event bus as such.

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);
        }
    }
}

I guess there's no way to handle this with less or even without @SuppressWarnings?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

拥抱我好吗 2024-11-14 01:03:03

问题是类型可能不同:

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

不允许您向多重映射添加任何内容,因为您不知道 ? 实际代表什么。例如,您可以拥有一个 Multimap> 并尝试添加一个 Integer,因为两者都实现了 Serialized

编辑:其实上面这段话有一点错误。您应该能够将处理程序添加到多重映射,但由于处理程序的类型参数未知,因此您将无法使用处理程序,请参见下文。

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

您的第二种方法确实得到了正确的分配,但是 handle() 方法不起作用,因为您传入了一个 Serialized ,它可以是任何东西 (String< /code>、Integer、其他),编译器仍然不知道处理程序的类型是否与该类型匹配(想象它是一个 Handler 并且您传递了 <代码>字符串doStuff2)。

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

  1. 只需使用 Multimap>,它允许您传递任何 Serialized 对象到处理程序
  2. 使用具体类型,例如 Multimap>,这将限制您只能使用字符串处理
  3. 程序 获取运行时的处理程序和强制转换,如果你没有正确处理,可能会容易出错

The problem is that the types might be different:

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

wouldn't allow you to add anything to the multimap, since you don't know what ? actually stands for. You could for example have a Multimap<String, Handler<String>> and try to add an Integer because both implement Serializable.

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 parameter T 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); (is T 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 a Serializable 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 a Handler<Number> and you pass a String to doStuff2).

You have several alternatives to fix that, each with it's own drawbacks:

  1. Just use Multimap<String, Handler<Serializable>>, which would allow you to pass any Serializable object to the handler
  2. Use a concrete type, e.g. Multimap<String, Handler<String>>, which would limit you to string handlers only
  3. Get the type parameter of the handler at runtime and cast, which might be error prone if you don't get it right
江湖正好 2024-11-14 01:03:03

如果您定义以下内容,效果会更好:

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

更新:问题的解释。

当您有类似 .. 之类的内容时,

private Multimap<String, Handler<? extends Serializable>> multimap;

这意味着 multimap 可以接受任何 Handler 其中 N 扩展可序列化。我们假设它将包含一个 Handler 类型的值和一个 Handler 类型的值。 FooBar 不相关,也不相互扩展。

当您在函数中想要使用类型来表示 Handler,你试图表达一个同时是 FooBar 的类型,但是不存在这样的类型。

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

It will work better if you define:

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

Update: Explaination of your problem.

When you have something like ..

private Multimap<String, Handler<? extends Serializable>> multimap;

It means that multimap can accept ANY Handler<N> where N Extends Serializable. Let's consider that it will contain a value of type Handler<Foo> and a value of type Handler<Bar>. Foo and Bar are unrelated and do not extend from each other.

When in your function you want to use a type to represent the type of all the possible values of Handler<? extends Serializable>, you are trying to express a type which is at the same time Foo and Bar, but there is no such a type.

This explains your problem with the compiler. Now remove this "-1" and vote for my answer if you think I am correct.

淡淡離愁欲言轉身 2024-11-14 01:03:03

分两部分回答。首先,你的方法“消耗”对象,所以你不能使用“扩展”......你需要使用“超级”(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;

如果你不这样做,你会得到一个错误而不是警告......

是的,泛型有点混乱..:-/

Two part answer. Firts, your method "consumes" objects, so you can't use "extends"... you need to use "super" (PECS: Producer Extends, Consumer Super!).

Without changing your handler, this compiles with without warnings for me:

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);
    }
}

This way you define a multimap from string to a handler that consume at least Serializable.

Second, I have often used something similar to your construct:

Map<Class<?>, Handler<?>> 

and is where the handler returned is a consumer of Class. The main problem is that you have no way to "add to the generic type" when you know more... If you need to, always declare a new variable can put the @SuppressWarning at the declaration:

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

This only work if you are casting the whole generic type. If you are given Handler and you know that what you have is really Handler>, the only way you can cast it is by going through the raw type:

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

If you don't, you get an error instead of a warning...

Yes, generics are kind of messy.. :-/

浸婚纱 2024-11-14 01:03:03

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

I modified your updated code a bit.
Now it works without @SuppressWarnings.

I've made some assumptions, of course.

But I hope it will be helpful.

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();
}

Modified 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);
        }
    }
}

And some more code, just to complete the example.

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 ...

}

The program output looks like this:

MyEvent@12276af2
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文