I've long used an idiom in Java for using the class information of a (non-abstract) class in the methods of its (generally abstract) ancestor class(es) (unfortunately I can't find the name of this pattern):
public abstract class Abstract<T extends Abstract<T>> {
private final Class<T> subClass;
protected Abstract(Class<T> subClass) {
this.subClass = subClass;
}
protected T getSomethingElseWithSameType() {
....
}
}
An example of a subclass thereof:
public class NonGeneric extends Abstract<NonGeneric> {
public NonGeneric() {
super(NonGeneric.class);
}
}
However, I'm having trouble defining a subclass of Abstract
which has its own generic parameters:
public class Generic<T> extends Abstract<Generic<T>> {
public Generic() {
super(Generic.class);
}
}
This example is not compilable; likewise, it is not possible to specify the generic types using e.g. Generic<T>.class
or even to use a wildcard like Generic<?>
.
I also tried replacing the declaration of generic type T
in the superclass to ? extends T
, but that isn't compilable either.
Is there any way I can get this pattern to work with generic base classes?
Does a superclass have access to the members of a subclass? Does a subclass have access to the members of a superclass? No, a superclass has no knowledge of its subclasses.
Whenever you want to restrict the type parameter to subtypes of a particular class you can use the bounded type parameter. If you just specify a type (class) as bounded parameter, only sub types of that particular class are accepted by the current generic class.
Generics also provide type safety (ensuring that an operation is being performed on the right type of data before executing that operation). Hierarchical classifications are allowed by Inheritance. Superclass is a class that is inherited. The subclass is a class that does inherit.
A subclass inherits all the members (fields, methods, and nested classes) from its superclass. Constructors are not members, so they are not inherited by subclasses, but the constructor of the superclass can be invoked from the subclass.
The "pattern" (idiom) of passing an instance of Class<T>
(typically to the constructor) is using Class Literals as Runtime-Type Tokens, and is used to keep a runtime reference to the generic type, which is otherwise erased.
The solution is firstly to change the token class bound to:
Class<? extends T>
and then to put a similar requirement on your generic subclass as you did with your super class; have the concrete class pass a type token, but you can type it properly as a parameter:
These classes compile without casts or warnings:
public abstract class Abstract<T extends Abstract<T>> {
private final Class<? extends T> subClass;
protected Abstract(Class<? extends T> subClass) {
this.subClass = subClass;
}
}
public class NonGeneric extends Abstract<NonGeneric> {
public NonGeneric() {
super(NonGeneric.class);
}
}
public class Generic<T> extends Abstract<Generic<T>> {
public Generic(Class<? extends Generic<T>> clazz) {
super(clazz);
}
}
And finally at the concrete class, if you declare the usage as its own class, it doesn't require a cast anywhere:
public class IntegerGeneric extends Generic<Integer> {
public IntegerGeneric() {
super(IntegerGeneric.class);
}
}
I haven't figured out how to create an instance of Generic
(anonymous or not) without a cast:
// can someone fill in the parameters without a cast?
new Generic<Integer>(???); // typed direct instance
new Generic<Integer>(???) { }; // anonymous
I don't think it's possible, but I welcome being shown otherwise.
The major problem you have got here is, there is no class literal for concrete parameterized type. And that makes sense, since parameterized types don't have any runtime type information. So, you can only have class literal with raw types, in this case Generic.class
.
Reference:
Well, that's fine, but Generic.class
gives you a Class<Generic>
which is not compatible with Class<Generic<T>>
. A workaround is to find a way to convert it to Class<Generic<T>>
, but that too you can't do directly. You would have to add an intermediate cast to Class<?>
, which represents the family of all the instantiation of Class
. And then downcast to Class<Generic<T>>
, which will remove the compiler error, though you will an unchecked cast warning. You can annotate the constructor with @SuppressWarnings("unchecked")
to remove the warning.
class Generic<T> extends Abstract<Generic<T>> {
public Generic() {
super((Class<Generic<T>>)(Class<?>)Generic.class);
}
}
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