回归基础:方法Sig中的泛型

时间:2017-07-07 08:30:37

标签: java generics

因此,当我尝试将对象Map<String, Future<Cache<?>>传递给具有Map<String, Future<?>>的方法时,我的代码显示编译器错误。如下......

private static void logProgress(Map<String, Future<Cache<?>>> cacheLoaders, Map<String, Future<?>> cacheWriters)
            throws InterruptedException, ExecutionException {
        if (logFuturesAndCheckStillRunning("loader", cacheLoaders)
                && logFuturesAndCheckStillRunning("writer", cacheWriters)) {
            try {
                TimeUnit.SECONDS.sleep(10);
            } catch (InterruptedException e) {
                logger.error("interrupted whilst log progress slept", e);
            }
            logProgress(cacheLoaders, cacheWriters);
        }
    }

    private static boolean logFuturesAndCheckStillRunning(String context, Map<String, Future<?>> futures)
            throws InterruptedException, ExecutionException {
        boolean areStillRunning = false;
        for (String key : futures.keySet()) {
            Future<?> future = futures.get(key);
            if (future.isDone()) {
                futures.remove(key);
                logger.info("{} future has completed: {}", context, future.get());
            } else {
                logger.info("{} future is still running: {}", context, future);
                areStillRunning = true;
            }
        }
        return areStillRunning;
    }

这应该有效,因为我的方法应该接受一个Map,其中键是一个String,值是任何类型的Future。

下面显示我可以使用缓存加载器并将其放入缓存编写器映射但是我无法将缓存编写器放入缓存加载器映射中,因为缓存加载器映射中的未来指定了CacheLoader泛型类型&#34;在我脑海中&#34;是一个类似的概念。

            logger.info("starting cache loading");
            Map<String, Future<Cache<?>>> cacheLoaders = startCacheLoading(injector, executorLoadingService);

            logger.info("starting cache writing");
            Map<String, Future<?>> cacheWriters = startCacheWriting(injector, executorWritingService);

            Future<Cache<?>> aa = cacheLoaders.get("");
            cacheWriters.put("", aa);

            Future<?> bb = cacheWriters.get("");
            cacheLoaders.put("", bb);

所以我的问题是......有人可以向我解释为什么原始代码片段存在编译错误,并且是否有一种方法让我通过两个地图而丢失值作为期货的类型?

1 个答案:

答案 0 :(得分:6)

更多地剥离你的例子,你正在尝试编译它:

interface Cache<T> {}

static void logProgress(Map<String, Future<?>> futures) {}

public static void main (String[] args) {
    Map<String, Future<Cache<?>>> map = new HashMap<>();
    logProgress(map);  // Compiler error.
}

futures地图上的界限更改为:

Map<String, ? extends Future<?>>

Ideone demo

您的初始代码不起作用的原因是Java中的泛型是不变的:即使Future<Cache<?>>Future<?>Map<String, Future<Cache<?>>也不是Map<String, Future<?>> },与Map<String, Integer>不是Map<String, Object>的方式非常相似。

如果是,您的logProgress方法可能会在Map<Future<SomethingOtherThanCache>>地图中添加futures,从而导致返回时发生非类型安全事件:

void logProgress(Map<String, Future<?>> futures) {
  Future<String> future = ...
  futures.put("", future);  // Compiler error, but let's pretend it's OK.
}

Map<String, Future<Cache<?>> map = new HashMap<>();
logProgress(map);  // Compiler error, but let's pretend it's OK.
Future<Cache<?>> future = map.values().iterator().next();
Cache<?> cache = future.get();  // ClassCastException here!

相反,您需要使用上限(? extends Future<?>),这使得logProgress尝试将任何内容放入地图(除文字null之外)的编译器错误

void logProgress(Map<String, ? extends Future<?>> futures) {
  futures.put("", Futures.immediateFuture("boom!")); // Compiler error.

  futures.put("", null);  // OK.
}