Java 中的泛型 - 通配符用例
Generics in Java - Wildcard Usecases
当我们想要传递包含子 class 对象的列表时,我们在方法参数中使用通配符。但如下所示,我们可以使用 Type 参数实现相同的功能。那么为什么我们需要通配符?
场景
假设我们有 base-class 命名为 Department 及其 sub-classes 命名为 开发 & 销售。
Development & Sales 是 Department 的子类型,但 List & List 不是 List 的子类型。
因此,当我们想要将 Development 或 Sales 对象的列表作为方法 arg 传递时,我们使用通配符来接受任何类型的 Department 列表。但是在代码中我们可以看到我们可以在不使用通配符的情况下实现相同的效果。
根据 Effective Java 在 return 类型中使用通配符确实是一个糟糕的选择。通配符真正有用的其他用例是什么?
class MyGenericsClass<T extends SuperClass> {
//These 2 methods works the same
<H> void unboundedMethod1(AnotherGenericClass<H> arg) {
}
void unboundedMethod2(AnotherGenericClass<?> arg) {
}
//These two methods works the same
<H extends SuperClass> void boundedMethod1(AnotherGenericClass<H> arg) {
}
void boundedMethod2(AnotherGenericClass<? extends SuperClass> arg) {
}
//These two methods works the same
<U extends Building> void boundedListMethod1(List<U> list){
}
void boundedListMethod2(List<? extends Building> list) {
}
//Why can't we write like this? Giving Compile time error
//<U> void notWorkingMethod1(List<U extends SuperClass> list){ //Statements }
//<T> void notWorkingMethod2(List<U extends SuperClass> list){ //Statements }
}
在 notWorkingMethods1 和 notWorkingMethods2 中,为什么我们不能直接传递 Bounded Type 参数,但我们可以通过在 return type 之前先声明它来做到这一点?
首先,您最初假设这两种方法的行为相同是不正确的。
假设通用 类 是列表。尝试将 H
类型的内容添加到 List<H>
中,并将任何内容添加到 List<?>
中,看看它们的行为是否相同。
关于最后一个问题。
<U extends Building> void boundedListMethod1(List<U> list)
表示 U 是扩展 Building 的类型,而 List 包含该类型。
然而,
<U> void notWorkingMethod1(List<U extends Building> list)
表示存在某种类型 U
和一个需要类型 U that extends Building
的列表。这两个陈述并不意味着兼容性。 U 可能不是 Building 的子类,但 List 需要它。
当您只想做某事而不考虑类型时,通配符很有用。
List<Map<String,Integer>> list = ....
for (Map<?,?> m : list) {
System.out.println(m);
}
它们对于复制类型也很有用。
public <H> void copy(List<? extends H> src, List<? super H> dst) {
for (H a : src) {
dst.add(a);
}
}
当我们想要传递包含子 class 对象的列表时,我们在方法参数中使用通配符。但如下所示,我们可以使用 Type 参数实现相同的功能。那么为什么我们需要通配符?
场景
假设我们有 base-class 命名为 Department 及其 sub-classes 命名为 开发 & 销售。
Development & Sales 是 Department 的子类型,但 List & List 不是 List 的子类型。
因此,当我们想要将 Development 或 Sales 对象的列表作为方法 arg 传递时,我们使用通配符来接受任何类型的 Department 列表。但是在代码中我们可以看到我们可以在不使用通配符的情况下实现相同的效果。
根据 Effective Java 在 return 类型中使用通配符确实是一个糟糕的选择。通配符真正有用的其他用例是什么?
class MyGenericsClass<T extends SuperClass> {
//These 2 methods works the same
<H> void unboundedMethod1(AnotherGenericClass<H> arg) {
}
void unboundedMethod2(AnotherGenericClass<?> arg) {
}
//These two methods works the same
<H extends SuperClass> void boundedMethod1(AnotherGenericClass<H> arg) {
}
void boundedMethod2(AnotherGenericClass<? extends SuperClass> arg) {
}
//These two methods works the same
<U extends Building> void boundedListMethod1(List<U> list){
}
void boundedListMethod2(List<? extends Building> list) {
}
//Why can't we write like this? Giving Compile time error
//<U> void notWorkingMethod1(List<U extends SuperClass> list){ //Statements }
//<T> void notWorkingMethod2(List<U extends SuperClass> list){ //Statements }
}
在 notWorkingMethods1 和 notWorkingMethods2 中,为什么我们不能直接传递 Bounded Type 参数,但我们可以通过在 return type 之前先声明它来做到这一点?
首先,您最初假设这两种方法的行为相同是不正确的。
假设通用 类 是列表。尝试将 H
类型的内容添加到 List<H>
中,并将任何内容添加到 List<?>
中,看看它们的行为是否相同。
关于最后一个问题。
<U extends Building> void boundedListMethod1(List<U> list)
表示 U 是扩展 Building 的类型,而 List 包含该类型。
然而,
<U> void notWorkingMethod1(List<U extends Building> list)
表示存在某种类型 U
和一个需要类型 U that extends Building
的列表。这两个陈述并不意味着兼容性。 U 可能不是 Building 的子类,但 List 需要它。
当您只想做某事而不考虑类型时,通配符很有用。
List<Map<String,Integer>> list = ....
for (Map<?,?> m : list) {
System.out.println(m);
}
它们对于复制类型也很有用。
public <H> void copy(List<? extends H> src, List<? super H> dst) {
for (H a : src) {
dst.add(a);
}
}