繁体   English   中英

列表,列表之间的区别<!--?--> , 列表<t> , 列表<e> , 和列表<object><div id="text_translate"><p> <strong>List 、 List<?> 、 List<T> 、 List<E>和List<Object>之间有什么区别?</strong></p><h1> 1.清单</h1><p> List :是原始类型,因此不是typesafe的。 它只会在转换错误时产生运行时错误。 当转换错误时,我们希望出现编译时错误。 不推荐使用。</p><h1> 2.列表<?></h1><p> List<?>是一个无限通配符。 但我不确定它的用途是什么? 我可以毫无问题地打印一个List<?> :</p><pre> public static void test(List<?> list){ System.out.println(list); // Works }</pre><p> <strong>为什么我不能将项目添加到List<?> ?</strong></p><pre> public static void test(List<?> list){ list.add(new Long(2)); // Error list.add("2"); // Error System.out.println(list); }</pre><h1> 3.列表<T></h1><pre> public static void test(List<T> list){ // T cannot be resolved System.out.println(list); }</pre><p> 我不明白这种语法。 我看到了这样的东西,它有效:</p><pre> public <T> T[] toArray(T[] a){ return a; }</pre><p> <strong>有时,我会看到<T>或<E>或<U> 、 <T,E> 。</strong> <strong>它们都是一样的还是代表不同的东西?</strong></p><h1> 4. 列表<对象></h1><p> 这给出了错误“方法test(List<Object>)不适用于参数List<String> ”:</p><pre> public static void test(List<Object> list){ System.out.println(list); }</pre><p> 如果我尝试这个然后我得到“无法从List<String>投射到List<Object> ”:</p><pre> test((List<Object>) names);</pre><p> 我很迷惑。 <strong>String是Object的子类,那么为什么List<String>不是 List List<Object>的子类?</strong></p></div></object></e></t>

[英]Difference between List, List<?>, List<T>, List<E>, and List<Object>

ListList<?>List<T>List<E>List<Object>之间有什么区别?

1.清单

List :是原始类型,因此不是typesafe的。 它只会在转换错误时产生运行时错误。 当转换错误时,我们希望出现编译时错误。 不推荐使用。

2.列表<?>

List<?>是一个无限通配符。 但我不确定它的用途是什么? 我可以毫无问题地打印一个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.列表<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. 列表<对象>

这给出了错误“方法test(List<Object>)不适用于参数List<String> ”:

public static void test(List<Object> list){
    System.out.println(list);
}

如果我尝试这个然后我得到“无法从List<String>投射到List<Object> ”:

test((List<Object>) names);

我很迷惑。 StringObject的子类,那么为什么List<String>不是 List List<Object>的子类?

1) 正确

2) 您可以将那个视为“只读”列表,您不关心项目的类型。例如,可以由返回列表长度的方法使用。

3) T、E 和U 是相同的,但人们倾向于使用例如T 表示类型,E 表示元素,V 表示值,K 表示键。 编译的方法说它接受了一个特定类型的数组,并返回了一个相同类型的数组。

4)你不能把橙子和苹果混在一起。 如果可以将字符串列表传递给需要 object 列表的方法,则可以将 Object 添加到字符串列表中。 (并非所有对象都是字符串)

最后一部分:虽然String是Object的子集,但是List<String>并不是继承自List<Object>。

符号List<?>的意思是“某物的列表(但我不是在说什么)”。 由于test中的代码适用于列表中的任何类型的 object,因此它用作正式方法参数。

使用类型参数(如第 3 点),需要声明类型参数。 Java 的语法是将<T>放在 function 的前面。这完全类似于在方法主体中使用名称之前向方法声明形式参数名称。

关于List<Object>不接受List<String> ,这是有道理的,因为String不是Object 它是Object的子类。 解决方法是声明public static void test(List<? extends Object> set)... 但是extends Object是多余的,因为每个 class 直接或间接扩展Object

您不能将List<String>转换为List<Object>的原因是它会让您违反List<String>的约束。

考虑以下场景:如果我有一个List<String> ,它应该只包含String类型的对象。 (这是final堂课)

如果我可以将其转换为List<Object> ,那么我就可以将Object添加到该列表中,从而违反了List<String>的原始合同。

因此,一般来说,如果 class C继承自 class P ,则不能说GenericType<C>也继承自GenericType<P>

注意我已经在之前的回答中对此发表了评论,但想对其进行扩展。

让我们在 Java 的历史背景下谈谈它们;

  1. List

List表示可以包含任意Object。List在Java 5.0之前的版本中; Java 5.0 引入了 List,用于向后兼容。

List list=new  ArrayList();
list.add(anyObject);
  1. List<?> :

? 表示未知 Object 不是任何 Object; 通配符? introduction是为了解决Generic Type构建的问题; 通配符 但这也导致了另一个问题:

Collection<?> c = new ArrayList<String>();
c.add(new Object()); // Compile time error
  1. List< T> List< E>

表示在您的项目 Lib 中没有 T 或 E 类型的前提下的通用声明。

  1. List< Object>表示泛型参数化。

理论

String[]可以转换为Object[]

List<String>不能转换为List<Object>

实践

对于列表,它比这更微妙,因为在编译时不检查传递给方法的列表参数的类型。 方法定义还不如说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.Collections$UnmodifiableRandomAccessList Object[] ):

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 中,参数set在编译时不是类型化List :编译器将其视为List<?> 存在运行时错误,因为在运行时, set变为从main()传递的实际 object,即List<String> 无法将List<String>强制转换为List<Object>

在 C 中,参数set需要一个Object[] String[] object 作为参数调用时没有编译错误和运行时错误。 那是因为String[]转换为Object[] 但是test()收到的实际 object 仍然是String[] ,它没有改变。 所以params object 也变成了String[] 并且String[]的元素 0 不能分配给Long

(希望我这里的一切都是正确的,如果我的推理是错误的,我相信社区会告诉我。更新:我已经更新了示例 A 中的代码,以便它可以实际编译,同时仍然显示所提出的观点。)

在你的第三点,“T”无法解析,因为它没有声明,通常当你声明一个泛型 class 时,你可以使用“T”作为绑定类型参数的名称,许多在线示例包括 oracle 的教程 都使用“T”作为类型参数的名称,例如,您声明一个 class,例如:

public class FooHandler<T>
{
   public void operateOnFoo(T foo) { /*some foo handling code here*/}

}

你是说FooHandler's operateOnFoo方法需要一个类型为“T”的变量,它在 class 声明本身上声明,考虑到这一点,你可以稍后添加另一个方法,如

public void operateOnFoos(List<T> foos)

在所有情况下,T、E 或 U 都有类型参数的所有标识符,您甚至可以有多个使用语法的类型参数

public class MyClass<Atype,AnotherType> {}

在你的第四个观点中,虽然实际上 Sting 是 Object 的子类型,但在 generics 类中没有这种关系, List<String>不是List<Object>的子类型,从编译器的角度来看,它们是两种不同的类型,这最好在这篇博文中解释

我建议阅读 Java 益智游戏。 它很好地解释了 inheritance、generics、抽象和声明中的通配符。 http://www.javapuzzlers.com/

问题 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:集合不允许类型参数协变。 但是数组确实允许协变。

你是对的:String 是 Object 的子集。由于 String 比 Object 更“精确”,你应该转换它以将其用作 System.out.println() 的参数。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM