在静态嵌套类类型定义中理解原始与无界通配符

时间:2016-01-27 04:50:38

标签: java generics

我在一些奇怪的代码中一直在做一些代码考古,我遇到了类似的东西:

public abstract class Outer<T>
{
    protected Outer(Inner<?> inner)
    {
        // ...
    }

    public static abstract class Inner<U extends Outer>
    {
        // ...
    }
}

让我印象深刻的是,在Inner类型Outer类型(<U extends Outer>位)的使用情况下,没有无界通配符类型。

使用Inner<U extends Outer<?>>Inner<U extends Outer>的含义是什么?

我可以使用两种类型的版本成功编译和运行测试,但是我很难理解幕后发生的事情。

1 个答案:

答案 0 :(得分:2)

  1. 虽然示例中调用了Inner,但它实际上不是内部类,而是静态嵌套类。内部类是非静态嵌套类(请参阅https://docs.oracle.com/javase/tutorial/java/javaOO/nested.html)。
  2. 这是一个静态嵌套类而不是顶级类的事实并不重要。
  3. 使用类Inner的代码可以使用原始类型Inner(其中绕过所有类型检查 - 此处不感兴趣),或者为类型参数U指定实际类型。在后一种情况下,上限将该类型限制为泛型类型Outer<T>的子类型,其中T可以是任何类型,无论Inner是否声明为Inner<U extends Outer>或{ {1}}。
  4. 在类签名中使用原始类型仍然可以在声明变量或参数时使用强类型检查。例如,以下将编译(假设Inner<U extends Outer<?>>具有no-args构造函数):

    Inner

    但是用Outer.Inner<Outer<String>> x = new Outer.Inner<Outer<String>>(); 替换任何一方(但不是另一方)的Outer<String>将产生编译器错误。如果使用无界通配符而不是原始类型,则此行为将完全相同,因此目前没有差异

    实际差异在于如何允许类Outer使用Inner类型的变量。假设您在构造函数中传递了这样的变量:

    U

    假设public Inner(U u) { this.u = u; } 有一个方法接受类型Outer的参数(它自己的类型参数),例如:

    T

    现在,在原始上限(void add(T) { ...} )的情况下,类U extends Outer中的代码用任何对象调用此方法是合法的,例如一个字符串:

    Inner

    虽然会发出编译器警告(除非被禁止),并且如果实际运行时类型this.u.add("anything") T不同,则会在代码中抛出String对象是另一种类型。

    对于无界通配符(ClassCastException),由于U extends Outer<?>是特定但未知的类型,因此调用T方法将导致编译器错误,无论哪个参数你给它。

    由于您在两种情况下都提到代码编译正常,因此add中不存在使用T的此类方法,或者未从Outer调用此方法。但是通过添加无界通配符,您可以证明给类没有发生的类用户(因为否则代码将无法编译)。

    为了允许调用Inner this.u.add(s)s参数而不使用上限的原始类型,String必须声明为{{ 1}},遵循PECS原则,因为在这种情况下Inner是消费者的类型。

相关问题