是否可以实现具有签名List <class <?的方法? extends =“”annotation =“”>&gt;在Java?

时间:2015-07-04 09:55:16

标签: java generics wildcard

问题在于一般限制:

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

实际回报类型为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通配符被破坏?

2 个答案:

答案 0 :(得分:2)

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

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

因此,当编译器看到 Arrays.asList(Override.class)语句时,它将推断该方法的type-parameter应替换为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()方法显式设置type-parameter。

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

  

为什么List<Class<Override>>List<Class<? extends Annotation>>不兼容?

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

所以,

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

List<Class<Override>>表示我们只能添加Class<Override>的实例,而不会添加任何其他内容。这很好,因为我知道我们甚至无法添加Class<Override>个子类,因为Class类型是final

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

此类List代表整个系列的类列表,所有这些类都是Annotation类型的子类,这意味着我们可以成功添加任何注释键入(例如,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

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

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

更多信息:

答案 1 :(得分:1)

  

如果元素的类型匹配,则ArrayList是List的子类型:

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>>的子类型{1}}。不过,它将是List<? extends Class<? extends Annotation>>的子类型。

也就是说,代码无法编译的原因是在Java 7中,类型推断在推断返回语句的表达式时没有考虑方法的返回类型,所以它默认为可以分配给

的最具体类型
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;

或将方法返回类型更改为

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

因此推断类型并不重要。