Java 仿制药 - 什么时候?需要通配符
Java generics - When is ? wildcard needed
假设你有一些 class 类型 T
:
class MyClass<T extends SomeOtherClass> {
....
}
现在,您想将此 class 的实例存储到一个集合中,但您并不真正关心类型。我会表达如下:
private final List<MyClass> entries = new ArrayList<>();
有什么好的reason/advantage改写下面的吗?
private final List<MyClass<?>> entries = new ArrayList<>();
甚至:
private final List<MyClass<? extends SomeOtherClass> entries = new ArrayList<>();
我自己只能找到一个不好的理由这样做:每当 MyClass
的类型定义发生变化(例如添加另一种类型)时,您必须更改 List<MyClass<?>>
或 List<MyClass<? extends SomeOtherClass>>
定义遍布你的代码。
更新
更新我的问题:
为什么编译器无法在您编写 List<MyClass>
(甚至 List<MyClass<? extends SomeOtherClass>>
)时跟踪 MyClass
的类型?他知道 MyClass
被定义为 MyClass<T extends SomeOtherClass>
,所以当你写 List<MyClass>
时他 able/allowed 为什么不这样做?
换句话说,为什么List<MyClass>
不等于List<MyClass<?>>
(甚至List<MyClass<? extends SomeOtherClass>>
)?编译器拥有自己得出结论的所有信息,afaik。
你的第一个声明意味着,你没有为未来的反射系统提供任何关于你的通用数据的信息,这些信息可以被为你的主程序编写的插件使用。
第二个声明告诉他们,该字段包含一个对象泛型。
第三个更具体,这意味着反射系统知道这个字段的详细信息。
?
通配符在您不需要再次引用类型时非常有用,因此您不需要创建特定的标识符。
您的第一个片段以 class MyClass<T extends SomeOtherClass>
开头。当 T
稍后很重要时,这是必要的,可能声明字段参数或 return 类型。例如:
class MyClass<T extends SomeOtherClass> {
private final List<T> aListOfT;
public T getSomething() {
return this.aListOfT.get(0);
}
由于 MyClass
是一个泛型类型,所有对它的引用都应该被限定以避免可避免的运行时错误。因此,当您声明 List<MyClass>
时,您会收到一个编译器警告,提示您使用原始类型 MyClass
。如果您不关心代码中的那个位置 MyClass
限定了哪种类型,那么您可以使用 ?
告诉编译器您不关心并让它跟踪类型并检查所有操作的有效性。
使用第一种类型,java 将假定泛型类型为 Object。这样做的原因是,泛型是在 1.5 版中引入的。在此之前,collection-类 将所有内容都存储为一个对象。出于兼容性原因,不提供通用参数意味着您正在使用对象。
第二种就是说你不知道或者不关心它是什么类型。编译代码时会保留此信息。所以其他可能使用你的代码的程序员会知道你不在乎。
由于java的类型擦除,这两者在运行时没有区别。
最后一个表格你说:
我不在乎它是什么,但它必须是 SomeOtherClass 或派生类型。与以下内容相同:
List<MyClass<SomeOtherClass>>
你也可以反过来做:
List<MyClass<? super SomeOtherClass>>
表示您不关心它是什么类型,除非它是 SomeOtherClass 的超类型。
假设你有一些 class 类型 T
:
class MyClass<T extends SomeOtherClass> {
....
}
现在,您想将此 class 的实例存储到一个集合中,但您并不真正关心类型。我会表达如下:
private final List<MyClass> entries = new ArrayList<>();
有什么好的reason/advantage改写下面的吗?
private final List<MyClass<?>> entries = new ArrayList<>();
甚至:
private final List<MyClass<? extends SomeOtherClass> entries = new ArrayList<>();
我自己只能找到一个不好的理由这样做:每当 MyClass
的类型定义发生变化(例如添加另一种类型)时,您必须更改 List<MyClass<?>>
或 List<MyClass<? extends SomeOtherClass>>
定义遍布你的代码。
更新
更新我的问题:
为什么编译器无法在您编写 List<MyClass>
(甚至 List<MyClass<? extends SomeOtherClass>>
)时跟踪 MyClass
的类型?他知道 MyClass
被定义为 MyClass<T extends SomeOtherClass>
,所以当你写 List<MyClass>
时他 able/allowed 为什么不这样做?
换句话说,为什么List<MyClass>
不等于List<MyClass<?>>
(甚至List<MyClass<? extends SomeOtherClass>>
)?编译器拥有自己得出结论的所有信息,afaik。
你的第一个声明意味着,你没有为未来的反射系统提供任何关于你的通用数据的信息,这些信息可以被为你的主程序编写的插件使用。
第二个声明告诉他们,该字段包含一个对象泛型。
第三个更具体,这意味着反射系统知道这个字段的详细信息。
?
通配符在您不需要再次引用类型时非常有用,因此您不需要创建特定的标识符。
您的第一个片段以 class MyClass<T extends SomeOtherClass>
开头。当 T
稍后很重要时,这是必要的,可能声明字段参数或 return 类型。例如:
class MyClass<T extends SomeOtherClass> {
private final List<T> aListOfT;
public T getSomething() {
return this.aListOfT.get(0);
}
由于 MyClass
是一个泛型类型,所有对它的引用都应该被限定以避免可避免的运行时错误。因此,当您声明 List<MyClass>
时,您会收到一个编译器警告,提示您使用原始类型 MyClass
。如果您不关心代码中的那个位置 MyClass
限定了哪种类型,那么您可以使用 ?
告诉编译器您不关心并让它跟踪类型并检查所有操作的有效性。
使用第一种类型,java 将假定泛型类型为 Object。这样做的原因是,泛型是在 1.5 版中引入的。在此之前,collection-类 将所有内容都存储为一个对象。出于兼容性原因,不提供通用参数意味着您正在使用对象。
第二种就是说你不知道或者不关心它是什么类型。编译代码时会保留此信息。所以其他可能使用你的代码的程序员会知道你不在乎。
由于java的类型擦除,这两者在运行时没有区别。
最后一个表格你说: 我不在乎它是什么,但它必须是 SomeOtherClass 或派生类型。与以下内容相同:
List<MyClass<SomeOtherClass>>
你也可以反过来做:
List<MyClass<? super SomeOtherClass>>
表示您不关心它是什么类型,除非它是 SomeOtherClass 的超类型。