使用通配符时 Java 泛型类型不匹配

问题描述 投票:0回答:2

我试图理解 Java 中的泛型,但遇到了一些问题。这是我写的代码:

class Superclass{
}

class Subclass extends Superclass{
}

class Container <T>{
    T elem;
}

public class Test {
    public static void main(String[] args) {
        Container<? extends Superclass> b = new Container<Superclass>();
        b.elem = new Superclass();
    }   
}

这导致了一个奇怪的异常,我无法理解:

Exception in thread "main" java.lang.Error: Unresolved compilation problem: 
    Type mismatch: cannot convert from Superclass to capture#1-of ?

    at Test.main(Test.java:4)

我尝试做出一些改变。如果我将注释行替换为:

Container<Superclass> b = new Container<Superclass>();

一切正常。这对我来说很有意义。这只是普通的泛型,没有任何通配符。

然后我再次更改为注释行:

Container<?> b = new Container<Superclass>();

令我惊讶的是,这也导致了异常。然而我发现即使使用通配符,Java 也能保证类型安全。我想我明白为什么这会导致异常。

但我就是不明白为什么

Container<? extends Superclass> b = new Container<Superclass>();  

结果出现错误。据我所知, 意味着我可以在容器中使用超类的任何子类型。在 Java 中,超类是其自身的子类型,或者至少具有像以前一样的功能。我认为通过使用 extends Superclass 会有类型安全性并且 Java 会接受分配。

我尝试研究我的问题,但找不到任何令人满意的答案。如果有人可以帮助我,我会非常感激,我告诉我,我的错误是什么,并帮助我理解为什么这条线会导致问题。

java oop generics wildcard subtyping
2个回答
1
投票

假设你写的是

Container<? extends Superclass> b = new Container<Subclass>(); 
  // Clearly should compile, that's the whole point of ? extends

b.elem = new Superclass(); // SHOULD NOT compile

第一行之后,

b.elem
具有
Subclass
的“真实类型”。您无法将
Superclass
分配给
Subclass
,因此第二行 不应该 已编译。

您已明确告诉 Java 忘记

b
的“真实”类型参数。众所周知,它是
Superclass
的某个子类。但由于它可能是
Subclass
,并且不会让
b.elem = new Superclass()
编译,因此它拒绝编译它。

您只能以有效的方式使用

Container<? extends Superclass>
无论实际使用的是
Superclass
的哪个子类。
b.elem = new Superclass()
则不然。

这是正常现象,也是意料之中的。


0
投票

您面临的问题与协变的概念以及 Java 通配符泛型与类型声明和赋值相结合时的行为有关。

让我们分解一下您提到的不同案例:

  1. Container<? extends Superclass> b = new Container<Superclass>();
    此行声明了一个类型为
    b
    的变量
    Container<? extends Superclass>
    ,这意味着它可以保存作为
    Container
    子类型的任何类型的
    Superclass
    。但是,当您尝试为其分配
    Container<Superclass>
    时,就会出现问题。编译器正在阻止此分配,因为通配符
    ?
    代表
    Superclass
    的未知特定子类型,并且由于
    elem
    Container<? extends Superclass>
    的类型未知,因此它不允许您将任何特定对象放入其中(除了
    null
    )。例如,您可以将
    Container<Subclass>
    分配给
    b
    ,然后 尝试将
    Superclass
    对象放入其中将不是类型安全的
    。这就是编译器引发错误的原因。

  2. Container<Superclass> b = new Container<Superclass>();
    这很简单并且工作得很好,因为您在
    Superclass
    的声明和实例化中都使用了确切的类型
    Container

  3. Container<?> b = new Container<Superclass>();
    这一行声明了一个原始类型
    b
    的变量
    Container<?>
    ,它是
    Container<? extends Object>
    的简写。这是泛型最宽松的用法,它本质上意味着您可以将任何类型的
    Container
    (包括
    Container<Subclass>
    Container<SomeOtherClass>
    )分配给
    b
    。但是,由于
    elem
    Container<?>
    的类型未知,因此您不能在其中放入任何内容(除了
    null
    ),这就是为什么它在这种情况下通常不太有用。

© www.soinside.com 2019 - 2024. All rights reserved.