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>>
The problem with this constructor
public Collection() {
super(Collection.class);
}
Is that the superclass constructor is expecting a Class<E>
, but the class literal Collection.class
is a Class<Collection>
. These types are incompatible, because E
could be a Collection
, a Document
, or anything else that might extend Collection
.
Any class like Document
that extends Collection
must supply its own class, so it will be calling the other Collection
constructor that takes a Class<E>
anyway, so I don't think the Collection()
constructor has any use. I would remove it.
Also, in your upper bound for E
, you are using the raw form of the very classes you are attempting to make generic. Use
public abstract class BaseEntity<E extends BaseEntity<E>> {
and
public class Collection<E extends Collection<E>> extends BaseEntity<E> {
The type Collection
is generic, so you must specify the generic type parameter that matches the argument to the Collection
constructor.
Collection<Document> c = new Collection<Document>(Document.class);
c = c.getNewInstance();
Document is not itself generic, so this code is still fine:
Document d = new Document();
d = d.getNewInstance();
Document
must be supplied as a type argument to Collection
even when directly creating a Document
, because a Document
is a Collection<Document>
.
Collection<Document> cd = new Document();
cd = cd.getNewInstance();
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.