Reputation: 2283
public abstract class BaseClass<T extends BaseClass<T>> {
T method1(){
return getThis();
}
public abstract T getThis();
}
public class SubClass extends BaseClass<SubClass> {
public SubClass getThis(){
return this;
}
}
If it's just one level of inheritance i can do something like the above and get a reference of SubClass when I call method1().
What if I have inheritance at two levels like
public abstract class SubClass1<T extends SubClass1<T>> extends BaseClass<SubClass1<T>> {
@Override
public SubClass1<T> getThis() {
return this;
}
}
public class SubSubClass1 extends SubClass1<SubSubClass1> {
}
what should I change to the method1 and BaseClass so that when method1 is called I can get back SubSubClass1 type. I wan't to do this without any suppress warnings
Upvotes: 4
Views: 1626
Reputation: 122429
SubSubClass1
must be the one implementing getThis()
, like this:
public abstract class BaseClass<T> {
T method1(){
return getThis();
}
public abstract T getThis();
}
public abstract class SubClass1<T> extends BaseClass<T> {
}
public class SubSubClass1 extends SubClass1<SubSubClass1> {
@Override
public SubSubClass1 getThis() {
return this;
}
}
Upvotes: 1
Reputation: 180141
It is important to understand that each type-parameterized class's type parameters are its own. In particular, they (the parameters themselves) are not shared with super- or subclasses. It is common for type-parameterized subclasses to intentionally share type parameter values with their superclasses, but that's a different thing.
Classes get to specify their superclasses, but they do not get to alter their superclass' own inheritance. Thus, if SubClass1
extends BaseClass<SubClass1<T>>
then BaseClass
's type parameter for class SubClass1<T>
and every subclass thereof is SubClass1<T>
, because that class alone is the one that SubClass1<T>
extends.
Any concrete subclass of your BaseClass<T>
must implement its abstract method getThis()
, and that requires such a class (e.g. SubClass1
) to specify BaseClass
's type parameter T
as an instantiable type (else it cannot provide a return value). From that point on down the hierarchy, subclasses can employ covariant return types to narrow the type returned by getThis()
, but they cannot change the BaseClass
type parameter specified by SubClass1
, except to the extent that it depends on SubClass1
's own type parameter(s). For example, this compiles cleanly:
public abstract class BaseClass<T extends BaseClass<T>> {
// ...
public abstract T getThis();
}
public abstract class SubClass1<T extends SubClass1<T>> extends BaseClass<SubClass1<T>> {
@Override
public SubClass1<T> getThis() {
return this;
}
}
public class SubSubClass1 extends SubClass1<SubSubClass1> {
@Override
public SubSubClass1 getThis() {
return this;
}
}
But that's an exercise of covariant return types, not type parameterization, so this also works:
public abstract class BaseClass {
// ...
public abstract BaseClass getThis();
}
public abstract class SubClass1 extends BaseClass {
@Override
public SubClass1 getThis() {
return this;
}
}
public class SubSubClass1 extends SubClass1 {
@Override
public SubSubClass1 getThis() {
return this;
}
}
Props to @HannoBinder who first suggested covariant return types in the comments.
Note that if the main idea here was use a type parameter to mark which descendant of BaseClass
you actually have -- or at least an upper bound on that -- then that's wrongheaded. In that approach, the type BaseClass<SomeSubClass>
is no more meaningful or expressive than the type SomeSubClass
itself. The parameterized version is distinguished only by being harder to work with.
Upvotes: 2