来自Guava的@CompatibleWith注释的目的是什么?

时间:2018-03-29 10:42:25

标签: java guava

来自com.google.errorprone.annotations.CompatibleWith的文档:

  

声明方法的参数必须与其中一个类型参数“兼容”   方法的封闭类,或方法本身。 “兼容”意味着可以存在   “参考铸造转换”从一种类型到另一种类型(JLS 5.5.1)。

     

例如,Collection.contains(java.lang.Object)将注释如下:

interface Collection<E> {
    boolean contains(@CompatibleWith("E") Object o);
}
     

表示必须传递Collection.contains(java.lang.Object)的调用类型与Collection实例的泛型类型参数兼容的参数:

以下是com.google.common.cache.Cache的用法:

public interface Cache<K, V> {

    V getIfPresent(@CompatibleWith("K") Object key);

    V get(K key, Callable<? extends V> loader) throws ExecutionException;
...

@CompatibleWith("E") Object代替E作为参数的类型有什么好处?为什么他们使用@CompatibleWith中的getIfPresent注释而不是来自Cache的get方法?

1 个答案:

答案 0 :(得分:2)

getIfPresent操作允许“太宽”类型的对象是安全的(你没有从带有来自getIfPresent(42)的字符串键的缓存中获得任何内容)。另一方面,对于假设get(Object, Callable)允许插入错误类型的对象(例如。42而不是字符串"foo")会损坏底层集合,这就是为什么你有编译时间检查不允许它。

话虽如此,这段代码:

Cache<String, Foo> cache = CacheBuilder.newBuilder()
// and later
Foo value = cache.getIfPresent(42);

很可能是错误的,并且像Error Prone这样的框架将其视为可能的错误是有意义的。

this old, but still relevant blog post "Why does Set.contains() take an Object, not an E?"中解释了有关“在安全操作中使用对象而非通用类型”约定(不仅在Guava中使用,也在JDK集合框架中使用)的更详细说明:

  

为什么代码应该如下编译?

Set<Long> set = new HashSet<Long>();
set.add(10L);
if (set.contains(10)) {
  // we won't get here!
}
     

我们询问该集合是否包含整数十;这是一个“显而易见的”   错误,但编译器不会捕获它,因为Set.contains()接受   Object。这不是愚蠢和邪恶吗?

以后回答标题中的问题:

  

真正的区别在于add()在使用错误类型调用时会对集合造成“损害”,而contains()remove()则不能。

结论也很重要:

  

静态分析在构建无bug软件中起着极其重要的作用。

这是有道理的,因为作者Kevin Bourrillion也是Guava的首席开发人员。