Java 通用参数:似乎只有菱形运算符有效
背景:这个问题是在(确切地说是答案的第一次修订)中提出的。这个问题中给出的代码被缩减到最低限度来解释这个问题 假设我们有以下代码:Java 通用参数:似乎只有菱形运算符有效,java,generics,type-inference,Java,Generics,Type Inference,背景:这个问题是在(确切地说是答案的第一次修订)中提出的。这个问题中给出的代码被缩减到最低限度来解释这个问题 假设我们有以下代码: public class Sample<T extends Sample<T>> { public static Sample<? extends Sample<?>> get() { return new Sample<>(); } public static
public class Sample<T extends Sample<T>> {
public static Sample<? extends Sample<?>> get() {
return new Sample<>();
}
public static void main(String... args) {
Sample<? extends Sample<?>> sample = Sample.get();
}
}
返回新样本不在类型变量T的范围内
返回使用通配符实例化泛型的新样本
这里有几个问题,但在深入研究之前,让我先谈谈你的实际问题:
是否可以显式定义返回值的泛型类型,或者在这种情况下是否需要菱形运算符
不可能显式地实例化示例>
(或者就此而言是示例
)。在实例化泛型类型时,通配符不能用作类型参数,尽管它们可能嵌套在类型参数中。例如,虽然实例化ArrayList
是合法的
最明显的解决方法是简单地返回一些可分配给Sample
的其他具体类型。例如:
class Sample<T extends Sample<T>> {
static class X extends Sample<X> {}
public static Sample<? extends Sample<?>> get() {
return new X();
}
}
public class Sample<T extends Sample<T>> {
public static Sample<? extends Sample<?>> get() {
final Sample<?> s = get0();
return s;
}
private static <T extends Sample<T>> Sample<T> get0() {
return new Sample<T>();
}
}
在main
中,变量s
保存的值是样本
和Y
,但不是a样本
。这是你的本意吗?如果不是,我建议用类型变量替换方法签名中的通配符,然后让调用方决定类型参数:
class Sample<T extends Sample<T>> {
static class X extends Sample<X> {}
static class Y extends Sample<X> {}
public static <T extends Sample<T>> Sample<T> get() { /* ... */ }
public static void main(String... args) {
Sample<X> x = Sample.get(); // legal
Sample<Y> y = Sample.get(); // NOT legal
Sample<?> ww = Sample.get(); // legal
Sample<?> wx = Sample.<X>get(); // legal
Sample<?> wy = Sample.<Y>get(); // NOT legal
}
}
类示例{
静态类X扩展了样本{}
静态类Y扩展了样本{}
公共静态示例get(){/*…*/}
公共静态void main(字符串…参数){
Sample x=Sample.get();//合法
Sample y=Sample.get();//不合法
Sample ww=Sample.get();//合法
Sample wx=Sample.get();//合法
Sample wy=Sample.get();//不合法
}
}
上述版本有效地保证,对于类型为A
的某些返回值,返回值扩展了Sample
。理论上,它甚至可以在T
绑定到通配符时工作。为什么?它返回到通配符捕获:
在原始的get
方法中,这两个通配符可能最终指向不同的类型。实际上,您的返回类型是Sample,它只是说:
如果类的类型参数列表为空(菱形形式
),则类的类型参数为推断的
那么,是否有一些推断的X
可以满足解决方案?对
当然,要明确定义这样的X
,必须声明:
public static <X extends Sample<X>> Sample<? extends Sample<?>> get() {
return new Sample<X>();
}
publicstaticsample>get(){
返回新样本();
}
显式的Sample
与返回类型Sample>
兼容,所以编译器很高兴
返回类型是一个混乱的Sample>
是一个完全不同的故事。您编写public static Sample>get()
而不仅仅是public static Sample get()
有什么特别的原因吗?在我看来,您已经定义了Sample
,类型参数必须扩展自身的Sample
。这个声明对我来说似乎是多余的,如果您不能使两个?
值匹配,可能会导致问题。@DawoodibnKareem请看一下上面提到的答案(及其问题)。它是在编写构建器和在构建器之间使用继承的上下文中出现的。不,我觉得在链接的问题中没有任何东西可以证明在方法签名中添加extends
子句是正确的。试着让get
返回一个Sample
@DawoodibnKareem忘记我刚才写的东西。这是一个大脑放屁。在这个具体问题上,你似乎是对的。我的问题仍然存在:这是需要
的情况吗?我认为Sample
和Sample>
是等效的类型。例如,见。不过,我想不出一个直截了当的方法来解释这里发生了什么。这似乎是明智的。但我的问题仍然存在:这是不是不能通过Java中的显式类型来表达,从而迫使我们使用菱形?不,不能直接表达这种类型。你必须依靠类型推断,要么通过菱形操作符,要么通过调用泛型方法,让通配符被类型变量捕获。回答得好。这似乎是一个很奇怪的签名。为什么不简单地编写publicstaticsampleget()
,让调用者决定X
是绑定到通配符还是具体类型?@MikeStrobel Ask。是他想出了引发这个问题的疯狂密码。我只是在回答这里发布的菱形运算符问题。@MikeStrobel这实际上会导致编译时错误,因为builder()
-方法(在a
和B
中)将具有相同的擦除。“最干净”的解决方案似乎是返回一个Builder
。
public class Sample<T extends Sample<T>> {
public static Sample<? extends Sample<?>> get() {
final Sample<?> s = get0();
return s;
}
private static <T extends Sample<T>> Sample<T> get0() {
return new Sample<T>();
}
}
class Sample<T extends Sample<T>> {
static class X extends Sample<X> {}
static class Y extends Sample<X> {}
public static Sample<? extends Sample<?>> get() {
return new Y();
}
public static void main(String... args) {
Sample<?> s = Sample.get(); // legal (!)
}
}
class Sample<T extends Sample<T>> {
static class X extends Sample<X> {}
static class Y extends Sample<X> {}
public static <T extends Sample<T>> Sample<T> get() { /* ... */ }
public static void main(String... args) {
Sample<X> x = Sample.get(); // legal
Sample<Y> y = Sample.get(); // NOT legal
Sample<?> ww = Sample.get(); // legal
Sample<?> wx = Sample.<X>get(); // legal
Sample<?> wy = Sample.<Y>get(); // NOT legal
}
}
public static <X extends Sample<X>> Sample<? extends Sample<?>> get() {
return new Sample<X>();
}