由于Java泛型,我无法编译Guava的缓存构建器



我有以下简短的、自包含的代码,在编译时显示了一个错误。我拼命想把它编出来。我通常对泛型不再有麻烦了,但是我放弃了这个,我请求团队的帮助。

import com.google.common.cache.CacheBuilder;
import com.google.common.cache.CacheLoader;
import com.google.common.cache.LoadingCache;
public class CacheWithGenericsDoesNotCompile {
  // Class definition can't be modified
  static class Resource<T extends Resource<T>> {}
  // Class definition can't be modified
  static class ResourceType<T extends Resource<T>> {
    public ResourceType(Class<T> type) {}
  }
  // Variable definition may be modified
  static LoadingCache<Class<? extends Resource<?>>, ResourceType<? extends Resource<?>>> cache = CacheBuilder.newBuilder().build(
      new CacheLoader<Class<? extends Resource<?>>, ResourceType<? extends Resource<?>>>() {
        @Override public ResourceType<? extends Resource<?>> load(Class<? extends Resource<?>> key) throws Exception {
          return new ResourceType<? extends Resource<?>>(key);
        }
    });
  // Method definition can't be modified, method content may.
  @SuppressWarnings("unchecked")
  static <T extends Resource<T>> ResourceType<T> getResourceType(Class<T> type) {
    return (ResourceType<T>)cache.getUnchecked(type);
  }
}

编译失败的行是:

return new ResourceType<? extends Resource<?>>(key);

我知道为什么它失败了:我可能不写带问号的new Xxxx<...> (?)。我只是不能用不同的方式写这行来让其他行编译。

Resource没有泛型的情况下,我有一个后备解决方案,但在可能的限制下,我想保持Resource具有泛型。

我对LoadingCache的泛型没有任何限制,除了我需要像getResourceType(Class)一样调用它。

所以…我如何修复这个代码?

带有一些被忽略的警告的解决方案:

  static LoadingCache<Class<? extends Resource<?>>, ResourceType<? extends Resource<?>>> cache = CacheBuilder.newBuilder().build(
      new CacheLoader<Class<? extends Resource<?>>, ResourceType<? extends Resource<?>>>() {
        @SuppressWarnings({ "rawtypes", "unchecked" })
        @Override public ResourceType<? extends Resource<?>> load(Class<? extends Resource<?>> key) throws Exception {
          return new ResourceType(key);
        }
    });

正如@John B在评论中建议的。由于类型擦除,该代码在运行时与问题中的代码没有什么不同。

我玩了更多关于gontard的解决方案(所以如果你给这个投票,一定要给gontard的答案投票)和millimoose的评论。我采用了这个解决方案,它完全删除了泛型,以获得更具可读性的代码。

@SuppressWarnings("rawtypes")
static LoadingCache<Class, ResourceType> cache = CacheBuilder.newBuilder().build(
    new CacheLoader<Class, ResourceType>() {
      @SuppressWarnings("unchecked")
      @Override public ResourceType load(Class key) throws Exception {
        return new ResourceType(key);
      }
  });

如果我们可以将key转换为合适的类型

Class<T> for some T that T<:Resource<T>

那么我们写

就没有问题了
ResourceType<? extends Resource<?>> result = new ResourceType<>(casted_key)

通常这种类型的强制转换可以通过通配符捕获来完成,但是如果类型变量有一个自引用绑定,它就不起作用了。请参阅泛型和Class>, EnumSet.allOf(class) vs class. getenumconstants()。

最新更新