Bounded Type parameter (T extends) 和 Upper Bound Wildcard (? extends) 的区别
Difference between Bounded Type parameter (T extends) and Upper Bound Wildcard (? extends)
我知道已经发布了一个类似的问题,尽管我认为我的问题有些不同...
假设你有两种方法:
// Bounded type parameter
private static <T extends Number> void processList(List<T> someList) {
}
// Upper bound wildcard
private static void processList2(List<? extends Number> someList) {
// ...
}
据我所知,这两种方法都接受参数,即 Number
类型的 List
或 子类型 的 List
Number
。
但这两种方式到底有什么区别呢?
我能想到以下不同点:
a) 在方法中修改您的列表,考虑以下代码:
private static <T extends Number>void processList(List<T> someList)
{
T t = someList.get(0);
if ( t.getClass() == Integer.class )
{
Integer myNum = new Integer(4);
someList.add((T) myNum);
}
}
// Upper bound wildcard
private static void processList2(List<? extends Number> someList)
{
Object o = someList.get(0);
if ( o instanceof Integer )
{
Integer myNum = new Integer(4);
someList.add(myNum); // Compile time error !!
}
}
使用通配符无法向列表中添加元素!编译器告诉您它不知道 myNum
是什么。但是在第一种方法中,您可以通过首先检查 T
是否为 Integer
来添加 Integer
,没有编译时错误。
b) 第一种方法称为泛型方法。它遵循为 generic method 定义的语法。
方法定义中指定的上限用于限制参数类型。
第二个不一定称为泛型方法,它是一个普通方法,恰好接受泛型参数。
带有 extends
关键字的通配符 ?
用作 放宽 方法可以接受的类型的手段。
区别在于编译器方面。
在第一个上你可以使用类型(例如转换某些东西或将其用作绑定来调用另一个方法)而在第二个上你不能使用它。
编译时两种语法之间存在一些差异:
- 使用第一种语法,您可以向
someList
添加元素,但使用第二种语法则不能。这通常称为 PECS 而不太常见的是 PUT 和 GET 原则。
- 使用第一种语法,你有一个类型参数
T
的句柄,所以你可以用它来做一些事情,比如在类型 T
的方法中定义局部变量,转换引用到 T
类型,调用 T
表示的 class 中可用的方法,等等。但是使用第二种语法,您没有该类型的句柄,因此您可以做这些。
第一个方法其实可以从第二个方法调用到
捕获通配符。这是 capture 最常见的方式
通过辅助方法的通配符。
private static <T extends Number> void processList(List<T> someList) {
T n = someList.get(0);
someList.add(1,n); //addition allowed.
}
private static void processList2(List<? extends Number> someList) {
Number n = someList.get(0);
//someList.add(1,n);//Compilation error. Addition not allowed.
processList(someList);//Helper method for capturing the wildcard
}
请注意,由于泛型是编译时糖,因此更广泛层面上的这些差异仅限于编译。
如果您想使用类型信息,请使用 bounded。使用通配符时,参数将显示为通用对象,您将无法调用基于该类型的方法。
public static <T extends Object> ListIterator<T> createListIterator(ListIterator<T> o)
{
return new ListIteratorAdaptor<T>(o);
}
在JAVA中,通常与Generic一起使用的通配符有以下三种。每一个都在下面举例说明。
Upper-bounded 通配符:
? extends T :在上限通配符中,仅支持 T 或其子类型。
例如我们有一个 Animal class 并且有 Dog , Cat 作为它的子类型。所以遵循通用方法只会
接受 Data<Animal>, Data<Dog> and Data<Cat>
类型的参数
public static void add(Data<? extends Animal> animalData) {
}
Lower-bounded 通配符:
? super T :在 Lower-bounded 通配符中,仅支持 T 或其超类型。
我们用于定义 Lower-bounded 通配符的相同示例。假设我们有 Animal class 作为 super 或 parent class
和 Dog 作为它的 child class。现在下面的方法使用 Lower-bounded 通配符并且只接受类型
的参数
Data<Animal>, Data<Dog> and Data<Object>
public static void add(Data<? super Dog> animalData) {
}
无限通配符:
? : 无限通配符支持所有类型。所以我们上面的示例方法可以采用
类型的参数
Data<Animal>, Data<Dog> , Data<Object> and Data<Cat>
public static void add(Data<?> animalData) {
}
我知道已经发布了一个类似的问题,尽管我认为我的问题有些不同...
假设你有两种方法:
// Bounded type parameter
private static <T extends Number> void processList(List<T> someList) {
}
// Upper bound wildcard
private static void processList2(List<? extends Number> someList) {
// ...
}
据我所知,这两种方法都接受参数,即 Number
类型的 List
或 子类型 的 List
Number
。
但这两种方式到底有什么区别呢?
我能想到以下不同点:
a) 在方法中修改您的列表,考虑以下代码:
private static <T extends Number>void processList(List<T> someList)
{
T t = someList.get(0);
if ( t.getClass() == Integer.class )
{
Integer myNum = new Integer(4);
someList.add((T) myNum);
}
}
// Upper bound wildcard
private static void processList2(List<? extends Number> someList)
{
Object o = someList.get(0);
if ( o instanceof Integer )
{
Integer myNum = new Integer(4);
someList.add(myNum); // Compile time error !!
}
}
使用通配符无法向列表中添加元素!编译器告诉您它不知道 myNum
是什么。但是在第一种方法中,您可以通过首先检查 T
是否为 Integer
来添加 Integer
,没有编译时错误。
b) 第一种方法称为泛型方法。它遵循为 generic method 定义的语法。 方法定义中指定的上限用于限制参数类型。
第二个不一定称为泛型方法,它是一个普通方法,恰好接受泛型参数。
带有 extends
关键字的通配符 ?
用作 放宽 方法可以接受的类型的手段。
区别在于编译器方面。 在第一个上你可以使用类型(例如转换某些东西或将其用作绑定来调用另一个方法)而在第二个上你不能使用它。
编译时两种语法之间存在一些差异:
- 使用第一种语法,您可以向
someList
添加元素,但使用第二种语法则不能。这通常称为 PECS 而不太常见的是 PUT 和 GET 原则。 - 使用第一种语法,你有一个类型参数
T
的句柄,所以你可以用它来做一些事情,比如在类型T
的方法中定义局部变量,转换引用到T
类型,调用T
表示的 class 中可用的方法,等等。但是使用第二种语法,您没有该类型的句柄,因此您可以做这些。 第一个方法其实可以从第二个方法调用到 捕获通配符。这是 capture 最常见的方式 通过辅助方法的通配符。
private static <T extends Number> void processList(List<T> someList) { T n = someList.get(0); someList.add(1,n); //addition allowed. } private static void processList2(List<? extends Number> someList) { Number n = someList.get(0); //someList.add(1,n);//Compilation error. Addition not allowed. processList(someList);//Helper method for capturing the wildcard }
请注意,由于泛型是编译时糖,因此更广泛层面上的这些差异仅限于编译。
如果您想使用类型信息,请使用 bounded。使用通配符时,参数将显示为通用对象,您将无法调用基于该类型的方法。
public static <T extends Object> ListIterator<T> createListIterator(ListIterator<T> o)
{
return new ListIteratorAdaptor<T>(o);
}
在JAVA中,通常与Generic一起使用的通配符有以下三种。每一个都在下面举例说明。
Upper-bounded 通配符:
? extends T :在上限通配符中,仅支持 T 或其子类型。
例如我们有一个 Animal class 并且有 Dog , Cat 作为它的子类型。所以遵循通用方法只会
接受 Data<Animal>, Data<Dog> and Data<Cat>
public static void add(Data<? extends Animal> animalData) {
}
Lower-bounded 通配符:
? super T :在 Lower-bounded 通配符中,仅支持 T 或其超类型。 我们用于定义 Lower-bounded 通配符的相同示例。假设我们有 Animal class 作为 super 或 parent class 和 Dog 作为它的 child class。现在下面的方法使用 Lower-bounded 通配符并且只接受类型
的参数Data<Animal>, Data<Dog> and Data<Object>
public static void add(Data<? super Dog> animalData) {
}
无限通配符:
? : 无限通配符支持所有类型。所以我们上面的示例方法可以采用
类型的参数Data<Animal>, Data<Dog> , Data<Object> and Data<Cat>
public static void add(Data<?> animalData) {
}