Reputation: 1253
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:
<DoesEvenMoreStuff.ElementB> Seems like this ought to work, since ElementB extends ElementA, which itself extends Element
<DoesStuff.Element> In the worst case, treat everything like Element, but this also doesn't work.
<? extends DoesStuff.Element> In desperation, say that we don't care what the class is, as long as its a subtype of Element; as long as the ArrayList is only going to be read, this would be ok, but it doesn't work either.
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
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
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