在使用 Java™ 语言的泛型时,通配符非常令人困惑,并且最常见的一个错误就是在使用有界通配符的两种形式的其中之一(“
?
” 和 “
super T? extends T
”)时出现错误。您出错了吗?别沮丧,即使是专家也会犯这种错误,本月
Brian Goetz 将展示如何避免这个错误。
在 Java 语言中,数组是协变的(因为一个 Integer
同时也是一个 Number
,一个
Integer
数组同时也是一个 Number
数组),但是泛型不是这样的(List<Integer>
并不等于
List<Number>
)。人们会争论哪些选择是 “正确的”,哪些选择是 “错误的” — 当然,每种选择都各有优缺点 —
但有一点毫无疑问,存在两种使用差别很小的语义构造派生类型的类似机制,这将导致大量错误和误解。
有界通配符(一些有趣的 “? extends T
” 通用类型说明符)是语言提供的一种工具,用来处理协变性缺乏 —
有界通配符允许类声明方法参数或返回值何时具有协变性(或相反,声明方法参数或返回值何时具有逆变性(contravariant))。虽然了解何时使用有界通配符是泛型较为复杂的方面,但是,使用有界通配符的压力通常都落在库作者的身上,而非库用户。最常见的有界通配符错误就是忘记使用它们,这就限制了类的使用,或是强制用户不得不重用现有的类。
让我们从一个简单的泛型类开始(一个称为 Box
的值容器),它持有一个具有已知类型的值:
public interface Box<T> { |
由于泛型不具备协变性,Box<Integer>
并不等同于
Box<Number>
,尽管 Integer
属于
Number
。但是对于 Box
这样的简单泛型类来说,这不成问题,并且常常被忽略,因为
Box<T>
的接口完全指定为 T 类型的变量 — 而不是通过 T 泛型化的类型。直接处理类型变量允许实现多态性。清单 1
展示了这种多态性的两个示例:获取 Box<Integer>
的内容,并将它作为一个
Number
,然后将一个 Integer
放入 Box<Number>
中:
|
通过使用简单的 Box
类,使我们确信可以没有协变性,因为....
本文转自IBM Developerworks中国