是否可以在 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;
//允许
ArrayList
是 List
的子类型,如果元素的类型匹配:
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.class
、Override.class
、Documented.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
实例,这是非常错误的。
我们有足够聪明的编译器,可以实际检测到此类可能的问题,抛出编译时错误是防止我们这样做的方法。
更多信息:
- What do multi-level wildcards mean?
- Is
List<Dog>
a subclass of List<Animal>
?
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()
因此推断类型无关紧要。
问题出在通用限制中:
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;
//允许
ArrayList
是 List
的子类型,如果元素的类型匹配:
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 withList<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.class
、Override.class
、Documented.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
实例,这是非常错误的。
我们有足够聪明的编译器,可以实际检测到此类可能的问题,抛出编译时错误是防止我们这样做的方法。
更多信息:
- What do multi-level wildcards mean?
- Is
List<Dog>
a subclass ofList<Animal>
?
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()
因此推断类型无关紧要。