I have a generic repository class that looks like this:
public class HibernateRepository<E extends Object, EId>
implements EntityRepository<E, EId> {
// Many things that are working fine
#Override
public E getById(EId id) {
return entityManager.getReference(E.class, id); // <<< Error here!
}
}
The method getById() is supposed to return a certain entity instance given its Id. Something very trivial in many other situations, but not in this one, since the compiler returns:
Illegal class literal for the type parameter E
Notice that I don't want to pass the desired class to the method, since I already did that in the class definition.
The usage for this class could be:
public class MyClassRepositoryHibernate
extends HibernateRepository<MyClass, Long> {
}
Now this new repository works on MyClass instances and the Id is typed with Long.
Is that possible in Java?
You will have to pass the actual class type to the constructor of your HibernateRepository and use that throughout your methods. The JVM has no knowledge of what "E" is at runtime hence you need to provide the concrete class type.
You can ensure you don't instantiate the object incorrectly by using the Generic parameter type in the constructor, like this:
public class HibernateRepository<E extends Object, EId>
implements EntityRepository<E, EId> {
private Class<E> clazz;
public HibernateRepository(Class<E> clazz) {
this.clazz = clazz;
}
#Override
public E getById(EId id) {
return entityManager.getReference(clazz, id); // <<< Error here!
}
}
A pain, but a necessity due to type erasure.
The type will be erased after compilation and thus is not available. This is called Type Erasure. You have to pass the Class object somewhere.
Related
I have the following, small interface:
public interface EntityController<T> {
public void update( float elapsed, T applyTo );
}
Which I want to use in the following way:
private Map<Class<? extends GameObject>, EntityController<?>> registeredControllers;
public EntityController<?> getController(GameObject o) {
return registeredControllers.get(o.getClass());;
}
...
getController(myObj).update(elapsed, myObj);
The last line gives me the error that The method update(float, capture#1-of ?) in the type EntityController<capture#1-of ?> is not applicable for the arguments (float, GameObject)
Why is that? Basically, what I want to achieve is the following:
I know that each EntityController is responsible only for handling one specific type of class. Therefore, in the update method of it I will always have to cast my GameObject to the respective type, which is annoying, and I guess also generates some kind of overhead? I thought generics would be a nice way of solving the problem, allowing me to create specific controllers in the following manner:
public class MyController implements EntityController<MyType> {
public void update(float elapsed, MyType applyTo){}
}
Why isn't that possible?
To put it simply, you cannot assign a value to a wildcard type, because the actual type is unknown. So if the return type is EntityController<?>, you cannot assign to update's 2nd argument.
The easiest (but not 100% type safe) solution would be
private Map<Class<? extends GameObject>, EntityController<? extends GameObject>> registeredControllers;
#SuppressWarnings("unchecked")
public <T extends GameObject> EntityController<T> getController(T o) {
return (EntityController<T>) registeredControllers.get(o.getClass());
}
Example:
class AbstractConverter <T> {
public abstract T convert(AbstractEntity e);
}
class CityEntity extends AbstractEntity {...}
class CityConverter extends AbstractConverter {
#Override
public CityDTO convert(CityEntity entity) {...} // why can't I do that??
}
As my CityEntity is of type AbstractEntity, why can't I do that in my cityConverter?
The method convert(CityEntity) of type CityConverter must override or implement a supertype method
I guess the solution is to cast, but it's not ellegant:
#Override
public CityDTO convert(AbstractEntity entity) {
CityEntity cityEntity = (CityEntity) entity;
}
You can't reduce the allowed parameter type, because you would break Liskov substitution principle. If one calls convert on an unknown AbstractConverter implementation (due to polymorphy), (s)he would guess that he can always pass any AbstractEntity implementation. This wouldn't be the case if CityConverter only allows a very specific subtype.
Now about your code:
class AbstractConverter <T> {
public abstract T convert(AbstractEntity e);
}
The first thing that wonders me here is: why is AbstractEntity a fixed type here? I would name the converter class AbstractEntityConverter or something like this if I always want to convert AbstractEntity instances into something different.
So I guess you really want something like this:
class AbstractConverter<F, T> {
public abstract T convert(F source);
}
Where F is the "from" type which acts as the source and T is the target type, which will be returned. So you can let the AbstractConverter subtypes decide what they likes to convert.
Then you have this:
class CityConverter extends AbstractConverter {
}
Why do you use AbstractConverter as a raw type here? Shouldn't it be?
class CityConverter extends AbstractConverter<CityDTO> {
}
But anyway, if you like to use my suggestion, then also add the source type:
class CityConverter extends AbstractConverter<CityEntity, CityDTO> {
#Override
public CityDTO convert(CityEntity entity) {...}
}
This would work.
Your CityConverter extends AbstractConverter and AbstractConverter<T> requires implementation for
public abstract T convert(AbstractEntity e);
It is not a problem that extends AbstractConverter uses raw-types because overridden method can be more specific in case of returned object (since it is still of type described by parent class).
But problem appears when in derived class you want to require more specific type as argument.
Remember that derived class can still be used from reference which is of one of parents type like it is possible to have code like:
AbstractConverter ac = new CityConverter();
So when we will invoke ac.convert(...) compiler will allow us to use any type of AbstractEntity as argument, not only CityEntity which could brake code of CityConverter#convert.
Which is why we can't declare more specific type of method argument when overriding it.
Now about question from your title:
Why do I need cast in this case?
...
CityEntity cityEntity = (CityEntity) entity;
you need casting because entity is declared to be AbstractEntity which means there is a chance that passed instance may not be of type CityEntity so compiler can't compile code like:
CityEntity cityEntity = entity;//error without cast
By casting you are basically saying "I am sure that passed instance will be of type (CityEntity) and if not, I am willing to face consequences and am ready to see my application stopped by ClassCastException".
I got an interesting issue. Consider the following code:
public class GenericsTest
{
// An interface with a generic type.
public interface IObject<K>{}
// An class with a generic type
public static class ObjectA<K>
{
// An inner class without generic type, but implementing the interface with generic Type
// When adding a genericType to this class, it will popup the warning: 'hiding'
public class ObjectB implements IObject<K>
{
}
// A getter with the interface as return Type
public IObject<K> getObjectB()
{
return new ObjectB();
}
}
public ObjectA<String> objectA = new ObjectA<String>();
// This field is yelling for an genericType, though it can't get one because the class doesn't support a generic argument.
public ObjectB genericObject = (ObjectB)objectA.getObjectB();
}
So the issue is that my IDE is complaining about a missing genericType of the genericObject field, and that I should add a SupressWarning annotation to the method. (luckily not code breaking, though still pretty annoying).
I could add a generic type to the inner class, though than it would 'hide' a generic argument, meaning I would need to add a SupressWarning annotation there.
A second fix would be to use a second generic type like <S extends K>. In which case I don't need a SupressWarning annotation at the class. Though when I try to use the getter, my IDE is complaining:
The member type GenericsTest.ObjectA.ObjectB<String> must be qualified with a parameterized type, since it is not static.
So basically I can't use the getter, unless I add an argument of the genericType to the method.
My question is, what is the cleanest way to solve this problem without changing the inner class to a nested class?
Here's a short example that compiles with no issues:
public class Test
{
interface K<T> { }
static class A<T>
{
class B implements K<T> { }
public K<T> getK() { return new B(); }
}
A<String> a = new A<String>();
A<String>.B b = (A<String>.B) a.getK();
}
Notice the last line:
A<String>.B b = (A<String>.B) a.getK();
To be honest, I'm not sure how the example you've given even compiles as far as it does - the class 'ObjectB' is not visible from the main 'GenericsTest' scope, it needs to be prefixed with its' parent class.
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();
I have lost in the Jungle of Generics, please help me :) I have something like this:
public class BaseClass<TYPE> {
public BaseClass(Class<TYPE> clazz) {};
}
public class FirstLevelClass<REFRESHABLE
extends RefreshableInterface> extends BaseClass<REFRESHABLE> {
public FirstLevelClass(Class<REFRESHABLE> clazz) {
super(clazz);
};
}
public class Argument<T extends AnyOtherClass>
implements RefreshableInterface {
public refresh() {}
}
pulbic class ProblematicClass
extends FirstLevelClass<Argument<AnyOtherClassDescendant>> {
public ProblematicClass() {
//Compiler error: Constructor
//FirstLevelClass<Argument<AnyOtherClassDescendant>>(Class<Argument>) is undefined
super(Argument.class);
}
}
As far as I think, the compiler should accept Argument since it implements RefreshableInterface.
Why do I get this error?
How can I make the ProblematicClass working?
ps: if you have better title for this, please change it. I could not make up better.
Issue is, your constructor expects a Class<T>, and T in your code is inferred as Argument<AnyOtherClassDescendant>.
So, you should pass a Class<Argument<AnyOtherClassDescendant>>, and you're passing Class<Argument>. But you can't pass that Class instance directly, as you cannot do Argument<AnyOtherClassDescendant>.class.
You can however, solve the issue by typecasting the class to required instance:
public ProblematicClass() {
super((Class<Argument<AnyOtherClassDescendant>>)(Class<?>)Argument.class);
}
Note, how you've to typecast Class<Argument> first to Class<?>, and then the resultant type to Class<Argument<AnyOtherClassDescendant>>. There is no simple way to achieve that.
The reason behind this is, there is only a single Class instance for all parameterized instantiation of a generic type, that is associated with the class itself. A single compilation unit of a generic type, compiles to just a single class file. I guess this is different in how C++ implements templates. There you get different machine codes for different instantiation.
So, if you execute the below code, you'll get true as output:
List<String> strList = new ArrayList<String>();
List<Integer> intList = new ArrayList<Integer>();
boolean isSameClassInstance = strList.getClass() == intList.getClass();
System.out.println(isSameClassInstance);