I would like to have the generic outer class with inner-class as parameter. I was expecting, that I will derive from base class and either use it's inner, or derive inner too. At each level I was expecting to limit inner class usage starting from current level of deriving.
Unfortunately, I meeting various errors and warnings when using the pattern so that I can't imagine, how to use it.
package tests.java;
public class Try_GenericInnerRecursion {
// base class, consisting of outer and inner parts
public static class Outer1<E extends Outer1<E>.Inner1> {
public class Inner1 {
}
public void addElement(E e) {
System.out.println("Added " + e.toString());
}
}
// extending outer, but not inner
public static class Outer2<E extends Outer1<E>.Inner1> extends Outer1<E>{
}
// extending both outer and inner
public static class Outer3<E extends Outer3<E>.Inner3> extends Outer1<E>{
public class Inner3 extends Inner1 {
}
}
// extending both outer and inner and stopping extension
public static class Outer4 extends Outer1<Outer4.Inner4> {
public class Inner4 extends Outer1<Inner4>.Inner1 {
}
}
// instantiating
public static void main(String[] args) {
Outer1<Outer1.Inner1> a1; // WARNING: Outer1.Inner1 is a raw type
a1 = new Outer1<Outer1.Inner1>(); // WARNING: Outer1.Inner1 is a raw type
Outer1<?> a2; // OK
a2 = new Outer1<?>(); // ERROR: Cannot instantiate the type Outer1<?>
Outer1<Outer1<?>.Inner1> a3; // ERROR: Bound mismatch: The type Outer1<?>.Inner1 is not a valid substitute for the bounded parameter <E extends Outer1<E>.Inner1> of the type Outer1<E>
Outer1<? extends Outer1<?>.Inner1> a4; // OK
a4 = new Outer1<Outer1.Inner1>(); // ERROR: Type mismatch
Outer2<Outer1.Inner1> b1; // WARNING: Outer1.Inner1 is a raw type
b1 = new Outer2<Outer1.Inner1>(); // WARNING: Outer1.Inner1 is a raw type
// and so on
}
}
How can I use this pattern correctly?
I believe you can simply do
DerivedCorpus1<?>
during wildcard capture, it becomes
DerivedCorpus1<x> where x extends Corpus<x>.Element
which is correctly bounded.
In your exmaple
DerivedCorpus1<? extends Corpus<?>.Element>
during wildcard capture, it becomes
DerivedCorpus1<x> where x extends Corpus<x>.Element
and x extends Corpus<?>.Element
apparently, the extends Corpus<?>.Element
clause is redundant.
Outer1<Outer1.Inner1> a1; // WARNING: Outer1.Inner1 is a raw type
Actually, I get "type argument Outer1.Inner1 is not within bounds of type-variable E`".
Outer1.Inner1
is a raw type, because Outer1
is a raw type. To use a not-raw type, you would need to write Outer1<something>.Inner1
. However, that something
would also have to extend Outer1<something>.Inner1
in turn. In order to have recursion like this you need a named recursive type. Unfortunately, since Inner1
is a non-static inner class, it has an implicit reference to an instance of Outer1
, and so any class that extends it would also need to have an enclosing instance of Outer1
. Outer4
and Inner4
basically do this.
Outer4 a1 = new Outer4(); // compiles fine
a2 = new Outer1<?>(); // ERROR: Cannot instantiate the type Outer1<?>
You can never do new something<?>()
.
Outer1<Outer1<?>.Inner1> a3; // ERROR: Bound mismatch: The type Outer1<?>.Inner1 is not a valid substitute for the bounded parameter <E extends Outer1<E>.Inner1> of the type Outer1<E>
This is true. Outer1<?>.Inner1
is not a subtype of Outer1<E>.Inner1
-- it's the other way around -- Outer1<E>.Inner1
is a subtype of Outer1<?>.Inner1
. This is just like how ArrayList<?>
is not a subtype of ArrayList<String>
; it's the other way around.
Outer1<? extends Outer1<?>.Inner1> a4; // OK
This is okay because you have a wildcard at the top level, and your wildcard's bound intersects the type parameter E
's bound. In fact, anything that satisfies E
's original bound must satisfy this bound, so this bound is useless, and this is the same as Outer1<?> a2;
above.
a4 = new Outer1<Outer1.Inner1>(); // ERROR: Type mismatch
Doesn't work, for, among other reasons, the same reason that a1
doesn't work (Outer1.Inner1
does not satisfy E
's bound). It additionally doesn't satisfy your bound (Outer1<?>.Inner1
) I believe.
Outer2<Outer1.Inner1> b1; // WARNING: Outer1.Inner1 is a raw type
This actually gives the same error as a1
, for the same reason
Generic specification
Before I go on to the instantiation problems I revise your generic definition:
Outer1<E extends Outer1<E>.Inner1>
Outer1 should basically hold elements of type Inner1 or any child classes of it. Therefore the type of the Outer1 object is not of relevance and can be replaced with the following code:
Outer1<E extends Outer1<?>.Innter1>
Also the extending classes have been slightly modified using the above mentioned.
Instantiation
For the instantiation of objects you stated the following:
Outer1<Outer1.Inner1> a1; // WARNING: Outer1.Inner1 is a raw type a1 = new Outer1<Outer1.Inner1>(); // WARNING: Outer1.Inner1 is a raw type
As newacct has already mentioned, Outer1
is declared with a generic type though you didn't specify any while instantiating the object. The code therefore should be changed to the following one:
Outer1<Outer1<?>.Innter1> a1; // or simply Outer1<?> a1;
a1 = new Outer1<Outer1<?>.Innter1>(); // or simply a1 = new Outer1<>();
Similar for assigning classes which extended a base class to a type of that base class.
You specify with the generic type definition what type of elements the instance will hold.
A declaration like
Outer1<Outer1<?>.Innter1> test;
will accept any instances extending Outer1
that also match the generic type. This means you can only assign
test = new Outer1<Outer1<?>.Inner1>();
test = new Outer2<Outer2<?>.Inner1>();
to it, but not Outer3
or even Outer4
!
Even if you modify the code further to add f.e.
public static class Outer5<E extends Outer1<?>.Inner1> extends Outer1<E>{
public class Inner5 extends Inner1 {
}
}
you are just able to add
test = new Outer5<Outer5<?>.Inner1>();
but not
test = new Outer5<Outer5<?>.Inner5>(); // FAILS!
to something that defined Inner1
as type argument.
However, there is actually a quite simple solution to this issue. If you define the object like this:
Outer1<? extends Outer1<?>.Inner1> test2;
you are actually able to do the following things:
test2 = new Outer1<Outer1<?>.Inner1>();
test2 = new Outer2<Outer2<?>.Inner1>();
test2 = new Outer3<Outer3<?>.Inner3>();
test2 = new Outer4();
test2 = new Outer5<Outer1<?>.Inner1>();
test2 = new Outer5<Outer5<?>.Inner1>();
test2 = new Outer5<Outer5<?>.Inner5>();
as you now explicitly told the compiler that either the Inner1
type itself or any of its extensions is allowed.
Code
I've updated your code and added a couple of new assignments to verify that the changes made actually do not produce any compile errors or warnings except for the redundant type arguments in new expression
due to explicitly specifying the type arguments instead of using the diamond operator <>
:
public class GenericInnerRecursion
{
// base class, consisting of outer and inner parts
public static class Outer1<E extends Outer1<?>.Inner1> {
public class Inner1 {
}
public void addElement(E e) {
System.out.println("Added " + e.toString());
}
}
// extending outer, but not inner
public static class Outer2<E extends Outer1<?>.Inner1> extends Outer1<E>{
}
// extending both outer and inner
public static class Outer3<E extends Outer3<?>.Inner3> extends Outer1<E>{
public class Inner3 extends Inner1 {
}
}
// extending both outer and inner and stopping extension
public static class Outer4 extends Outer1<Outer4.Inner4> {
public class Inner4 extends Outer1<Inner4>.Inner1 {
}
}
public static class Outer5<E extends Outer1<?>.Inner1> extends Outer1<E>{
public class Inner5 extends Inner1 {
}
}
// instantiating
public static void main(String[] args) {
Outer1<Outer1<?>.Inner1> a1;
a1 = new Outer1<Outer1<?>.Inner1>();
Outer1<?> a2;
a2 = new Outer1<>();
Outer1<Outer1<?>.Inner1> a3;
Outer1<? extends Outer1<?>.Inner1> a4;
a4 = new Outer1<Outer1<?>.Inner1>();
Outer2<Outer1<?>.Inner1> b1;
b1 = new Outer2<Outer1<?>.Inner1>();
// and so on
// assigning extension-classes to the parent-class
Outer1<Outer1<?>.Inner1> c1;
c1 = new Outer2<Outer2<?>.Inner1>();
// assigning inner-extension-classes to parent-class
Outer1<Outer3<?>.Inner3> c2;
c2 = new Outer3<Outer3<?>.Inner3>();
// assigning extension class without specified generics to parent class
Outer1<Outer4.Inner4> c3;
c3 = new Outer4();
Outer1<Outer1<?>.Inner1> test;
test = new Outer1<>();
test = new Outer2<>();
test = new Outer5<Outer5<?>.Inner1>();
Outer1<? extends Outer1<?>.Inner1> test2;
test2 = new Outer1<Outer1<?>.Inner1>();
test2 = new Outer2<Outer2<?>.Inner1>();
test2 = new Outer3<Outer3<?>.Inner3>();
// new Outer3<Outer3<?>.Inner1>(); not possible as generic type extends Outer3<?>.Inner3 and not Outer1<?>.Inner1!
test2 = new Outer4();
test2 = new Outer5<Outer1<?>.Inner1>();
test2 = new Outer5<Outer5<?>.Inner1>();
test2 = new Outer5<Outer5<?>.Inner5>();
}
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With