我想知道为什么Java编译器不接受这个赋值:
Class<? extends List<?>> blbost = ArrayList.class;
请注意我对Class<? extends List<?>> blobst = (Class<? extends List<?>>)ArrayList.class;
等解决方案不感兴趣,我想知道它可以用于什么原因。
最初的动机是在注释中使用泛型:
@SomeAnnotation(SomeFunction.class) // Syntax error here!
private static class SomeClass {
}
private @interface SomeAnnotation {
Class<? extends Function<?, ?>> value();
}
private static class SomeFunction<T> implements Function<T, String> {
@Override
public String apply(T t) {
return t.toString();
}
}
我不能在注释中使用强制转换,因为值必须是常量。
为什么我不能将SomeFunction
作为参数传递给SomeAnnotation
,我该怎么办呢?
答案 0 :(得分:5)
首先,简短的回答是Class<ArrayList>
不是Class<? extends List<?>>
的子类型。实际上存在如下图所示的关系:
Class
|
Class<?>
|
Class<? extends List>
/ \
Class<? extends List<?>> Class<? extends ArrayList>
| |
Class<List<?>> Class<ArrayList>
换句话说,Class<ArrayList>
和Class<? extends List<?>>
共享一个常见的超类型。
如果Class<? extends List<?>> cls = ArrayList.class
是Class<? extends List<?>>
的超类型,Class<ArrayList>
之类的作业将被编译,因此我们需要找出Class<ArrayList>
的超类型是什么:
给定泛型类型声明
C<F1,...,Fn>
( n &gt; 0),参数化类型C<T1,...,Tn>
的直接超类型,其中Ti
(1≤< em> i ≤ n )是一种类型,全部如下:
D<U1 θ,...,Uk θ>
,其中D<U1,...,Uk>
是泛型类型,是泛型类型C<T1,...,Tn>
的直接超类型,θ
是替换[F1:=T1,...,Fn:=Tn]
C<S1,...,Sn>
,其中Si
包含Ti
(1≤ i ≤ n )。类型
Object
,如果C<F1,...,Fn>
是没有直接超接口的通用接口类型。- 醇>
原始类型
C
。
规则1基本上只是说如果你有一个类型,例如ArrayList<String>
然后它有超类型List<String>
,Iterable<String>
等,其中type参数仅替换为超类或超接口。 Class
没有相关的超类型,因为对于这个特例我们并不关心它的超类或超接口。
规则2是我们在这里真正感兴趣的唯一一个,并且JLS将我们引导到4.5.1以解释何时类型参数包含另一个类型参数:
如果由
T1
表示的类型集合,则类型参数T2
被称为包含另一个类型参数T2 <= T1
,写为T2
可证明是T1
表示的类型集的子集,在以下规则的反身和传递闭包下(其中<:
表示子类型):
? extends T <= ? extends S
ifT <: S
[...]
T <= ? extends T
[...]
使用这两个规则和传递属性,如果? extends S
是T
的超类型,我们可以推导出类型参数S
包含类型参数T
的规则。
使用此规则,我们现在可以按如下方式构建原始问题:
Class<? extends List<?>>
包含Class<ArrayList>
,则 ? extends List<?>
是ArrayList
的超类型。
? extends List<?>
是ArrayList
的超类型,则 List<?>
包含ArrayList
。
那么原始类型ArrayList
的超类型是什么?我们可以参考4.10.2:
给定泛型类型声明
C<F1,...,Fn>
( n &gt; 0),原始类型C
的直接超类型是以下所有:
原始类型
C
的直接超类。原始类型
C
的直接超接口。- 醇>
类型
Object
,如果C<F1,...,Fn>
是没有直接超接口的通用接口类型。
(规则3与我们在此考虑的例子无关。)
另外,我们需要快速跳转到4.8:
原始类型的超类(分别是超级接口)是泛型类型的任何参数化的超类(超接口)的擦除。
换句话说,ArrayList
的超类型是所有原始类型AbstractList
,List
,Iterable
等。
通过这一切,我们最终可以得出结论:Class<ArrayList>
不是Class<? extends List<?>>
的子类型,这就是为什么问题中的赋值不能编译的原因。
对于不涉及投射的解决方案,Marv展示了其中一个,即将Class<? extends Function<?, ?>>
更改为Class<? extends Function>
。
另一个是永远不会使用,例如SomeFunction.class
,其原始类型参数为Class<SomeFunction>
。相反,您始终使用具体类型参数扩展SomeFunction
。例如,如果您想要SomeFunction<Double>
,则需要创建一个显式子类:
class DoubleFunction extends SomeFunction<Double> {}
然后您就可以使用DoubleFunction.class
。
答案 1 :(得分:1)
ArrayList
不是List<?>
的子类型的原因是因为Java中的泛型是invariant
,这意味着对于任何两种不同的类型Type1
和{{1} },Type2
既不是List<Type1>
的子类型也不是超类型。
这也扩展到原始类型和参数化类型,这意味着像List<Type2>
这样的原始类型永远不能是ArrayList
等参数化类型的子类型或超类型,并且可能更直观地参数化类似List<Type1>
的类型永远不会是ArrayList<Type1>
等原始类型的子类型,即使List
是ArrayList
的子类型。
从内部类型中删除通配符将允许您将原始类型类传递给注释:
List
不是也不可能从像import java.util.function.Function;
public class Example {
@SomeAnnotation(SomeFunction.class)
private static class SomeClass {
}
private @interface SomeAnnotation {
Class<? extends Function> value();
}
private static class SomeFunction<T> implements Function<T, String> {
@Override
public String apply(T t) {
return t.toString();
}
}
}
这样的参数化类型中获取类文字。对于任何类型List<String>
,Type
将是语法错误,因此在此处使用原始类型是允许的,即使通常您不应使用原始类型。