自引用泛型类型

时间:2015-11-05 00:02:59

标签: java generics covariance

考虑这个例子:

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<...>>>>

2 个答案:

答案 0 :(得分:4)

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

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

钻石运算符在这里工作是因为它的类型推断在捕获?后运行 - 您的Main<>变为Main<?>而不是Main<capture#1 of ?>(假设Main<?>你将它分配给capture#1)。

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

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

通常,没有理由真正创造这样的东西。

答案 1 :(得分:1)

通过使用泛型辅助方法,可以在没有菱形运算符的情况下编译一些东西(当然,这会引发一个问题,即调用辅助方法的类型参数是什么):

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>();
    }
}