以下2行有什么区别?
public static <T extends Comparable<? super T>> int methodX(List<T> data)
public static <T> int methodX(List<? extends Comparable<? super T>> data)
答案 0 :(得分:2)
您的第一个选择是“更严格”的参数化。这意味着,您要使用一系列限制来定义类T
,然后在List
中使用它。在第二种方法中,参数类T
是通用的,没有条件,List
的类参数是根据参数T
定义的。
第二种方式在语法上也是不同的,使用?
而不是第一个选项的T
,因为在参数定义中你没有定义类型参数T
而是使用它,所以第二种方法不能具体。
由此产生的实际差异是继承。您的第一种方法需要是一种与超类 本身相似的类型 ,而第二种类型只需要与无条件/无关T
相媲美:
public class Person implements Comparable<Number> {
@Override
public int compareTo(Number o) {
return 0;
}
public static <T extends Comparable<? super T>> int methodX(List<T> data) {
return 0;
}
public static <T> int methodY(List<? extends Comparable<? super T>> data) {
return 0;
}
public static void main(String[] args) {
methodX(new ArrayList<Person>()); // stricter ==> compilation error
methodY<Object>(new ArrayList<Person>());
}
}
如果您更改Comparable
Person
以便能够比较Object
或Person
(基类的继承树),那么methodX
将也工作。
答案 1 :(得分:1)
对于来电者,第二个版本大致相当于
public static <T, X extends Comparable<? super T>> int methodX(List<X> data)
假设调用者使用具体类型为List<Foo>
的arg调用它。类型推断将得出结论X=Foo
。然后我们从X
的绑定
=>
Foo <: Comparable<? super T>
(A <: B
表示A是B的子类型
如果Foo
完全可比,那几乎可以肯定implements Comparable<Foo>
[2]
=>
Comparable<Foo> <: Comparable<? super T>
=>
T <: Foo
如果没有进一步的信息,推理会选择T=Foo
。
因此,从来电者的POV来看,这两个版本并没有真正的不同。
在方法体内,第二个版本无法访问类型参数X
,这是在编译阶段引入的合成参数。这意味着您只能阅读data
。像
X x = data.get(0);
data.set(1, x);
在版本#2中是不可能的; #{1}}版本#1中没有此类问题。
但是我们可以将#2转发到#1
T
这是因为对于编译器,<T1> method1(List<T1> data){ data.set(...); }
<T2> method2(List<?...> data)
{
method1(data);
}
(they must have difference method names; overloading not allowed since java7)
的类型确实是data
(它知道秘密List<X>
),因此在推断X
之后调用method1(data)
没有问题T1=X
[1] JLS3,5.1.10捕获转换
[2]根据Comparable
的javadoc,此接口对实现它的每个类的对象施加了一个总排序。这意味着如果Foo implements Comparable<W>
,W必须是Foo或超级Foo。对于子类实现来说,定义超类的对象之间的总顺序是不太可能的。所以W
绝对应该是Foo
。否则会发生有趣的事情。臭名昭着的例子是'Timestamp',它的javadoc(现在)解释了为什么它不能与它的超类型Date
进行比较
答案 2 :(得分:0)
第一种方法需要一个可以与自己的类或其超类型进行比较的元素列表。比方说,实数可以与任何类型的数字进行比较:
class Real extends Number implements Comparable<Number> {
public int compareTo(Number o) ...
}
更具限制性,但仍然可以接受第一种方法:
class Real extends Number implements Comparable<Real> {
public int compareTo(Real o) ...
}
但第二种方法实际上与此版本没有太大区别:
public static int methodY(List<? extends Comparable<?>> data) ...
也就是说,您可以将T
替换为未命名的通配符?
,因为它仅在方法签名中使用一次。它不使用同一个类或对象的自己的类等概念。