自引用泛型类型

2022-09-03 15:57:23

请考虑以下示例:

public final class Main<T extends Main<T>> {

    public static void main(String[] args) {
        Main<?> main = new Main<>();
    }
}

这可以完美地编译。但是,当我尝试在不使用菱形的情况下进行此编译时,我使其工作的唯一方法是使用原始类型。

Main<?> main = new Main();    

没有原始类型的尝试不起作用:

Main<?> main = new Main<?>();              // None of
Main<?> main = new Main<Main<?>>();        // these
Main<?> main = new Main<Main<Main<?>>>();  // compile

那么,为什么带有钻石的原始版本会起作用呢?写作时推断的类型是什么?Main<?> main = new Main<>();

它是推断原始类型,还是推断出某种像这样的无限嵌套类型?

Main<Main<Main<Main<...>>>>

答案 1

in 是一个占位符,在绑定时可以是任何类型。?Main<?>

在 source 中写入的每个变量都可能是不同的类型(在错误消息中称为 ),因此您无法将 的表达式分配给任何可表达类型的变量。?capture#2-of ?Main<?>

菱形运算符在这里工作,因为它的类型推断在捕获 s 之后运行 - 您的变为 but(假设您分配给它的是 )。?Main<>Main<?>Main<capture#1 of ?>Main<?>capture#1

换句话说,菱形运算符是唯一可以直接指定特定捕获的语法,就像在 C# 中一样,它是唯一可以直接指定匿名类型的语法。(请注意,使用方法类型推断进行重载解析也可以解析为特定的捕获)var


至于代码的含义,(对于 )是 的简写,或者,在你的情况下,(编译器自动将 约束为类型的约束)。这将成为 的协变视图,其中类型参数只能转换为(因为它实际上可能是任何类型,因此您不能假设超出约束的任何内容)。new Main<?>()?Main<? extends Object>Main<? extends Main<same ?>>?Main<>Main<?>

通常,没有理由实际创造这样的东西。


答案 2

通过使用通用帮助器方法,可以在没有菱形运算符的情况下进行编译(但是,当然,这就引出了一个问题,即调用帮助器方法时推断出什么类型的参数):

final class Main<T extends Main<T>> {

    public static void main(String[] args) {
        Main<?> main = helper();
    }
    private static <T extends Main<T>> Main<T> helper() {
        return new Main<T>();
    }
}

推荐