Java中的通用VS通配符

时间:2019-07-14 22:16:48

标签: java generics

在通用类下有以下两种方法:

public class Container<S> {
    public void f2(List<Object> l1, List<?> l2) {
        l1 = l2; //compilation error row #1
    }

    public void f3(List<?> c, List<S> l) {
        c = l; //ok row #2
        l = c; //compilation error row #3
    }
}

我真的不懂,为什么第二行还可以-如果我将此方法转移两个列表,一个是对象类型列表,一个是字符串列表,我应该得到编译错误吗?

真的很想知道为什么/不应该编译每一行。

2 个答案:

答案 0 :(得分:3)

为什么要编译?

List<?> c, List<S> l;
c = l; // OK

List<?>表示(或多或少)“某物的列表”(或更正式地为“未知物的列表”),而List<S>就是其中之一。

-

为什么不编译?

List<Object> l1, List<?> l2;
l1 = l2; //compilation error

如果允许,则可以将任何内容(例如字符串)添加到l1中,但是如果l2List<Object>之外的任何内容(例如Integer),则表示l2中错误的类型。这就是不允许此分配的原因。

另一种编译错误更细微,并且也没有用例-也就是说,没有理由将类型化列表分配给未类型化列表-但是通配符?的真正含义是“未知,但特定”。这与“任何东西”都不相同。是“某物”,但我们不知道是什么。类型S是什么,但是编译器无法验证它与S 一样。

答案 1 :(得分:1)

泛型和通配符功能强大,因为它们可确保在编译时进行更好的类型检查,这是使用它们的主要目的。为了确保您的代码不会在运行时由于类型检查不佳而中断。

enter image description here

  

尽管Integer是Number的子类型,List<Integer>不是List<Number>的子类型,实际上,这两种类型无关。 List<Number>List<Integer>的共同父级是List<?>

     

为了在这些类之间创建关系,以便代码可以通过Number的元素访问List<Integer>的方法,请使用上限通配符:

List<? extends Integer> intList = new ArrayList<>();
List<? extends Number>  numList = intList;  // OK. List<? extends Integer> is a subtype of List<? extends Number>
     

由于IntegerNumber的子类型,并且numListNumber对象的列表,因此intList之间存在一个关系( Integer个对象)和numList。下图显示了使用上下界通配符声明的几个List类之间的关系。

要对通配符使用子类型化和多态性,必须使用有界通配符

public void f2(List<Object> l1, List<?> l2) {
  l1 = l2; //compilation error row #1
}

//correct way of doing it
public void f2(List<? extends Object> l3, List<?> l4) {
  l3 = l4; 
}

编译器不会将l2视为l1的子类型

public void f3(List<?> c, List<S> l) {
  c = l; //ok row #2
  l = c; //compilation error row #3
}

编译器不会将c视为l的子类型(正确的做法是,这可能会导致运行时错误)。