您能帮我理解无界通配符类型列表和原始类型列表之间的区别吗?
List<?> b; // unbounded wildcard type
List a; // raw type
除此之外,任何人都可以帮助我理解什么是有界类型参数列表?
List<E extends Number> c;
答案 0 :(得分:32)
以下是三者的总结:
List
:没有类型参数的列表。它是一个列表,其元素可以是任何类型 - 元素可以是不同类型的。
List<?>
:具有无界类型参数的列表。它的元素是特定的,但未知的类型; 元素必须都是相同的类型。
List<T extends E>
:一个名为T
的类型参数的列表。 T
提供的类型必须是扩展E
的类型,或者不是参数的有效类型。
答案 1 :(得分:19)
你应该看看Effective 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;
}
不同之处在于,您只能将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>
的子类。对此的技术讨论是“通用类型是不变的”。 (与协变相反)。
要使最后一个示例起作用,您需要通过(安全地)转换Set<Cool>
中的每个元素来创建Set<ReallyCool>
。为了避免让你的api的客户经历这个讨厌的,不必要的代码,你可以让reportCoolness
方法更加灵活:
public static void reportCoolness(Set<? extends Cool> s) {
for (Cool coolItem : s) {
coolItem.cool();
}
}
现在,您的方法会使用包含Set
元素或Cool
的任何子类的任何Cool
。所有这些类型都遵循Cool
API ...因此我们可以安全地在任何元素上调用cool()
方法
有意义吗?希望这会有所帮助。
答案 2 :(得分:4)
关于第一个问题,List
和List<?>
之间的差异:
两者之间的一个显着区别是,当您使用通配符作为类型时,Collection
的类型是未知的,因此add
方法将引发编译时错误。
您仍然可以从List<?>
中获取值,但您需要显式转换。
答案 3 :(得分:0)
无界通配符类型列表和原始类型列表之间的区别。
两种情况下,我们都可以将任何类型的列表放入此变量中:
List nothing1 = new ArrayList<String>();
List nothing2 = new ArrayList();
List nothing3 = new ArrayList<>();
List nothing4 = new ArrayList<Integer>();
List<?> wildcard1 = new ArrayList<String>();
List<?> wildcard2 = new ArrayList();
List<?> wildcard3 = new ArrayList<>();
List<?> wildcard4 = new ArrayList<Integer>();
但是我们可以将哪些元素放入此对象中?
我们只能将String放入List<String>
:
List<String> strings = new ArrayList<>();
strings.add("A new string");
我们可以将任何对象放入列表:
List nothing = new ArrayList<>();
nothing.add("A new string");
nothing.add(1);
nothing.add(new Object());
除了{em> null ,我们什么也不能放入List<?>
!因为我们使用泛型。而且Java知道它的类型是List,但不知道它是什么类型。而且不要让我们犯错。
结论:List<?>
是通用列表,为我们提供了类型安全性。
P.S。切勿在代码中使用原始类型。