如何使用带有通配符的集合和泛型?

noo*_*one 5 java generics collections wildcard

首先,我将尝试解释此代码背后的想法.我有一堆类(处理器)可以处理某种类型的其他类(Processables).我有一个处理器列表以按特定顺序执行它们.我有一个Map,它将检索我要为某个处理器处理的数据(Processables).它看起来像这样.

public abstract class AbstractProcessable {
    ...
}

public class DummyProcessable extends AbstractProcessable {
   ...
}

public abstract class AbstractProcessor<T extends AbstractProcessable> {
    public abstract void process(List<T> listOfProcessables);
}

public class DummyProcessor extends AbstractProcessor<DummyProcessable> {

    @Override
    public void process(List<DummyProcessable> listToProcess) {
        ...
    }
}
Run Code Online (Sandbox Code Playgroud)

到目前为止,这似乎工作正常.没有编译错误.但现在我有一个类如下:

public class RandomClass {

    private List<AbstractProcessor<? extends AbstractProcessable>> processors;

    private Map<Class<? extends AbstractProcessor>, List<? extends AbstractProcessable>> data;

    public RandomClass() {
        processors = new ArrayList<>();
        processors.add(new DummyProcessor());

        data = new HashMap<>();
        data.put(DummyProcessor.class, new ArrayList<DummyProcessable>());

        processAll();
    }

    private void processAll() {
        for (AbstractProcessor<? extends AbstractProcessable> processor : processors) {
            List<? extends AbstractProcessable> dataToProcess;
            dataToProcess = data.get(processor);
            processor.process(dataToProcess); // compile error
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

编译错误:

    The method process(List<capture#4-of ? extends AbstractProcessable>) in the type AbstractProcessor<capture#4-of ? extends AbstractProcessable> is not applicable for the arguments (List<capture#5-of ? extends AbstractProcessable>)
Run Code Online (Sandbox Code Playgroud)

我知道这可能有点难以阅读,但我试图尽可能地简化它.我对泛型也不是那么好,所以也许我用了一些通配符错了?任何人都可以帮我解决这个问题吗?

非常感谢提前!

Raf*_*ele 1

如果我正确理解你的问题,我前段时间也问过类似的问题。简而言之,您无法按照您想要的方式使用这样的地图。好消息是您不需要它;)

问题是 Java 泛型是编译时的东西(您可能已经知道),它们仅存在于编译时,而不是当您实际填充List. 您无法找到一种用 Java 表达您的想法的方法,即使它看起来完全合法。

您可能会发现超级类型令牌在某些情况下对于提取参数很有用,并避免用户在合适的情况下提供显式参数(但请注意限制并考虑它们是否真的可以增加一些价值)。

简而言之,你最终会得到一个像这样的字段

private Map<Class<?>, List<?>> data;
Run Code Online (Sandbox Code Playgroud)

(或者您可以使用Guava 的Multimap实现),使用一些强制转换并发出一些警告,并依靠您的程序逻辑来保证客户端代码的类型安全。我在代码中使用了这种方法,到目前为止从未失败过。

这是我的代码。我认为这正是你的情况,只需将Subscriber<T>and替换Message为你的Processor<T>andProcessable

public class MessageBus {

    public enum Action {
        CREATE, REMOVE, UPDATE, DELETE;
    }

    private static Map<Class<?>, Set<Subscriber<?>>> subscriptions;

    static {
        subscriptions = new HashMap<Class<?>, Set<Subscriber<?>>>();
    }

    @SuppressWarnings("unchecked")
    public static <T> void publish(T message, Action action) {
        Set<Subscriber<?>> set = getSubscribersFor(message.getClass());

        if (set == null)
            return;

        for (Subscriber<?> subscriber: set) {
            ((Subscriber<T>) subscriber).onMessage(message, action);
        }
    }

    public static <T> void subscribe(Class<T> type, Subscriber<T> subscriber) {
        Set<Subscriber<?>> set = getSubscribersFor(type);

        if (set == null) {
            set = new HashSet<Subscriber<?>>();
            subscriptions.put(type, set);
        }

        set.add(subscriber);
    }

    public static <T> void unsuscribe(Class<T> type, Subscriber<T> subscriber) {
        Set<Subscriber<?>> set = getSubscribersFor(type);
        set.remove(subscriber);
    }

    private static Set<Subscriber<?>> getSubscribersFor(Class<?> topic) {
        return subscriptions.get(topic);
    }
}
Run Code Online (Sandbox Code Playgroud)