是否可以在 Java 中实现带有签名 List<Class<? extends Annotation>> 的方法?

Is it possible to implement method with signature List<Class<? extends Annotation>> in Java?

问题出在通用限制中:

public List<Class<? extends Annotation>> getAnnotations() {
    return new ArrayList<>(Arrays.asList(Override.class));
}

真正的return类型是ArrayList<Class<Override>>
方法需要 List<Class<? extends Annotation>>

Class<Override>Class<? extends Annotation>
的子类型 Class<? extends Annotation> c = Override.class; //允许

ArrayListList 的子类型,如果元素的类型匹配:
List<? extends Number> l = new ArrayList<Integer>(); // 允许

但是,这是不允许的:

List<Class<? extends Annotation>> l = Arrays.asList(Override.class);
List<Class<? extends Annotation>> l = new ArrayList<>(Arrays.asList(Override.class));

甚至可能 Class 通配符被破坏了吗?

我认为这是因为 jdk 1.7 类型推断的性质。

您可能已经知道,Arrays.asList(T ... elems) 方法是通用的,但我们很少明确指定我们希望该方法使用的类型参数,因此我们 依赖 关于编译器的类型推断功能。

因此,当编译器看到一个Arrays.asList(Override.class)语句时,它将推断该方法的类型参数应该替换为 Class<Override>,即我们将有一个 版本 的方法,形式如下:

public List<Class<Override>> asList(Class<Override> ... elems)

但是,如果您显式地将方法的类型参数设置

List<Class<? extends Annotation>> l = 
               Arrays.<Class<? extends Annotation>>asList(Override.class);

然后编译器将实际知道类型参数必须替换为什么,然后 .asList() 方法的 版本 将是:

public List<? extends Annotation> asList(Class<? extends Annotation> ... elems)

现在可以正常编译了,因为 Class<? extends Annotation>Class<Override> 兼容。在 Java8 中,类型推断功能得到了进一步改进,因此您不必为 .asList() 方法显式设置类型参数。

然而,更多有趣的问题转到

Why List<Class<Override>> is not compatible with List<Class<? extends Annotation>>?

java.lang.Class 是一个 final ,这将有助于回答以下两个问题,这两个问题的组合将回答上述问题。 :)

所以,

  • List<Class<Override>> 是什么意思?

List<Class<Override>> 意味着我们只能将 Class<Override> 的实例添加到列表中。这很好,因为 Class 类型是 final,我们甚至不能添加 Class<Override> sub-类。

  • List<Class<? extends Annotation>> 是什么意思?

这种类型的 List 表示 家族 的列表 类,所有这些都是 [=] 的子类 33=]类型,这意味着我们可以成功地将任何注释类型(例如SuppressWarnings.classOverride.classDocumented.class等)添加到列表中。

让我们假设以下示例实际上是正确的:

List<Class<Override>> overrides = Arrays.asList(Override.class);
List<Class<? extends Annotation>> annotations = new ArrayList<>();
annotations = overrides;
annotations.add(SuppressWarnings.class); //HUGE PROBLEM
annotations.add(Documented.class); //ANOTHER HUGE PROBLEM

这两个大问题来自于我们试图向 overrides 添加一些非 Override 实例,这是非常错误的。

我们有足够聪明的编译器,可以实际检测到此类可能的问题,抛出编译时错误是防止我们这样做的方法。

更多信息:

ArrayList is a subtype of a List, if types of the elements match:

List<? extends Number> l = new ArrayList<Integer>(); // allowed

是的,但在您的示例中,元素类型不匹配:

List<Class<? extends Annotation>> l = new ArrayList<Class<Override>>();

当然,Class<Override>Class<? extends Annotation> 的子类型,但就像 List<String> 不是 List<Object> 的子类型一样,List<Class<Override>> 也不是子类型List<Class<? extends Annotation>>。不过,它将是 List<? extends Class<? extends Annotation>> 的子类型。

就是说,您的代码无法编译的原因是在 Java 7 中,类型推断在推断 [=41= 的类型时没有考虑方法的 return 类型] 语句的表达式,因此它默认为可以分配给

的最具体的类型
Arrays.asList(Override.class)

没有意识到 return 语句只能用更灵活的类型编译(Java 8 类型推断更聪明,顺便说一句)。一种解决方法是明确指定类型参数:

Arrays.<Class<? extends Annotation>(Override.class);

或者通过首先分配给局部变量来给Java 7 的类型推断一个提示:

List<Class<? extends Annotation>> list = Arrays.asList(Override.class);
return list;

或将方法 return 类型更改为

List<? extends Class<? extends Annotation>> getAnnotations()

因此推断类型无关紧要。