Given a class hierarchy where the base class defines a recursive self-type:
abstract class A<T extends A<T>> { }
How can I declare another class (which should not be generic in T, because such a T could vary over the lifetime of the object) with a field that can hold any subclass of A?
The following does not work:
public class B { //fails to compile, because the capture of ? is not sufficiently narrow private A<?> a; public <T extends A<T>> setA(T a) { this.a = a; } }
— END OF QUESTION —
I’ve noticed a tendency of a number of StackOverflow members to approach certain difficult questions with ‘why are you doing that in the first place?’ The following is a justification of my use of this pattern – you can note that the Java standard library also uses recursive self-types in its definition of the Enum class: Enum<E extends Enum<E>>. This question could similarly be asked as ‘how to define a field of type Enum<?>.
Justification example:
abstract class A<T extends A<T>> { public abtract T self(); public B<T> bify(Bifyer bifyer) { return bifyer.bify(self()); } }
with subclasses:
class ASub1 extends A<ASub1> { public ASub1 self() { return this; } } class ASub2 extends A<ASub2> { public ASub2 self() { return this; } }
bound to a parallel class hierarchy:
abstract class B<T extends A<T>> { } class BSub1<T extends A<T>> implements B<T> { } class BSub2<T extends A<T>> implements B<T> { } //and others
And with generation of B instances managed by implementations of a Bifyer interface:
interface Bifyer { B<ASub1> bify(ASub1 asub1); B<ASub2> bify(ASub2 asub2); }
Implementations of this interface may return a BSub1 or BSub2 for the B. This is essentially an application of the Visitor pattern where the Bifyer is the visitor, but unlike the standard Visitor the accept method returns a value instead of void. This provides a modular framework where different Bifyer implementations can be specified to provide alternate behavior and return types for the Bify method – say one for each subclass of B.
If you bound the wildcard
?below byA, it should work: