我对Java泛型中通配符的使用有疑问: List<? extends Set>
和 List<T extends Set>
之间的基本区别是什么?在何时会使用它们?
我对Java泛型中通配符的使用有疑问: List<? extends Set>
和 List<T extends Set>
之间的基本区别是什么?在何时会使用它们?
有两个原因:
避免不必要的强制类型转换:
对于这种情况,您必须使用 T
变体:
public <T extends Set> T firstOf(List<T> l) {
return l.get(0);
}
使用 ?
,这将变为:
public Set firstOf2(List<? extends Set> l) {
return l.get(0);
}
...这并不能给firstOf
方法的调用者提供相同数量的信息。第一个版本允许调用者执行以下操作:
SubSet first = firstOf(listOfSubSet);
SubSet first = (SubSet)firstOf(listOfSubSet);
强制匹配参数类型:
public <T extends Set> boolean compareSets(List<T> a, List<T> b) {
boolean same = true;
for(T at : a) {
for (T bt: b) {
same &= at.equals(bt);
}
}
return same;
}
在这种情况下,没有直接相当于使用 ?
替换 T
的方法。请注意,由于 Java 的单分派,在上面的版本中,编译器将调用 at
的 equals(T)
方法,这可能与 at
的 equals(Set)
或 equals(Object)
方法不同。
Set
而不是T extends Set
的方法版本在其合同中没有公开返回给定List
包含的相同类型的信息,这更普遍地意味着您不能依赖于该信息是正确的。 - ColinDSet
没有compare
方法,因此很难理解您在第二个示例中的意思。如果要向Set
添加compare
方法以完成您的示例,则它们的签名会是什么样子?(确保在展示代码之前进行编译) - irreputablea
和b
被声明为List<? extends Set>
,那么equals(Set)
方法将被调用。 - irreputable这里的区别在于第二个版本中,你有一个类型变量T
,它指的是List
包含的特定子类型Set
。在需要确保其他内容与列表中包含的类型相同的情况下,您需要这个变量。以下是一些简单的例子:
// want to ensure that the method returns the same type contained in the list
public <T extends Set> T something(List<T> list) {
...
}
// want to ensure both lists contain the exact same type
public <T extends Set> List<T> somethingElse(List<T> first, List<T> second) {
...
}
简单规则:如果同一类型在两个地方都需要使用,请在方法签名中使用类型变量T extends Foo
。方法参数是其中一个地方,方法返回类型是另一个地方。如果您只需要确保在一个地方处理“某些东西是Foo
”,请使用通配符? extends Foo
。
另外:不要使用原始类型Set
。
我们使用通配符来指定类型元素匹配任何内容。 ?
代表未知类型。
List<? extends Set>
是一个有界通配符的示例,它表示列表可以接受 任何 Set
的子类型(例如 HashSet
)。
List<T extends Set>
则允许将 T
限制 为扩展 Set
的类型。
当我需要一个数据集合而不考虑其确切类型时,我会使用通配符。
null
可以用于带通配符? extends Set
的参数)。你也不能创建一个new ArrayList<? extends Set>()
。 - ColinD在声明变量时,您可以使用 List<? extends Set>
。例如:
List<? extends Number> l = new ArrayList<Integer>();
List<T extends Number>
可以在类或方法声明中使用。这将允许您在函数中稍后写 T 而不是 <? extends Number>
。
public <T extends Number> int addAll(List<T> list) {
int result = 0;
for (T t : list) {
result += t.intValue();
}
return result;
}
new ArrayList<Integer>()
时,基本上不应该声明一个类型为 List<? extends Number>
的变量。只需将其分配给List<Integer>
即可。通配符用于方法参数,以确保您可以在任何想要使用List<Integer>
的地方使用它。 - ColinDaddAll
方法可以很好地处理类型为 List<? extends Number>
的参数(你实际上根本没有使用 T
),因此这不是一个好的例子。 - ColinDT extends Object & Comparable<? super T>
时,T才变得有用。在类中使用有界通配符也更为常见,List被声明为public interface List<E> extends Collection<E>
,并且具有各种以E作为参数或返回类型的函数E get(int index);
。我现在真的想不出一个好的1个方法的例子了。 - Dorus通配符类型 G<? extends A>
是任何 G<Ai>
的超类型,其中 Ai
是 A
的子类型。
换句话说,G<? extends A>
是 G<A0>, ..., G<An>
的联合类型。
T extends A
和 ? extends A
之间的区别以及何时使用其中之一,而不是首先解释 ? extends A
是什么。 - blubb<?>
,并且这几乎引发了本文所讨论的所有议题。 - blubb
List
之间有什么区别... - BlindyList<T extends Set>
不是有效的语法。 - newacct