【问题标题】:Java Generics: Incompatible types: capture<? extends Object> and <? extends T>Java 泛型:不兼容的类型:捕获<?扩展对象> 和 <?扩展 T>
【发布时间】:2021-08-16 02:04:34
【问题描述】:

我有一个 T 类型的对象(通用)

public <T> void method1(T object) {...}

我想对该对象使用方法 getClass()。

Class<? extends T> clazz = object.getClass();

但编译器显示错误:

Incompatible types.
Found:    'java.lang.Class<capture<? extends java.lang.Object>>'
Required: 'java.lang.Class<? extends T>'

如何更正我的代码以便编译器能够编译,并且 clazz 对象将具有泛型类型 T 或 不强制转换?

【问题讨论】:

  • 你的方法需要接受一个额外的Class&lt;? extends T&gt;参数。

标签: java generics


【解决方案1】:

丑陋的演员是唯一的方法:

Class<? extends T> clazz = (Class<? extends T>) object.getClass();

... 然后禁止使用@SuppressWarnings 执行此操作时收到的警告。

更一般地说,想要 Class 对象几乎总是一个错误。也许扩展您认为需要它的原因,可能有更好的选择。

对于实际类型,代码可以正常工作(请参阅documentation),但不适用于类型变量:

实际结果类型是Class 其中 |X|是调用 getClass 的表达式的静态类型的擦除。例如,此代码片段中不需要强制转换:

对于您的特定T,“静态类型的擦除”只是Object,因此,您会得到Class&lt;? extends Object&gt;,它只是Class&lt;?&gt;,您不能将其分配给类型的变量Class&lt;? extends T&gt; 除非你是丑陋的演员。为什么它会这样工作?嗯,确实如此,规范是这样说的。我承认我不知道为什么,但事实就是如此。

注意:您还可以添加一个参数:void foo(Class&lt;? extends T&gt; clazz, T value),但现在您通过传递一个看似无用的附加参数来打扰调用者。可能不是一个好主意。编译器至少会检查你的调用者并抱怨他们是否通过例如String.class 然后传递一个整数作为值。

【讨论】:

    【解决方案2】:

    添加到另一个答案,.getClass() 返回Class&lt;? extends |X|&gt;,其中|X| 是调用getClass 的表达式的静态类型的擦除的原因是X 可能是参数化的类型,使用Class&lt;a parameterized type&gt; 或Class&lt;? extends a parameterized type&gt; 是不安全的。

    例如,考虑Class&lt;ArrayList&lt;String&gt;&gt;。但实际上只有一个Class 实例代表ArrayList——它并不特定于ArrayList&lt;String&gt; 或ArrayList&lt;Integer&gt; 等。如果你有一个Class&lt;ArrayList&lt;String&gt;&gt; 类型的表达式,你可以在它上面调用.cast()并传递任何对象并在没有警告的情况下获得ArrayList&lt;String&gt;,但这具有欺骗性,因为它并没有真正检查该对象是否为ArrayList&lt;String&gt;(在运行时无法做到这一点);它只检查它是ArrayList;如果它是ArrayList&lt;Integer&gt;,你可能仍然会得到一个令人讨厌的惊喜。同样,使用Class&lt;ArrayList&lt;String&gt;&gt;,您可以调用.isInstance() 并传递ArrayList&lt;Integer&gt; 并获取true。

    Class&lt;? extends ArrayList&lt;String&gt;&gt; 也是如此;即你可以调用.cast() 得到ArrayList&lt;String&gt;,你可以调用.isInstance() 得到欺骗性的结果。那些是不安全的。因此,您应该无法在没有警告的情况下输入Class&lt;? extends ArrayList&lt;String&gt;&gt;。由于在您的代码中T 可能是参数化类型(编译器必须假定类型变量可以是其范围内的任何类型),因此编译器无法在没有警告的情况下为您提供Class&lt;? extends T&gt;。为了安全,它必须返回编译时已知的类型,语言设计者选择的选项是Class&lt;? extends |T|&gt;,在您的情况下是Class&lt;? extends Object&gt;。

    【讨论】:

      猜你喜欢
      • 2023-02-02
      • 2021-12-18
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2017-05-21
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多