从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
作为参数类型有什么好处?为什么他们在Cachet的@CompatibleWith
中使用getIfPresent
批注,却未在Cachet的get
方法中使用? 最佳答案
对于getIfPresent
操作,允许“太宽”类型的对象是安全的(通过getIfPresent(42)
的字符串键,您不会从缓存中得到任何东西)。另一方面,对于允许插入的假想get(Object, Callable)
,错误类型的对象(例如42
而不是字符串"foo"
)会损坏基础集合,这就是为什么编译时检查不允许这样做。
话虽如此,这段代码:
Cache<String, Foo> cache = CacheBuilder.newBuilder()
// and later
Foo value = cache.getIfPresent(42);
这很可能是错误的,对于像“容易出错”这样的框架,将其表示为可能的错误是有意义的。
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()
不会。结论也很重要:
静态分析在构建无错误的软件中扮演着极其重要的角色。
这是有道理的,因为作者Kevin Bourrillion也是Guava的首席开发人员。