java - 了解静态嵌套类类型定义中的原始通配符与无限通配符

标签 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. 虽然叫Inner在这个例子中,它实际上不是一个内部类,而是一个静态嵌套类。内部类是非静态嵌套类(参见 https://docs.oracle.com/javase/tutorial/java/javaOO/nested.html )。
  2. 它是静态嵌套类而不是顶级类这一事实在这里无关紧要。
  3. 代码使用类Inner可以使用原始类型 Inner (其中绕过所有类型检查 - 此处不感兴趣),或为类型参数 U 指定实际类型.在后一种情况下,上限将该类型限制为泛型类型的子类型 Outer<T>其中 T可以是任何类型,无论是否 Inner声明为 Inner<U extends Outer>Inner<U extends Outer<?>> .

在类签名中使用原始类型仍然可以在声明变量或参数时使用强类型检查。例如,以下将编译(假设 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不使用原始类型作为上限的参数,Inner必须声明为 Inner<U extends Outer<? super String>> , 在 PECS 之后原则,因为U在这种情况下是消费者的类型。

关于java - 了解静态嵌套类类型定义中的原始通配符与无限通配符,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/35029134/

相关文章:

java - WAN 上的 Android P2P 连接

c# - 泛型语法

.net - 带有度量单位的重载函数

java - 文件输入流和哈夫曼树

java - 如何用Java编写位排序程序?

java - Java 中通配符泛型的继承

C# - 如何为 T 和 T[] 应用不同的泛型方法

generics - Swift - 无法将 BST 节点转换为通用节点

java - 使用 SharedPreferences 保存变量使我的应用程序崩溃

Java JPA类关联,NullPointerException?