gpt4 book ai didi

Java 泛型——需要解释

转载 作者:塔克拉玛干 更新时间:2023-11-02 08:22:03 24 4
gpt4 key购买 nike

我有一个关于 Java 泛型的问题。在下面的代码中,我们将接口(interface) B 参数化为另一个必须实现接口(interface) A 的类型。

这段代码是正确的。问题是:为什么它不适用于以下 list() 方法声明?

private <X extends A, Y extends B<X>> List<Y> list()

工作代码:

public interface A {
}
public interface B<T extends A> {
}
public class Test {

private static class AA implements A {}
private static class BB implements B<AA> {}

private <R extends A, X extends R, Y extends B<X>> List<Y> list() {
return null;
}

private void test() {
List<BB> l = list();
}
}

编辑:我已经重新编写了代码。现在我们已经根据它发出的声音对鸟类进行了参数化。问题是为什么 useless_t 是必需的?

public class Test {
public interface Sound {
}
public interface Bird<T extends Sound> {
}

private static class Quack implements Sound {}
private static class Duck implements Bird<Quack> {}


private <useless_t extends Sound, sound_t extends useless_t, bird_t extends Bird<sound_t>> List<bird_t> list() {
return null;
}

private void test() {
List<Duck> l = list();
}
}

最佳答案

我的 Eclipse IDE 不会按原样编译您的任何代码示例。但是当给出额外的类型提示时它们确实可以编译。在第二个示例中,有或没有类型参数 useless_t ,以下行不为我编译:

List<Duck> l = list();

但以下内容确实为我编译:

List<Duck> l = this.<Sound, Quack, Duck> list();

随着 useless_t分解出来,下面的编译也一样:

List<Duck> l = this.<Quack, Duck> list();

所以这基本上是编译器没有正确获取类型参数的问题,你需要明确地给出类型。

更新:如果您确实遇到了一个添加 useless_t 的程序有所不同,您处于不安全的地形,并且依赖于未指定的编译器行为。

您遇到了不同编译器行为不同的问题,即类型推断。 JLS 并不完全清楚编译器在哪些地方必须推断类型,在哪些地方必须拒绝推断,因此这里有回旋余地。不同版本的 Eclipse 编译器和不同版本的 javac 在它们推断类型的地方有所不同。对于 javac,即使比较不同的 1.5.0_x 版本也是如此,而且 Eclipse 编译器通常可以推断出比 javac 更多的信息。

你应该只依赖于所有常见编译器都成功的类型推断,否则给出类型提示。有时,这就像引入一个临时变量一样简单,但有时(如您的示例)您必须使用 var.<Types>method()语法。

关于评论:如果我希望方法 Duck.getSound() 返回 Quack,而不是使用泛型的 Sound,该怎么办?

假设 Bird 接口(interface)有以下方法:

public interface Bird<T extends Sound> {
T getSound();
}

然后你可以这样实现它:

private static class Duck implements Bird<Quack> {
public Quack getSound() { return new Quack(); }
}

这是泛型的一个用例——允许实现指定具体类型,这样即使父类(super class)也可以使用该类型。 (Bird 接口(interface)可以有一个 setSound(T) ,或者在不知道 T 的具体类型的情况下用 T 做其他事情。)

如果调用者只知道一个实例的类型是 Bird<? extends Sound> ,他必须像这样调用 getSound:

Sound birdSound = bird.getSound();

如果来电者知道 Quack , 他可以执行 instanceof测试。但是如果调用者知道这只鸟真的是Bird<Quack> ,甚至那是一个 Duck , 然后他可以写这个并按需要编译:

Quack birdSound = bird.getSound();

但要注意:在接口(interface)或父类(super class)中使用过多的类型会带来使系统过于复杂的风险。正如 Slanec 所写,重新思考您的实际设计,看看是否真的需要拥有这么多泛型。

我曾经走得太远,最终得到一个接口(interface)层次结构和两个实现层次结构,基于这样的接口(interface):

interface Tree<N extends Node<N>,
T extends Tree<N, T>> { ... }

interface SearchableTree<N extends SearchableNode<N>,
S extends Searcher<N>,
T extends SearchableTree<N, S, T>>
extends Tree<N, T> { ... }

我不建议效仿那个例子。 ;-)

关于Java 泛型——需要解释,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/10959310/

24 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com