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