List,List <?>,List <T>,List <E>和List <Object>之间的区别

Tha*_*ham 188 java generics

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

现在我不要盲目地问这个问题,所以请不要关闭这个帖子.我先介绍一下基本代码:

public static void test(List<?> list){
    System.out.println(list);   // Works
}
Run Code Online (Sandbox Code Playgroud)

我明白:

1 List.:是原始类型,因此不是typesafe.它只会在强制转换时生成运行时错误.当演员表不好时我们想要编译时错误.不建议使用.

2 . List<?>:是一个无界的通配符.但不确定这是为了什么?所以如果我改变List<?>方法

public static void test(List<?> list){
    list.add(new Long(2));     // Error
    list.add("2");             // Error
    System.out.println(list);
}
Run Code Online (Sandbox Code Playgroud)

它仍然很好.如果您能解释一下这种用法,我将不胜感激.

编辑:如果我这样做:

public static void test(List<T> list){   // T cannot be resolved
    System.out.println(list);
}
Run Code Online (Sandbox Code Playgroud)

但如果我改成List<?>这个:

public <T> T[] toArray(T[] a){
    return a;   
}
Run Code Online (Sandbox Code Playgroud)

3 . <T>:

public static void test(List<Object> list){
    System.out.println(list);
}
Run Code Online (Sandbox Code Playgroud)

我想我不明白这种语法.我看到这样的东西,它有效:

test((List<Object>) names);
Run Code Online (Sandbox Code Playgroud)

请帮我解释一下吗?有时候,我看到的<E>,或者<U>,或者<T,E>,test(List<Object>).它们是一样的还是代表不同的东西?

4.List<String>

public static void test(List<?> list){
    System.out.println(list);   // Works
}
Run Code Online (Sandbox Code Playgroud)

然后我得到List<String>了以下代码的错误.我很迷惑.我以为List<Object>是其中的一部分String

public static void test(List<?> list){
    list.add(new Long(2));     // Error
    list.add("2");             // Error
    System.out.println(list);
}
Run Code Online (Sandbox Code Playgroud)

编辑:如果我试试这个

public static void test(List<T> list){   // T cannot be resolved
    System.out.println(list);
}
Run Code Online (Sandbox Code Playgroud)

然后我得到了 Object

Kaj*_*Kaj 76

1)正确

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

3)T,E和U是相同的,但人们倾向于使用例如T代表类型,E代表元素,V代表值,K代表密钥.编译的方法表示它采用了某种类型的数组,并返回相同类型的数组.

4)你不能混合橙子和苹果.如果可以将字符串列表传递给期望对象列表的方法,则可以将String添加到String列表中.(并非所有对象都是字符串)

  • 这是一种创建可以接受任何类型项目的列表的方法,您很少使用它。 (3认同)
  • "2"上的只读列表+1.我写了一些代码来在`2`中演示这个.tyvm (2认同)

Far*_*ker 26

对于最后一部分:尽管String是Object的子集,但List <String>不是从List <Object>继承的.

  • 很好的一点; 许多人认为,因为C类继承自P类,所以List <C>也继承自List <P>.正如你所指出的那样并非如此.原因是如果我们可以从List <String>转换为List <Object>,那么我们可以将对象放入该列表中,从而在尝试检索元素时违反了List <String>的原始契约. (10认同)
  • List <Object>可用于存储不同类的对象列表. (8认同)
  • +1。好点。那么为什么人们会使用`List&lt;Object&gt;`? (2认同)

Ted*_*opp 20

符号的List<?>意思是"一个东西的列表(但我不是说什么)".由于代码test适用于列表中的任何类型的对象,因此它可用作形式化方法参数.

使用类型参数(如第3点所述),需要声明type参数.它的Java语法是放在<T>函数前面.这与在方法体中使用名称之前将形式参数名称声明为方法完全类似.

关于List<Object>不接受a List<String>,这是有道理的,因为a String不是Object; 它是的子类Object.修复是声明public static void test(List<? extends Object> set) ....但那extends Object是多余的,因为每个类都直接或间接地扩展Object.

  • 我认为"列表中的东西"对于`List <?>`更好,因为列表是某种特定但未知的类型.`List <Object>`实际上是"任何事物的列表",因为它确实可以包含任何东西. (10认同)

Pet*_*ter 13

你可以不投之所以List<String>List<Object>的是,它可以让你违反的制约List<String>.

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

如果我可以将其转换为a List<Object>,则允许我添加Object到该列表,从而违反原始合同List<String>.

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

NB我在之前的回答中已经对此进行了评论,但希望扩展它.

  • 一般来说,你不应该使用`List <Object>`,因为它有点破坏了泛型的目的.但是,有些情况下旧代码可能会有一个`List`接受不同的类型,因此您可能希望改进代码以使用类型参数化,以避免编译器警告原始类型.(但功能不变) (3认同)

Har*_*555 5

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

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

}
Run Code Online (Sandbox Code Playgroud)

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

public void operateOnFoos(List<T> foos)
Run Code Online (Sandbox Code Playgroud)

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

public class MyClass<Atype,AnotherType> {}
Run Code Online (Sandbox Code Playgroud)

虽然有效地将Sting作为Object的子类型,但在泛型类中没有这样的关系,List<String>不是List<Object>从编译器的角度来看它们是两种不同类型的子类型,这在本博客条目中得到了最好的解释


if_*_*one 5

我建议阅读Java益智游戏.它很好地解释了声明中的继承,泛型,抽象和通配符. http://www.javapuzzlers.com/


phi*_*hil 5

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

  1. List

列表意味着它可以包含任何对象。列表在Java 5.0之前的版本中;Java 5.0引入了List,以实现向后兼容。

List list=new  ArrayList();
list.add(anyObject);
Run Code Online (Sandbox Code Playgroud)
  1. List<?>

?表示未知对象而不是任何对象;通配符?介绍用于解决“通用类型”构建的问题;看到通配符 ; 但这还会导致另一个问题:

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

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

  1. List< Object> 表示通用参数化。


rad*_*ast 5

理论

String[] 可以投射到 Object[]

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);
}
Run Code Online (Sandbox Code Playgroud)

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
}
Run Code Online (Sandbox Code Playgroud)

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
}
Run Code Online (Sandbox Code Playgroud)

在B中,参数在编译时set不是类型化的List:编译器将其视为List<?>。存在运行时错误,因为在运行时set会变成从中传递的实际对象main(),即为List<String>List<String>无法将A 强制转换为List<Object>

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

(希望我在这里掌握了所有内容,如果我的推理是错误的,我敢肯定社区会告诉我。更新:我已经更新了示例A中的代码,以使它可以实际编译,同时仍然显示出要点。)