自引用通用类型
Self-referential Generic Types
考虑这个例子:
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<...>>>>
Main<?>
中的 ?
是一个占位符,在绑定时可以是任何类型。
您在源代码中编写的每个 ?
可能是不同的类型(在错误消息中称为 capture#2-of ?
),因此您不能将 Main<?>
的表达式分配给变量任何可表达的类型。
菱形运算符在这里工作是因为它的类型推断在 ?
被捕获之后运行——你的 Main<>
变成不是 Main<?>
而是 Main<capture#1 of ?>
(假设 Main<?>
你分配给它的是 capture#1
).
也就是说,菱形运算符是唯一可以直接指定特定捕获的语法,就像C#中的var
是唯一可以直接指定匿名类型的语法一样。 (请注意,使用方法类型推断的重载解析也可以解析为特定的捕获)
至于代码的含义,new Main<?>()
(对于 ?
的任何捕获)对于 Main<? extends Object>
是 shorthand,或者,在您的情况下,Main<? extends Main<same ?>>
(编译器自动将 ?
约束到类型的约束)。这成为 Main<>
的协变视图,其中类型参数只能转换为 Main<?>
(因为它实际上可能是任何类型,因此您不能假设任何超出约束的内容)。
通常,没有理由实际创建这样的东西。
通过使用通用的辅助方法,可以在没有菱形运算符的情况下编译某些东西(但是,当然,这会引出一个问题,为调用辅助方法推断出什么类型的参数):
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>();
}
}
考虑这个例子:
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<...>>>>
Main<?>
中的 ?
是一个占位符,在绑定时可以是任何类型。
您在源代码中编写的每个 ?
可能是不同的类型(在错误消息中称为 capture#2-of ?
),因此您不能将 Main<?>
的表达式分配给变量任何可表达的类型。
菱形运算符在这里工作是因为它的类型推断在 ?
被捕获之后运行——你的 Main<>
变成不是 Main<?>
而是 Main<capture#1 of ?>
(假设 Main<?>
你分配给它的是 capture#1
).
也就是说,菱形运算符是唯一可以直接指定特定捕获的语法,就像C#中的var
是唯一可以直接指定匿名类型的语法一样。 (请注意,使用方法类型推断的重载解析也可以解析为特定的捕获)
至于代码的含义,new Main<?>()
(对于 ?
的任何捕获)对于 Main<? extends Object>
是 shorthand,或者,在您的情况下,Main<? extends Main<same ?>>
(编译器自动将 ?
约束到类型的约束)。这成为 Main<>
的协变视图,其中类型参数只能转换为 Main<?>
(因为它实际上可能是任何类型,因此您不能假设任何超出约束的内容)。
通常,没有理由实际创建这样的东西。
通过使用通用的辅助方法,可以在没有菱形运算符的情况下编译某些东西(但是,当然,这会引出一个问题,为调用辅助方法推断出什么类型的参数):
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>();
}
}