类泛型的类型不匹配

时间:2020-03-06 15:02:13  来源:igfitidea点击:

我有以下无法编译的代码,尽管有一种使其可以编译的方法,但我想了解为什么它没有编译。有人能启发我具体为什么为什么我会收到我将在结尾处发布的错误消息吗?

public class Test {
    public static void main(String args[]) {
        Test t = new Test();
        t.testT(null);
    }

    public <T extends Test> void testT(Class<T> type) {
        Class<T> testType = type == null ? Test.class : type; //Error here
        System.out.println(testType);
    }
}

类型不匹配:无法从Class <capture#1-of转换?将Test>扩展到Class <T>

通过将" Test.class"转换为" Class <T>",它会以"未经检查的转换"警告进行编译,并且可以完美运行。

解决方案

删除条件,错误会更好一些。

public class Test {
    public static void main(String args[]) {
        Test t = new Test();
        t.testT(null);
    }

    public <T extends Test> void testT(Class<T> type) {
    Class<T> testClass = Test.class;
        System.out.println(testClass);
    }
}

Test.java:10: incompatible types
found   : java.lang.Class<Test>
required: java.lang.Class<T>
        Class<T> testClass = Test.class;

原因是Test.class的类型为Class <Test>。我们不能将Class <Test>类型的引用分配给Class <T>类型的变量,因为它们不是同一回事。但是,这可行:

Class<? extends Test> testType = type == null ? Test.class : type;

通配符允许将Class <T>和Class <Test>引用都分配给testType。

Angelika Langer Java泛型常见问题解答中有大量有关Java泛型行为的信息。我将基于其中的一些信息提供一个示例,该信息使用Number类的层次结构Java的核心API。

请考虑以下方法:

public <T extends Number> void testNumber(final Class<T> type)

这是为了使以下语句能够成功编译:

testNumber(Integer.class);
testNumber(Number.class);

但是以下内容无法编译:

testNumber(String.class);

现在考虑以下语句:

Class<Number> numberClass = Number.class;
Class<Integer> integerClass = numberClass;

第二行无法编译,并产生此错误"类型不匹配:无法从Class <Number>转换为Class <Integer>"。但是Integer扩展了Number,为什么它失败了?查看以下两个语句,以了解原因:

Number anumber = new Long(0);
Integer another = anumber;

很容易看出为什么第二行在这里没有编译。我们无法将Number实例分配给Integer类型的变量,因为无法保证Number实例具有兼容类型。在此示例中," Number"实际上是" Long",当然不能将其分配给" Integer"。实际上,该错误也是类型不匹配的原因:类型不匹配:无法从Number转换为Integer。

规则是,不能将实例分配给作为实例类型的子类的变量,因为不能保证兼容。

泛型的行为类似。在通用方法签名中," T"只是一个占位符,用于指示该方法允许编译器使用。当编译器遇到" testNumber(Integer.class)"时,它将本质上用" Integer"替换" T"。

通配符增加了额外的灵活性,因为将编译以下内容:

Class<? extends Number> wildcard = numberClass;

由于`Class <?扩展Number>表示类型为Number或者Number的子类的任何类型,这是完全合法的,并且在许多情况下可能有用。

假设我扩展测试:

public class SubTest extends Test {
  public static void main(String args[]) {
    Test t = new Test();
    t.testT(new SubTest());
  }
}

现在,当我调用" testT"时,类型参数" <T>"是" SubTest",这意味着变量" testType"是" Class <SubTest>"。 " Test.class"的类型为" Class <Test>",不能分配给" Class <SubTest>"类型的变量。

将变量testType声明为Class <?扩展Test>`是正确的解决方案;强制转换为Class <T>隐藏了一个真正的问题。