List和List<之间有什么区别?>, List<T>, List<E>, List<Object>?
1. 列表
List:是一个原始类型,因此不是类型安全的。它只会在强制转换错误时生成运行时错误。当强制转换错误时,我们希望出现编译时错误。不建议使用。
2. 列表< ?>
列表< ?>是一个无界通配符。但我不确定这是干什么用的?我可以打印一个List<?>无问题:
public static void test(List<?> list){
System.out.println(list); // Works
}
为什么我不能向List<?>?
public static void test(List<?> list){
list.add(new Long(2)); // Error
list.add("2"); // Error
System.out.println(list);
}
3. 目前居住;rolelabel T >
public static void test(List<T> list){ // T cannot be resolved
System.out.println(list);
}
我不明白这个语法。我看到了类似这样的东西,它起作用了:
public <T> T[] toArray(T[] a){
return a;
}
有时,我看到<T>,或<E>,或<U>, <T,E>。它们都是一样的还是代表着不同的东西?
4. List <物体>
这给出了错误“the method test(List<Object>) is not applied for the argument List<String>”:
public static void test(List<Object> list){
System.out.println(list);
}
如果我尝试这样做,那么我得到“不能从列表<字符串>到列表<对象>”:
test((List<Object>) names);
我很困惑。字符串是对象的子类,所以为什么List<String>不是List<Object>的子类?
在你的第三点,“T”不能解析,因为它没有声明,通常当你声明一个泛型类时,你可以使用“T”作为绑定类型参数的名称,许多在线示例包括oracle的教程使用“T”作为类型参数的名称,例如,你声明一个类:
public class FooHandler<T>
{
public void operateOnFoo(T foo) { /*some foo handling code here*/}
}
你是说,FooHandler的operateOnFoo方法期望一个类型为“T”的变量在类声明本身声明,考虑到这一点,你可以稍后添加另一个方法,如
public void operateOnFoos(List<T> foos)
在所有情况下,T, E或U都是类型参数的标识符,你甚至可以有多个使用该语法的类型参数
public class MyClass<Atype,AnotherType> {}
在你的第四点,虽然实际上Sting是对象的子类型,在泛型类中没有这样的关系,List<String>不是List<Object>的子类型,从编译器的角度来看,它们是两种不同的类型,这是最好的解释在这个博客条目
理论
字符串[]可以转换为对象[]
but
List<String>不能强制转换为List<Object>。
实践
对于列表来说,情况要微妙得多,因为在编译时传递给方法的List参数的类型不会被检查。方法定义也可以写成List<?>——从编译器的角度来看,它是等价的。这就是OP示例#2给出运行时错误而不是编译错误的原因。
如果你小心地处理传递给方法的List<Object>参数,这样你就不会强制对列表的任何元素进行类型检查,那么你可以使用List<Object>定义你的方法,但实际上从调用代码中接受List<String>参数。
答:所以这段代码不会给出编译或运行时错误,并且实际上(也许令人惊讶?)可以工作:
public static void main(String[] args) {
List argsList = new ArrayList<String>();
argsList.addAll(Arrays.asList(args));
test(argsList); // The object passed here is a List<String>
}
public static void test(List<Object> set) {
List<Object> params = new ArrayList<>(); // This is a List<Object>
params.addAll(set); // Each String in set can be added to List<Object>
params.add(new Long(2)); // A Long can be added to List<Object>
System.out.println(params);
}
B.这段代码将给出一个运行时错误:
public static void main(String[] args) {
List argsList = new ArrayList<String>();
argsList.addAll(Arrays.asList(args));
test1(argsList);
test2(argsList);
}
public static void test1(List<Object> set) {
List<Object> params = set; // Surprise! Runtime error
}
public static void test2(List<Object> set) {
set.add(new Long(2)); // Also a runtime error
}
C.这段代码将给出一个运行时错误(java.lang。ArrayStoreException: java.util。收藏美元UnmodifiableRandomAccessList对象[]):
public static void main(String[] args) {
test(args);
}
public static void test(Object[] set) {
Object[] params = set; // This is OK even at runtime
params[0] = new Long(2); // Surprise! Runtime error
}
在B中,参数集在编译时不是类型化的List:编译器将其视为List<?>。这是一个运行时错误,因为在运行时,set变成了从main()传递的实际对象,这是一个List<String>。List<String>不能强制转换为List<Object>。
在C语言中,参数集需要一个Object[]。当它以String[]对象作为参数调用时,没有编译错误和运行时错误。这是因为String[]强制转换为Object[]。但是test()接收到的实际对象仍然是String[],它没有改变。所以params对象也变成了String[]。String[]的元素0不能赋值给Long!
(希望我的一切都是正确的,如果我的推理是错误的,我相信社区会告诉我。UPDATED:我已经更新了示例A中的代码,以便它实际编译,同时仍然显示所做的重点。)
问题2没问题,因为“System.out.println(set);”意味着“System.out.println(set. tostring());”set是List的一个实例,因此编译器将调用List. tostring ();
public static void test(List<?> set){
set.add(new Long(2)); //--> Error
set.add("2"); //--> Error
System.out.println(set);
}
Element ? will not promise Long and String, so complier will not accept Long and String Object
public static void test(List<String> set){
set.add(new Long(2)); //--> Error
set.add("2"); //--> Work
System.out.println(set);
}
Element String promise it a String, so complier will accept String Object
问题3:这些符号是一样的,但是你可以给它们不同的规格。例如:
public <T extends Integer,E extends String> void p(T t, E e) {}
问题4:集合不允许类型参数协方差。但是数组允许协方差。