I have a set of classes that extend some base entity. Classes in this set may also extend from each other creating a nested hierarchy.
My goal is for all classes to have access to a method that creates a new instance of themselves. I want to implement this method in my base entity, so that all extending classes inherit this.
Here are three example classes defined to my pattern:
BaseEntity.java
public abstract class BaseEntity<E extends BaseEntity> {
Class<E> clazz;
public BaseEntity(Class<E> clazz) {
this.clazz = clazz;
}
public E getNewInstance() throws IllegalAccessException, InstantiationException {
return clazz.newInstance();
}
}
Collection.java
public class Collection<E extends Collection> extends BaseEntity<E> {
public Collection() {
super(Collection.class);
// compiler error: BaseEntity (java.lang.Class<E>) in BaseEntity cannot be applied to
// (java.lang.Class<Collection>)
}
public Collection(Class<E> clazz) {
super(clazz);
}
}
Document.java
public class Document extends Collection<Document> {
public Document() {
super(Document.class);
}
}
With this setup, I want to be able to do something like this:
Collection c = new Collection();
c = c.getNewInstance(); // compiler error
Document d = new Document();
d = d.getNewInstance();
Collection cd = new Document();
cd = cd.getNewInstance(); // compiler error
However note that there is a compiler error in the default constructor for Collection.java
. I'm not sure why this is being caused, and I think this is also causing the compiler errors in the sample main method. What am I doing incorrectly and how do I resolve this?
Note that this a contrived example pertaining to a bigger problem I'm trying to solve. I understand that this implementation by itself looks silly.
Collection<E...>
is a generic type, but your Collection c
is a raw type. That means that all of its methods will be treated as raw types, which means they'll return the erasure of any generic that's there.
Your base class is declared as BaseEntity<E extends BaseEntity>
, which means that in this method:
E getNewInstance()
the erasure is
BaseEntity getNewInstance();
That means that c.getNewInstance()
returns a BaseEntity
, not a Collection
, which is where your compilation error comes in.
Document
, on the other hand, is not a generic class. That means that the erasure doesn't matter at compile time (for these purposes), and that getNewInstance()
returns the type E
represents, which in this case is Document
. As such, d.getNewInstance()
has a return type of Document
, and so that line compiles fine.
As an aside: whenever you have recursive generics, you should make sure to account for the generic in the recursion. For instance, in this line:
BaseEntity<E extends BaseEntity>
you've defined BaseEntity
as a generic class -- but then immediately ignored its generic in E extends BaseEntity
. That line should instead be:
BaseEntity<E extends BaseEntity<E>>
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