覆盖包含通用参数的方法

时间:2013-07-04 11:37:07

标签: java generics override

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的实例。但是,我们在类层次结构中是另一个级别,并且正确指定类型参数(显示为“ * ”)结果很难。

以下是一些可能性,所有这些都是有意义的,但没有一个可行:

  • &LT; DoesEvenMoreStuff.ElementB&GT;看起来这应该有效,因为ElementB扩展了ElementA,它本身扩展了Element

  • &LT; DoesStuff.Element&GT;在最坏的情况下,将所有内容都视为元素,但这也不起作用。

  • &LT ;? extends DoesStuff.Element&gt;无奈之下,说我们不关心班级是什么,只要它是元素的子类型;只要只读取ArrayList,就可以了,但它也不起作用。

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

  • &LT; DoesMoreStuff.ElementA&GT;这可行,大概是因为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&lt; DoesMoreStuff。 ElementA&gt;“,因为ArrayList将精确地包含内部类的元素。但是,编译器不再接受它。

P.S。对于任何对这种复杂类结构的应用感兴趣的人:最上层的类为数据库应用程序的离线模式提供通用数据缓存功能。第二类为大多数数据类提供数据缓存。第三类为需要特殊内容的特定类提供缓存。内部类是缓存数据元素。

2 个答案:

答案 0 :(得分: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>

在超类中键入参数DoesEvenMoreStuff.ElementB set E类型参数,但未使用。

超级类有这些方法,您可以覆盖这些方法,而无需更改类型参数,如开头的示例所示。

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

希望这有助于澄清事情。

答案 1 :(得分:0)

当DoesMoreStuff从DoesStuff&lt; DoesMoreStuff.ElementA&gt;扩展时,你会继承你的继承树。而不是来自DoesStuff&lt; E&gt;用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 {}
}

编译。

您不能同时破坏并继承泛型的继承树。