无界通配符类型 List<> 和原始类型 List 之间有什么区别?
你能帮我理解无界通配符类型列表和原始类型列表之间的区别吗?
List<?> b; // unbounded wildcard type
List a; // raw type
除此之外,任何人都可以帮助我理解什么是有界类型参数列表?
List<E extends Number> c;
你能帮我理解无界通配符类型列表和原始类型列表之间的区别吗?
List<?> b; // unbounded wildcard type
List a; // raw type
除此之外,任何人都可以帮助我理解什么是有界类型参数列表?
List<E extends Number> c;
以下是这三者的摘要:
List
:没有类型参数的列表。它是一个列表,其元素是任何类型 - 元素可以是不同类型的。
List<?>
:具有无界类型参数的列表。其元素属于特定但未知的类型;元素必须都是相同的类型。
List<T extends E>
:具有名为 . 的类型参数的列表。提供的 type 必须是扩展的类型,或者它不是参数的有效类型。T
T
E
你应该真正看看有效的Java,第23项:不要在新代码中使用原始类型。
要使用该书中的示例,请考虑以下示例...如果你有一个集合,你不关心其中的元素类型,该怎么办?例如,您希望查看两个集合之间有多少个共同的元素。您可能会想到以下内容:
public static int numElementsInCommon(Set s1, Set s2) {
int result = 0;
for (Object o : s1) {
if (s2.contains(o)) {
++result;
}
}
return result;
}
此示例虽然有效,但由于使用了原始类型,因此不是一个好主意。原始类型根本不是类型安全的...您最终可能会以非类型安全的方式修改集合并损坏程序。相反,请谨慎行事,并使用类型安全的替代方法:
public static int numElementsInCommon(Set<?> s1, Set<?> s2) {
int result = 0;
for (Object o : s1) {
if (s2.contains(o)) {
++result;
}
}
return result;
}
不同之处在于,您只能添加到 中,并且不能对从 中取出的元素进行任何假设。如果您使用 raw ,则可以向其添加任何您想要的内容。该方法是一个很好的例子,您甚至不需要添加任何内容,也不需要对集合中的内容进行任何假设。这就是为什么它是使用通配符的良好候选者。null
Set<?>
Set<?>
Set
numElementsInCommon
?
希望这有帮助。在有效的Java中阅读整个项目,它将真正变得清晰。
要回答问题的第二部分...还记得我说过当你使用通配符时,你不能对你从集合中取出的元素做任何假设吗?如果您确实需要对从集合中删除的对象的接口进行假设,该怎么办?例如,假设您要跟踪一组内容。?
Cool
public interface Cool {
// Reports why the object is cool
void cool();
}
然后,您可能有一些这样的代码:
public static void reportCoolness(Set s) {
for (Object item : s) {
Cool coolItem = (Cool) item;
coolItem.cool();
}
}
这不是类型安全...你需要确保你传递了一个只有对象的集合。要修复它,你可以说:Cool
public static void reportCoolness(Set<Cool> s) {
for (Cool coolItem : s) {
coolItem.cool();
}
}
这太棒了!完全符合您的要求,并且类型安全。但是,如果以后你有这个:
public interface ReallyCool extends Cool {
// Reports why the object is beyond cool
void reallyCool();
}
由于所有对象都是 ,您应该能够执行以下操作:ReallyCool
Cool
Set<ReallyCool> s = new HashSet<ReallyCool>();
// populate s
reportCoolness(s);
但是你不能这样做,因为泛型具有以下属性:假设是 的子类,那么不是 的子类。这方面的技术讨论是“泛型类型是不变的”。(与协变相反)。B
A
Set<B>
Set<A>
要使最后一个示例正常工作,您需要创建一个通过强制转换(安全地)在 .为了避免让 API 的客户端通过这些令人讨厌的、不必要的代码,你可以让方法更灵活,如下所示:Set<Cool>
Set<ReallyCool>
reportCoolness
public static void reportCoolness(Set<? extends Cool> s) {
for (Cool coolItem : s) {
coolItem.cool();
}
}
现在,您的方法采用任何包含 的元素或 的任何子类。所有这些类型都符合api...因此,我们可以安全地在任何元素上调用该方法Set
Cool
Cool
Cool
cool()
有意义?希望这有帮助。