List,List <?>,List <T>,List <E>和List <Object>之间的区别
List
, List<?>
, List<T>
, List<E>
和List<Object>
什么区别?
现在我不盲目地问这个问题,所以请不要closures这个线程。 让我先介绍一下基本代码:
private static List<String> names = new ArrayList<String>(); static { names.add("Tom"); names.add("Peter"); names.add("Michael"); names.add("Johnson"); names.add("Vlissides"); } public static void test(List<String> set){ System.out.println(set); } public static void main(String args[]){ test(names); }
我明白:
1. List
:是原始types,因此不是typesafe
。 只有在投射不良时才会产生运行时错误。 当演员阵容糟糕时,我们想要一个编译时错误。 不build议使用。
2. List<?>
:是一个无界的通配符。 但不知道这是为了什么? 所以如果我改变test
方法
public static void test(List<?> set){ System.out.println(set); }
它仍然运作良好。 如果你能解释这个用法,我将不胜感激。
编辑 :如果我这样做:
public static void test(List<?> set){ set.add(new Long(2)); //--> Error set.add("2"); //--> Error System.out.println(set); }
但如果我改变这个test
:
public static void test(List<String> set){ set.add(new Long(2)); //--> Error set.add("2"); //--> Work System.out.println(set); }
3. List<T>
:
public static void test(List<T> set){ //T cannot be resolved System.out.println(set); }
我想我不明白这个语法。 我看到这样的东西,它的工作原理:
public <T> T[] toArray(T[] a){ return a; }
请给我解释一下吗? 有时, 我看到<T>
或<E>
,或<U>
, <T,E>
。 他们都是一样的还是代表不同的东西?
4. List<Object>
public static void test(List<Object> set){ System.out.println(set); }
然后我得到了错误The method test(List<Object>) is not application for the argument List<String>
用于下面的代码The method test(List<Object>) is not application for the argument List<String>
。 我很困惑。 我以为String
是Object
一个子集?
public static void main(String args[]){ test(names); }
编辑:如果我试试这个
test((List<Object>)names);
那么我得到了Cannot cast from List<String> to List<Object>
1)正确
2)你可以把那个视为“只读”列表,在那里你不关心的项目的types。可以例如由一个返回列表的长度的方法使用。
3)T,E和U是一样的,但是人们倾向于使用例如T代表types,E代表元素,V代表价值,K代表密钥。 编译的方法说,它需要一个特定types的数组,并返回一个相同types的数组。
4)你不能混合橙子和苹果。 如果您可以将string列表传递给期望对象列表的方法,则可以将对象添加到string列表中。 (并不是所有的对象都是string)
最后一部分:虽然String是Object的子集,但List <String>不是从List <Object>inheritance的。
符号List<?>
意思是“一个东西的列表(但我不是说什么)”。 由于test
的代码适用于列表中的任何types的对象,因此这可以作为正式的方法参数。
使用types参数(就像你的第3点),要求声明types参数。 Java的语法是把<T>
放在函数的前面。 这与在方法主体中使用名称之前向方法声明正式的参数名称非常类似。
关于List<Object>
不接受List<String>
,这是合理的,因为String
不是Object
; 它是Object
一个子类。 解决的办法是声明public static void test(List<? extends Object> set) ...
但是extends Object
是多余的,因为每个类直接或间接地扩展了Object
。
你不能强制转换List<String>
到List<Object>
是它允许你违反List<String>
的约束。
想想下面的场景:如果我有一个List<String>
,它应该只包含String
types的对象。 (这是final
堂课)
如果我可以将它转换为List<Object>
,那么允许我将Object
添加到该列表,从而违反了List<String>
的原始合同。
因此,一般来说,如果C
类从类P
inheritance,那么不能说GenericType<C>
也是从GenericType<P>
inheritance的。
NB我已经在之前的回答中对此进行了评论,但是想要对其进行扩展。
我会build议阅读Java益智游戏。 它在声明中很好地解释了inheritance,generics,抽象和通配符。 http://www.javapuzzlers.com/
在你的第三点,“T”不能被parsing,因为它没有被声明,通常当你声明一个generics类时,你可以使用“T”作为绑定types参数的名字,很多在线例子包括oracle的教程使用“T”作为types参数的名称,例如,你声明一个类如:
public class FooHandler<T> { public void operateOnFoo(T foo) { /*some foo handling code here*/} }
你在说FooHandler's
operateOnFoo
方法需要一个types为“T”的variables,它是在类声明本身声明的,考虑到这一点,你可以稍后添加另一个方法
public void operateOnFoos(List<T> foos)
在所有情况下,T,E或U都有types参数的所有标识符,甚至可以有多个使用语法的types参数
public class MyClass<Atype,AnotherType> {}
在你的第四个ponint中,虽然有效地Sting是Object的一个子types,但是在generics类中没有这样的关系, List<String>
不是List<Object>
的子types,它们是编译器的两种不同types,最好在这个博客条目中解释
问题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:集合不允许types参数协方差。 但数组确实允许协方差。
让我们在Java历史的背景下讨论它们;
-
List
:
列表意味着它可以包含任何对象。 列表是在Java 5.0之前发布的; Java 5.0引入了List,用于向后兼容。
List list=new ArrayList(); list.add(anyObject);
-
List<?>
:
?
意味着未知对象不是任何对象; 通配符?
介绍是为了解决Generic Type构build的问题; 看通配符 但是这也会导致另一个问题:
Collection<?> c = new ArrayList<String>(); c.add(new Object()); // Compile time error
-
List< T> List< E>
在您的项目Lib中,在没有T或Etypes的前提下指通用声明。
-
List< Object>
表示通用参数化。
你是对的:String是Object的一个子集。 由于String比Object更“精确”,因此应将其用作System.out.println()的参数。