【问题标题】:Type mismatch for Class Generics类泛型的类型不匹配
【发布时间】:2013-11-11 02:12:32
【问题描述】:

我有以下无法编译的代码,虽然有办法让它编译,但我想了解它为什么无法编译。有人可以告诉我具体为什么我会收到我将在最后发布的错误消息吗?

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&lt;capture#1-of ? extends Test&gt; to Class&lt;T&gt;

通过将Test.class 转换为Class&lt;T&gt;,编译时会出现Unchecked cast 警告并完美运行。

【问题讨论】:

    标签: java generics


    【解决方案1】:

    原因是Test.class 的类型是Class。您不能将 Class 类型的引用分配给 Class 类型的变量,因为它们不是同一个东西。但是,这有效:

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

    通配符允许将 Class 和 Class 引用分配给 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&lt;Number&gt; to Class&lt;Integer&gt;。但是Integer 扩展了Number,那为什么会失败呢?看看下面这两个语句,看看为什么:

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

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

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

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

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

    Class<? extends Number> wildcard = numberClass;
    

    由于Class&lt;? extends Number&gt; 表示NumberNumber 的子类的任何类型,因此这是完全合法的,并且在许多情况下都可能有用。

    【讨论】:

    • 我想了解您所说的 Class 类型的引用和 Class 类型的变量是什么意思。您是在谈论 Java 有引用吗?它是如何关联的?
    • 在 Java 中,除了原始类型之外的任何变量都是引用。我使用术语变量来指代赋值的左侧,使用术语引用来指代右侧。
    • 如果T 必须扩展Test,为什么不能将Class&lt;Test&gt; 分配给Class&lt;T&gt;Class&lt;Test&gt; 不是 Class&lt;T extends Test&gt; 的多态吗?
    • 原因是T 不是通配符,它​​是编译器在编译代码时替换的类型的占位符。通配符语法允许更广泛的分配,因为它不会以相同的方式限制声明。稍后我会尝试在我的答案中添加一个示例。
    【解决方案2】:

    假设我扩展测试:

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

    现在,当我调用testT 时,类型参数&lt;T&gt;SubTest,这意味着变量testTypeClass&lt;SubTest&gt;Test.class 的类型为 Class&lt;Test&gt;,不能分配给 Class&lt;SubTest&gt; 类型的变量。

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

    【讨论】:

      【解决方案3】:

      去掉条件,报错就好一点了……

      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;
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2020-10-18
        • 2010-09-20
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2015-04-20
        • 1970-01-01
        相关资源
        最近更新 更多