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>的子类?


当前回答

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

其他回答

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

不能将List<String>强制转换为List<Object>的原因是,它将允许您违反List<String>的约束。

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

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

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

注意:我在之前的回答中已经评论过这一点,但我想在此展开。

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

列表:

List意味着它可以包含任何对象。List是在Java 5.0之前发布的;为了向后兼容,Java 5.0引入了List。

List list=new  ArrayList();
list.add(anyObject);

列表< ?>:

? 指未知对象,而不是任何对象;通配符?引入是为了解决泛型类型构建的问题;看到通配符; 但这也导致了另一个问题:

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

List< T> List< E>

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

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

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

理论

字符串[]可以转换为对象[]

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中的代码,以便它实际编译,同时仍然显示所做的重点。)