22

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

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

Type mismatch: cannot convert from Class<capture#1-of ? extends Test> to Class<T>

通过强制转换Test.classClass<T>this 编译并发出Unchecked cast警告并完美运行。

4

3 回答 3

24

原因是 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 Generics FAQ中有大量关于 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;

第二行无法编译并产生此错误Type mismatch: cannot convert from Class<Number> to Class<Integer>。但是Integerextends Number,那为什么会失败呢?查看接下来的两个语句以了解原因:

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

很容易看出为什么第二行在这里没有编译。您不能将 的实例分配给Number类型变量,Integer因为无法保证该Number实例是兼容类型。在此示例中,Number实际是 a Long,当然不能将其分配给Integer。其实错误也是类型不匹配:Type mismatch: cannot convert from Number to Integer.

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

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

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

Class<? extends Number> wildcard = numberClass;

因为Class<? extends Number>表示任何属于 thisNumber或其子类的类型Number在许多情况下都是完全合法的并且可能有用。

于 2008-10-02T16:54:15.250 回答
4

假设我扩展测试:

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

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

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

于 2008-10-03T00:38:10.500 回答
1

删除条件,错误更好一点......

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;
于 2008-10-02T16:52:02.057 回答