Brad Richards
Brad Richards

Reputation: 1253

Overriding methods containing generic parameters

Generics in Java are not always obvious. I've come across a situation that I do not entirely understand - perhaps someone can help me out here. Consider the following three classes, each containing an inner class:

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 {}
}

Before we get to the third class, let us first look at MethodA in the second class. The type parameter for the ArrayList is "DoesMoreStuff.ElementA", which extends "DoesStuff.Element". This obviously works.

Now, in the third class, we want to override MethodB. In this class, we intend to work with instances of ElementB. However, we are another level down in the class hierarchy, and properly specifying the type parameter (shown as "*") turns out to be hard.

Here are some possibilities, all of which make some sense, and none of which work:

In fact, the only possibility seems to be:

Can anyone provide a clear explanation of this situation?

----- EDIT -----

The desired behavior for the second class is to be able to use the type parameter "DoesEvenMoreStuff.ElementB". The accepted answer below explains the problem: the second class has explicitly nailed the generic parameter of the top-most class, by setting it to "DoesMoreStuff.ElementA". Changing the second class as follows solves the problem:

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 {}
}

The one fly remaining in the ointment: In the second class, the declaration of MethodA is not really right: This really should be "MethodA(ArrayList<DoesMoreStuff.ElementA>", because the ArrayList will contain precisely elements of the inner class. This, however, is no longer acceptable to the compiler.

P.S. For anyone curious about the application of such a convoluted class structure: The uppermost class provides general-purpose data-caching abilities for the offline-mode of a database application. The second class provides data-caching for most data-classes. The third class provides caching for specific classes that need something special. The inner classes are the cache data-elements.

Upvotes: 1

Views: 163

Answers (2)

mmirwaldt
mmirwaldt

Reputation: 883

You break with your inheritance tree when DoesMoreStuff extends from DoesStuff<DoesMoreStuff.ElementA> instead of from DoesStuff<E> with E extends DoesMoreStuff.ElementB.

If you want the inheritance tree to succeed, then you need

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 {}
}

Then

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 {}
}

compiles.

You cannot break and succeed the inheritance tree of the generic at the same time.

Upvotes: 0

Tala
Tala

Reputation: 8928

Let me start with example:

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

In the second example if it was legal, you would be able to add DoesStuff objects into list of DoesMoreStuff.

The trick is in your DoesMoreStuff you have type Paramter E which is not used and you've already explicitly set type parameter and have these methods:

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

you can't overide them like this as described in the example above

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

Now coming to the third class:

public class DoesEvenMoreStuff extends DoesMoreStuff<DoesEvenMoreStuff.ElementB>

type parameter DoesEvenMoreStuff.ElementB set E type parameter in superclass which is not used.

Super class has these methods which you can override without changing type paramters as in the example in the beginning.

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

Hope that helped to clarify things.

Upvotes: 1

Related Questions