为什么List <string>是List的子类型,而不是List <object>?</object> </string>的子类型

时间:2012-06-26 01:52:45

标签: java generics

  

可能重复:
  Why is List<Number> not a sub-type of List<Object>?

Java中String的子类型不是Object吗?

那么,为什么我不能将List<String>类型的对象传递给接受List<Object>作为参数的函数?但是,我可以将这样的对象传递给接受List作为参数的函数。

5 个答案:

答案 0 :(得分:10)

  

String不是String中Object的子类型吗?

是的,但这不会使List<String>成为List<Object>的子类型。

考虑这个例子:

    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);

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


  

但是,我可以将这样的对象传递给接受List作为参数的函数。

是的,这是事实。但是,您正在处理原始类型,并且在将对象从列表中拉出时必须使用显式类型转换。例如,上述内容需要重写如下:

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

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

答案 1 :(得分:3)

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

鉴于List<T>T是参数化类型。

List<String>List<Object>的类型无关,不是List<String>List<Number>相关。 typeList<String>整个签名为type。但就像一切一样,也有例外;这些被称为Wildcard Parameterized Types

List<?>就是所谓的Unbounded Wildcard Parameterized Type,它的工作方式与原始类型非常相似,它们在语义上是相同的,但会导致更多关于不安全转换的编译器警告。

List<? extends Object>是一个有界的通配符参数化类型,它将是一个允许你放置extends Object但是因为Java extends Object中的每个类都没有语义任何类型的类型。不同于前两个选项。

现在它们的功能都相同,但它们会失败instanceof测试,它们相同的类型。

换句话说:

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

只接受type的{​​{1}},List<Object>ListtypeList<Object>无关,即使他们所有语义功能相同。

List<?>typeListList<Object>List<String>中的继承链在{{1}的类型时未被考虑关注,直到你进入Wild Card Parameterized Types

以下是示例:

<>

您不能实例化List<T>只为其分配具体实现。

Collection<?> coll = new ArrayList<String>(); List<? extends Number> list = new ArrayList<Long>(); 是整个类型。

Java中的泛型与C ++中的实现不同,除了令人困惑的名称。

答案 2 :(得分:2)

在Java中,当List<S>List<T>的子类型时,S 不是 T的子类型。此规则提供类型安全性。

假设我们允许List<String>成为List<Object>的子类型。请考虑以下示例:

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);

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

class MyCollection<T> {
    public void addAll(Collection<T> otherCollection) {
        ...
    }
}

这里有一个T的集合,你想要添加另一个集合中的所有项目。对于Collection<S> S T子类型,您无法使用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) } } 来调用此方法。理想情况下,这是可以的,因为您只是在集合中添加元素,而不是修改参数集合。

为了解决这个问题,Java提供了他们所谓的“通配符”。通配符是一种提供协方差/逆变的方法。现在考虑以下使用通配符:

{{1}}

现在,通过通配符,我们允许类型T中的协方差,并且我们阻止非类型安全的操作(例如将项添加到集合中)。这样我们就可以获得灵活性和类型安全性。

答案 3 :(得分:0)

StringObject的子类型,是的,但这并不意味着List是List的子类型。这就是Java的工作方式。您可以阅读更多here

答案 4 :(得分:0)

除了上面提到的所有内容,为了允许你想要的东西,你需要显式地将参数声明为List<? extends T>(这意味着,接受任何T的子类,包括T),在这种情况下,{{1}应该工作,但List<? extends Object>仅限于对象模板列表