将具有泛型参数的类传递给注释

Mif*_*eet 3 java generics types

我想知道为什么Java编译器不接受这个赋值:

Class<? extends List<?>> blbost = ArrayList.class;
Run Code Online (Sandbox Code Playgroud)

请注意,我对解决方案不感兴趣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();
    }
}
Run Code Online (Sandbox Code Playgroud)

我不能在注释中使用强制转换,因为值必须是常量.

为什么我不能SomeFunction作为一个参数传递,SomeAnnotation我该怎么做呢?

Rad*_*def 5

首先,简短的回答是,Class<ArrayList>它不是一个子类型Class<? extends List<?>>.实际上存在如下图所示的关系:

                      Class
                        |
                     Class<?>
                        |
              Class<? extends List>
                  /           \
Class<? extends List<?>>    Class<? extends ArrayList>
         |                             |
   Class<List<?>>              Class<ArrayList>
Run Code Online (Sandbox Code Playgroud)

换句话说,Class<ArrayList>Class<? extends List<?>>分享一个共同的超类型.

这主要在本规范的4.10.24.5.1节中介绍.

赋值比如Class<? extends List<?>> cls = ArrayList.class,如果将编译Class<? extends List<?>>为的超类型Class<ArrayList>,所以我们需要找出的超类型Class<ArrayList>有:

给出一个通用的类型声明(Ñ > 0),参数类型的直接超类型,其中(1≤ Ñ)是一种类型的,是以下所有:C<F1,...,Fn>C<T1,...,Tn>Ti

  1. D<U1 ?,...,Uk ?>,其中是泛型类型,它是泛型类型的直接超类型并且是替换.D<U1,...,Uk>C<T1,...,Tn>?[F1:=T1,...,Fn:=Tn]

  2. C<S1,...,Sn>,其中包含(1≤ Ñ).SiTi

  3. type Object,if 是没有直接超接口的通用接口类型.C<F1,...,Fn>

  4. 原始类型C.

规则1,基本上是说,如果你有一个类型如ArrayList<String>再有超类型List<String>,Iterable<String>等等,其中类型的说法,只是到超类或超取代.Class没有这种类型的相关超类型,因为对于这个特定的例子,我们不关心它的超类或超接口.

规则2是我们在这里真正感兴趣的唯一一个,并且JLS将我们引导到4.5.1以解释何时类型参数包含另一个类型参数:

A型参数被说成包含另一种类型的自变量,写入,如果设定的表示为类型可证明是该组由表示类型的子集的以下规则自反和传递闭包下(其中,表示子类型):T1T2T2 <= T1T2T1<:

  • ? extends T <= ? extends S 如果 T <: S

  • [...]

  • T <= ? extends T

  • [...]

使用这两个规则和传递属性,我们可以推导出类型参数? extends S包含类型参数的规则,T如果S是超类型T.

使用此规则,我们现在可以将原始问题框架如下:

  1. Class<? extends List<?>>是的超类型Class<ArrayList>,如果? extends List<?>包含ArrayList.

  2. ? extends List<?>包含ArrayListif List<?>是超类型ArrayList.

那么原始类型的超类型是ArrayList什么?我们可以参考4.10.2:

给定泛型类型声明(n > 0),原始类型的直接超类型是以下所有:C<F1,...,Fn>C

  1. 原始类型的直接超类C.

  2. 原始类型的直接超接口C.

  3. type Object,if 是没有直接超接口的通用接口类型.C<F1,...,Fn>

(规则3与我们在这里考虑的例子无关.)

另外,我们需要快速跳转到4.8:

原始类型的超类(分别为超级接口)是泛型类型的任何参数化的超类(超接口)的擦除.

换句话说,的超类型ArrayList是所有的原始类型AbstractList,List,Iterable等.

从这一切,我们最终可以得出结论,这Class<ArrayList>不是一个子类型Class<? extends List<?>>,这就是为什么问题中的赋值不能编译.


至于不涉及施法的解决方案,Marv已经展示了其中一个,即Class<? extends Function<?, ?>>改为Class<? extends Function>.

另一个是永远不会使用例如a SomeFunction.class,它具有原始类型参数Class<SomeFunction>.相反,您总是SomeFunction使用具体类型参数进行扩展.例如,如果你想要a SomeFunction<Double>,你需要创建一个显式子类:

class DoubleFunction extends SomeFunction<Double> {}
Run Code Online (Sandbox Code Playgroud)

然后你就可以使用了DoubleFunction.class.