关于java:为什么是List< String>

Why is List<String> a subtype of List, but not of List<Object>?

本问题已经有最佳答案,请猛点这里访问。

Possible Duplicate:
Why is List not a sub-type of List?

EDOCX1 0是Java中的EDOCX1×1的子类型吗?

那么,为什么我不能将List类型的对象传递到接受List作为参数的函数中呢?不过,我可以将这样的对象传递到接受List作为参数的函数中。


Isn't String a subtype of Object in Java?

是的,但这并不能使List成为List的一个亚型。

考虑这个例子:

1
2
3
4
    List<String> l1 = new ArrayList<String>();
    List<Object> l2 = l1;  // This is a compilation error in real Java
    l2.add(new Integer(42));
    String oops = l1.get(0);

如果(假设)ListList的子类型,那么您最终会将Integer分配给最后一个语句中的String变量。简而言之,我们会破坏静态类型的安全性。

I can, though, pass such an object into a function that accepts List as a parameter.

是的,那是真的。但接下来您要处理的是原始类型,当将对象从列表中拉出时,必须使用显式类型转换。例如,上述内容需要改写如下:

1
2
3
4
    List<String> l1 = new ArrayList<String>();
    List l2 = l1;
    l2.add(new Integer(42));
    String oops = (String) l1.get(0);

注意类型转换意味着我们必须用运行时类型安全替换静态类型安全。(当然,在这种情况下,类型转换将在运行时失败,因为实例的类型错误。)


您将参数化类型与具体类型混淆。

给定ListT是参数化类型。

ListList在任何方面都不相关,不超过ListList相关。typeList,整个签名为type。但和所有事情一样,也有例外,这些被称为Wildcard Parameterized Types

List是一种称为无边界通配符参数化类型的类型,它的工作方式与原始类型非常相似,在语义上相同,但会导致更多有关不安全转换的编译器警告。

EDOCX1 11是一个有界通配符参数化类型,它将允许您放置EDCOX1×12的任何东西,但是因为Java EDCOX1中的每一个类都有12个语义,它在语义上与前两个选项没有任何不同。

现在,它们的功能都是一样的,但它们将无法通过instanceof测试,它们不是相同的类型。

换言之:

1
public void myfunction(final List<Object> list) {}

只接受ListtypeListtypeListList无关,尽管它们在语义上功能相同。

type要么是List要么是List要么是List,当考虑List的类型时,不考虑<>中的继承链,直到进入通配符参数化类型。

以下是示例:

1
2
Collection<?> coll = new ArrayList<String>();
List<? extends Number> list = new ArrayList<Long>();

您不能实例化一个new Collection,只能为它分配一个具体的实现。

List是整个类型。

Java中的泛型与C++中的实现不同,而不是混淆的名称。


在爪哇中,当EDCX1〔33〕是EDCX1〔1〕的亚型时,EDCX1〔31〕不是EDCX1〔0〕的亚型。此规则提供类型安全性。

假设我们允许一个ListList的一个子类型。请考虑以下示例:

1
2
3
4
5
6
7
8
9
10
public void foo(List<Object> objects) {
    objects.add(new Integer(42));
}

List<String> strings = new ArrayList<String>();
strings.add("my string");
foo(strings); // this is not allow in java
// now strings has a string and an integer!
// what would happen if we do the following...??
String myString = strings.get(1);

因此,强制这样做提供了类型安全性,但它也有一个缺点,即灵活性较低。请考虑以下示例:

1
2
3
4
5
class MyCollection<T> {
    public void addAll(Collection<T> otherCollection) {
        ...
    }
}

这里有一个T的集合,您希望添加另一个集合中的所有项。对于TS子类型,不能用Collection调用此方法。理想情况下,这是可以的,因为您只是将元素添加到集合中,而不是修改参数集合。

为了解决这个问题,Java提供了他们称之为"通配符"的东西。通配符是一种提供协方差/反方差的方法。现在考虑使用通配符进行以下操作:

1
2
3
4
5
6
7
8
class MyCollection<T> {
     // Now we allow all types S that are a subtype of T
     public void addAll(Collection<? extends T> otherCollection) {
         ...

         otherCollection.add(new S()); // ERROR! not allowed (Here S is a subtype of T)
     }
}

现在,对于通配符,我们允许在类型t中使用协方差,并阻止不安全类型的操作(例如,将项添加到集合中)。这样我们就获得了灵活性和类型安全性。


除了上面所说的,为了满足您的需要,您需要显式地将参数声明为List(这意味着,接受包括t在内的t子类的任何内容),在这种情况下,List应该有效,但List仅限于对象模板化列表。


StringObject的一个子类型,是的,但这并不意味着列表是列表的一个子类型。这正是Java工作的方式。你可以在这里阅读更多。