无法将泛型嵌套类型转换为非特定类型

13
我有两个带嵌套泛型的类。有没有办法消除“类型不匹配:无法将Msg<Value<String>>转换为Msg<Value<?>>”错误?在最后一次赋值中。
public class Value<V> {
    V   val;

    public Value(V val) {
        this.val = val;
    }
    @Override
    public String toString() {
        return "" + val;
    }
}

public class Msg<T> {

    T holder;

    public Msg( T holder) {
        this.holder = holder ;
    }
    public String toString() {
        return "" + holder;
    }

    public static void main(String[] args) {
        Msg<Value<String>>strMsg = new Msg(new Value<String>("abc"));
        // This is OK
        Msg<?>objMsg = strMsg;
        // Type mismatch: cannot convert from Msg<Value<String>> to Msg<Value<?>>   
        Msg<Value<?>>objMsg = strMsg;
    }
}

1
你尝试过添加显式转换来查看是否可以至少引出更好的类型错误吗? - Gian
@Gian 是的,同样的错误信息。 - stacker
你可能想考虑使用类型推断的 static 工厂方法来代替构造函数;这样,每次使用 new 时就不必为 MsgValue 进行参数化。请注意,在原始代码片段中,您忘记了为 new Msg 进行参数化,并使用了原始类型。请参阅此问题以了解类型推断的 static 工厂方法可以为您做什么:https://dev59.com/L3A65IYBdhLWcg3w9DqF - polygenelubricants
5个回答

24

请使用以下内容:

Msg<? extends Value<?>> someMsg = strMsg;
问题在于Msg<Value<?>> objMsg中的?不能进行捕获转换。它不是“某种类型的ValueMsg”,而是“Value任意类型的Msg”。
这也解释了为什么除了声明更改之外,我还将变量重命名为someMsgValue不能只是任何Object。 它必须属于某个类型(在此示例中为String)。

一个更通用的例子

让我们考虑一个更通用的例子:List<List<?>>。类似于原始场景,List<List<?>>不能捕获转换List<List<Integer>>
    List<List<Integer>> lolInt = null;

    List<List<?>> lolAnything = lolInt;         // DOES NOT COMPILE!!!
    // a list of "lists of anything"

    List<? extends List<?>> lolSomething = lolInt;   // compiles fine!
    // a list of "lists of something"

这里是另一种看待它的方式:

  • Java泛型是类型不变的
  • Integer可以转换为Number,但List<Integer>不是List<Number>
    • 类似地,List<Integer>可以被一个List<?>捕获转换,但List<List<Integer>>不是List<List<?>>
  • 使用有界通配符,List<? extends Number>可以捕获转换List<Integer>
    • 类似地,List<? extends List<?>>可以捕获转换List<List<Integer>>

某些?可以捕获而其他一些不能,这也解释了以下代码片段:

    List<List<?>> lolAnything = new ArrayList<List<?>>(); // compiles fine!

    List<?> listSomething = new ArrayList<?>(); // DOES NOT COMPILE!!!
        // cannot instantiate wildcard type with new!

相关问题

另请参阅


2
+20 for all the links - Shadow Man

4

我的回答与其他人类似,但希望更加清晰易懂。

List<List<?>> is a list of (lists of anything).

List<List<String>> is a list of (lists of strings).

后者无法转换为前者,因为这样做将允许您向List<List<String>>中添加一个List<Number>,这显然是不正确的。
请注意,如果您用一些没有.add方法的类型替换List,则其规则不会改变。要实现此目的,Java需要声明位置的协变和/或逆变(如C#的IEnumerable<out T>或Scala的List[+A])。而Java只有使用位置的协变和逆变(? extends X,? super X)。

3
尽管您的泛型类型参数包含通配符,但它本身不是通配符。当将非通配符泛型类型 T 分配给变量(Msg<T>)时,被分配的对象必须恰好具有 T 作为其泛型类型(包括 T 的所有泛型类型参数,通配符和非通配符)。在您的情况下,TValue<String>,它与 Value<?> 不是相同的类型。
因为 Value<String> 可以分配给 Value<?>,所以您可以使用通配符类型:
Msg<? extends Value<?>> a = new Msg<Value<String>>();

1

0

ILMTitan有一个很好的解决方案,如果您不想将类专门针对Value进行设置,那么在这一点上,您可以使用基本类型而不是泛型,因为您将关闭一个安全功能,但是有一种方法。您甚至可以传递参数使此方法更通用,但关键是"@SuppressWarnings"。

@SuppressWarnings("unchecked")
Msg<Value<?>> convert()
{
    return (Msg<Value<?>>) this;
}

如果this是一个Msg<Value<String>>,这段代码将无法编译通过。这不是一个未经检查的转换,而是一个明显非法的转换。这就像试图将一个String强制转换为一个Integer一样。 - polygenelubricants

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接