1

Java 中的泛型并不总是显而易见的。我遇到了一个我不完全理解的情况 - 也许有人可以在这里帮助我。考虑以下三个类,每个类都包含一个内部类:

public class DoesStuff<E extends DoesStuff.Element> {
    ArrayList<E> elements;
    public void MethodA(ArrayList<E> moreElements) {}
    public void MethodB(ArrayList<E> moreElements) {}

    public static class Element {}
}

public class DoesMoreStuff<E extends DoesMoreStuff.ElementA> extends DoesStuff<DoesMoreStuff.ElementA> {
    ArrayList<DoesMoreStuff.ElementA> otherElements;

    @Override
    public void MethodA(ArrayList<DoesMoreStuff.ElementA> moreElements) {}

    public static class ElementA extends DoesStuff.Element {}
}

public class DoesEvenMoreStuff extends DoesMoreStuff<DoesEvenMoreStuff.ElementB> {
    ArrayList<DoesEvenMoreStuff.ElementB> stillOtherElements;

    @Override
    public void MethodB(ArrayList<*****> moreElements) {}

    public static class ElementB extends DoesMoreStuff.ElementA {}
}

在进入第三节课之前,让我们先看看第二节课中的MethodA。ArrayList 的类型参数是“DoesMoreStuff.ElementA”,它扩展了“DoesStuff.Element”。这显然有效。

现在,在第三个类中,我们要覆盖 MethodB。在这个类中,我们打算使用 ElementB 的实例。但是,我们在类层次结构中处于另一个层次,并且正确指定类型参数(显示为“ * ”)变得很困难。

以下是一些可能性,所有这些都有意义,但都不起作用:

  • <DoesEvenMoreStuff.ElementB> 似乎这应该有效,因为 ElementB 扩展了 ElementA,它本身扩展了 Element

  • <DoesStuff.Element> 在最坏的情况下,将所有内容都视为 Element,但这也行不通。

  • <? extends DoesStuff.Element> 无奈之下,说我们不在乎这个类是什么,只要它是Element的子类型;只要 ArrayList 只会被读取,就可以了,但它也不起作用。

事实上,唯一的可能似乎是:

  • <DoesMoreStuff.ElementA> 这行得通,大概是因为 ElementA 直接扩展了 Element,因此匹配顶级类中的原始声明。但是,这似乎不合逻辑,至少对我来说不是。

任何人都可以对这种情况提供明确的解释吗?

- - - 编辑 - - -

第二类的期望行为是能够使用类型参数“DoesEvenMoreStuff.ElementB”。下面接受的答案解释了这个问题:第二个类通过将其设置为“DoesMoreStuff.ElementA”明确地确定了最顶层类的通用参数。如下更改第二个类可以解决问题:

public class DoesMoreStuff<E extends DoesMoreStuff.ElementA> extends DoesStuff<E> {
    ArrayList<E> otherElements;

    @Override
    public void MethodA(ArrayList<E> moreElements) {}

    public static class ElementA extends DoesStuff.Element {}
}

美中不足的是:在第二个类中,MethodA 的声明并不正确:这确实应该是“MethodA(ArrayList<DoesMoreStuff.ElementA>”,因为 ArrayList 将包含内部类的元素。这个但是,编译器不再接受。

PS 对于任何对这种复杂类结构的应用感到好奇的人:最上面的类为数据库应用程序的离线模式提供通用数据缓存能力。第二类为大多数数据类提供数据缓存。第三个类为需要特殊的特定类提供缓存。内部类是缓存数据元素。

4

2 回答 2

1

让我从例子开始:

List<DoesStuff> a = new LinkedList<DoesStuff>;
LinkedList<DoesStuff> b = new LinkedList<DoesMoreStuff>; //doesn't work. 

在第二个示例中,如果它是合法的,您将能够将DoesStuff对象添加到DoesMoreStuff.

诀窍在于DoesMoreStuff您有E未使用的类型参数,并且您已经明确设置了类型参数并具有以下方法:

public void MethodA(ArrayList<DoesMoreStuff.ElementA> moreElements) {}
public void MethodB(ArrayList<DoesMoreStuff.ElementA> moreElements) {}

你不能像上面例子中描述的那样覆盖它们

@Override
public void MethodA(ArrayList<E> moreElements) {} // as described in the example above

现在进入第三节课:

public class DoesEvenMoreStuff extends DoesMoreStuff<DoesEvenMoreStuff.ElementB>

type parameter DoesEvenMoreStuff.ElementBsetE超类中未使用的类型参数。

超类具有这些方法,您可以在不更改类型参数的情况下覆盖这些方法,如开头示例中所示。

public void MethodA(ArrayList<DoesMoreStuff.ElementA> moreElements) {}
public void MethodB(ArrayList<DoesMoreStuff.ElementA> moreElements) {}

希望这有助于澄清事情。

于 2013-07-04T12:07:23.053 回答
0

当DoesMoreStuff 从DoesStuff<DoesMoreStuff.ElementA> 扩展而不是从DoesStuff<E> 扩展而E 扩展DoesMoreStuff.ElementB 时,您的继承树会中断。

如果您希望继承树成功,那么您需要

public class DoesMoreStuff<E extends DoesMoreStuff.ElementA> extends DoesStuff<E> {
    ArrayList<DoesMoreStuff.ElementA> otherElements;

    @Override
    public void MethodA(ArrayList<E> moreElements) {}

    public static class ElementA extends DoesStuff.Element {}
}

然后

public class DoesEvenMoreStuff extends DoesMoreStuff<DoesEvenMoreStuff.ElementB> {
    ArrayList<DoesEvenMoreStuff.ElementB> stillOtherElements;

    @Override
    public void MethodB(ArrayList<DoesEvenMoreStuff.ElementB> moreElements) {}

    public static class ElementB extends DoesMoreStuff.ElementA {}
}

编译。

你不能同时打破和继承泛型的继承树。

于 2013-07-04T12:12:07.253 回答