来自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
方法?
答案 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的首席开发人员。