Extending generics in method parameter - java

I'm trying to extend a library that uses generics in entities and entities manager, so I have the following:
public class MyEntity extends ParentEntity extends BaseEntity
and
public class MyEntityManager extends ParentEntityManager extends BaseEntityManager<ParentEntity, ParentEntityDao>
Now my problem is, I'm using another class of the library to update the entity, and the method I need to call expects:
public <ENT extends BaseEntity> void update (Class<ENT> entityClass, BaseEntityManager<ENT> entityManager)
So when I try to call the method with MyEntity and MyEntityManager, it fails because my EntityManager extends from the BaseEntityManager with ParentEntity as parameter, not MyEntity, so they don't match.
I would say that the cleanest way to solve this would be to copy the utility class that has the update method and extend it in my project, but I would like to make it generic so I can pass it EntityManagers that use any children class of ParentEntity, but I've been trying for a while and I cannot find the solution.
I tried changing the method signature to this:
public <ENT extends BaseEntity> void update (Class<ENT> entityClass, BaseEntityManager<? extends ENT> entityManager)
but I'm still getting a compiler exception...
EDIT: Modifying ParentEntityManager, BaseEntityManager or ParentEntity is not possible, since there are too many dependencies to those classes

Since it doesn't appear you can easily fix the problem I would use a work around. You can use type erasure.
update((Class) MyEntity.class, myEntityManager);
This will compile with a warning. The warning is valid IMHO as your class structure isn't entirely logical ;) You can suppress this warning if you want.
What I do is; the container class returns the type of object it contains/manages.
class BaseEntityManager<E> {
private final Class<E> typeManaged;
public Class<E> typeManaged() { return typeManaged; }
}
// remove duplicate class parameter
public <ENT extends BaseEntity> void update (BaseEntityManager<ENT> entityManager)
The manager know what type it manages and the update() method can ask it by calling typeManaged(). This avoid having to give a matching type or for this type to be checked.
The problem you have is that
MyEntityManager extends BaseEntityManager<ParentEntity>
Which mean the class you have to provide is ParentEntity.class not MyEntity.class.
It appears the real solution is that you need to change
MyEntityManager extends BaseEntityManager<MyEntity>
This would be more logical IMHO and fix your problem.

You need to call the update method with ParentEntity.class and a MyEntityManager, e.g.
update(ParentEntity.class, new MyEntityManager());
because your MyEntityManager is a BaseEntityManager<ParentEntity>.
If this is not what you want (because for example update creates new objects of entityClass and you want them to be MyEntity instances), you need to change the signature of the update method and introduce wildcards:
public <ENT extends BaseEntity> void update (Class<? extends ENT> entityClass, BaseEntityManager<ENT> entityManager)
Now you can call it with MyEntity.class and a MyEntityManager.

I think if you look replace the Manager with Container then the problem becomes apparent: MyEntityManager manages all sorts of super ParentEntity classes, while the update method wants one catering specifically to the subclass.
Does the following work for you? It compiles but lint complains
class BaseEntity {}
class BaseEntityManager<ENT extends BaseEntity> {}
class ParentEntity extends BaseEntity {}
class ParentEntityManager extends BaseEntityManager<ParentEntity> {}
class MyEntityManager extends ParentEntityManager { public void update(ParentEntity e) {}}
class MyEntity extends ParentEntity {}
class XEntityManager<ENT extends ParentEntity> extends BaseEntityManager<ENT>
{
MyEntityManager _delegate;
XEntityManager(MyEntityManager m)
{
_delegate = m;
}
public void update(ENT e)
{
_delegate.update(e);
}
}
class Foo {
public <ENT extends BaseEntity> void update (Class<ENT> entityClass, BaseEntityManager<ENT> entityManager) {}
void bar(MyEntity e, MyEntityManager m)
{
update(MyEntity.class, new XEntityManager(m));
}
}

Related

Java : generic type with type parameters?

I have a class like this :
public class MyClass <T extends Model1, X extends Model2> {
private CommonMessage<T,X> someMethod() {
....
}
}
Now I have a customized message type MyMessage extends CommonMessage, so I want to know how to have generic type that still having T and X as parameters ? For example :
public class MyClass <M extends CommonMessage, T extends Model1, X extends Model2> {
private M<T,X> someMethod() {
....
}
}
Short answer:
First of all as CommonMessage is generic, extending it in a non-generic way is very bad so you should have done M extends CommonMessage<T, X> And this way because type parameter passed to CommonMessage at class declaration you should not mention this parameter type again at method return type so method return type should be M.
Long answer:
I know you do know this definitions but sometimes we as human forget simple things. First we should consider what generics are created for, with generics we can create classes with different parameter types, this parameter types will be provided when they are extended by another class or when we create new instance of them with new() operator, so when we are writing our class we don't know the exact type for those parameter and we want to delay this decision until later, it is contradictory to something you are doing in your class because here your method is private and you can't change its implementation in your child class(the class which inherited from your class). But know we can change your implementation to something like this which will be compiled well:
public class MyClass<M extends CommonMessage<T, X>, T extends Model1, X extends Model2> {
private M method1(){
...
}
}
public class CommonMessage<T, X>{
}
public class MyMessage<T, X> extends CommonMessage<T, X>{
}
public class Model1{
}
public class Model2{
}
although this implementation will be compiled the problem is that when you are writing your private method(method1) you don't know what is the type of M at the time of writing this class because it will be passed when we want to create new instance of this class or when we inherit another class from this class. so what type of Object do you want to create and return in your method1? the only thing that you know here is that its type is M which extends CommonMessage but you don't know what the exact type of M is at the time of writing your private method(method1)!
And on the top of that you can't delegate this decision to your subclass(because this method is private). Now the question is that why it is allowed and compiled well when we don't know the exact type of M? for a moment forget this question I will make it clear after explaining correct approach. so what is the correct approach? Think about it, the person who write subclass does know exactly what the type of parameter M is and they can create appropriate instance of M in implementation of method1 to return from this method. so why not delegate this decision to subclass and making this method abstract? This completely make senses. in a nutshell we have some implementation like this:
public abstract class MyClass<M extends CommonMessage<T, X>, T extends Model1, X extends Model2> {
public abstract M method1();
}
public class CommonMessage<T, X>{
}
public class MyMessage<T, X> extends CommonMessage<T, X>{
}
public class Model1{
}
public class Model2{
}
now lets get back to our question why first program that I suggested to you compiled well? why we are allowed to have private method that its return type is generic that will be passed at instanciation or inheritance time?
because there are a lot of situations that make it correct and appropriate.
one situation is that our private method call another public method which return the appropriate type, like this:
public abstract class MyClass<M extends CommonMessage<T, X>, T extends Model1, X extends Model2> {
private M method1(){
return method2();
}
abstract M method2();
}
public class CommonMessage<T, X>{
}
public class MyMessage<T, X> extends CommonMessage<T, X>{
}
public class Model1{
}
public class Model2{
}

How to pass generics to an Spring CRUD Repository's save method

Let's say we have three JPA objects with name and id. I made an interface with getters+setters for name and id.
class Car implements MetadataObject
class Bus implements MetadataObject
class Train implements MetadataObject
We also have three repositories for these JPA objects:
interface CarRepository extends CrudRepository<Car, Long>
interface BusRepository extends CrudRepository<Bus, Long>
interface TrainRepository extends CrudRepository<Train, Long>
For each of these objects we want to run the same method in an spring service. (highly simplified)
private void importMetadata(CrudRepository<? extends MetadataObject, String> mRepository) {
Optional<? extends MetadataObject> currentOptional = mRepository.findById(1);
if (currentOptional.isPresent()) {
MetadataObject current = (MetadataObject) currentOptional.get();
current.setName("a1");
mRepository.save(current);
}
}
Which is callable by the same spring service by
#Autowired
private CarRepository carRepository;
...
importMetadata(carRepository);
This results in the error:
The method save(S) in the type CrudRepository<capture#4-of ? extends MetadataObject, Long> is not applicable for the arguments (MetadataObject)
Which is odd if I look at Springs CRUD Repository: CrudRepository<T, ID> and its save method: <S extends T> S save(S entity);.
In our example we have T = ? extends MetadataObject and S = ? extends ? extends MetadataObjects.
If we change my function to private void importMetadata(CrudRepository<MetadataObject, String> bdbRepository) the save method is correct but I can't call the method with my carRepository anymore
The method importMetadata(CrudRepository<MetadataObject,String>) in the type <...> is not applicable for the arguments (CarRepository)
Be aware: I highly simplified the example. I know that in this example Interfaces for these JPA classes makes no sense. I also know that my method makes no sense but it highlights the problem perfectly.
My question would be: What to pass to save or how to rewrite this function that it works? What exactly is the issue here?
You can use this method definition:
private void <T extends MetadataObject>importMetadata(CrudRepository<T, String> mRepository) {
Optional<T> currentOptional = mRepository.findById(1);
if (currentOptional.isPresent()) {
T current = currentOptional.get();
current.setName("a1");
mRepository.save(current);
}
}
I will try the explain the reason the method save of CrudRepository<? extends MetadataObject, String> mRepository fails.
Suppose we have a generic class:
class C<T> {
public void save (T t) {
// .. whatever
}
}
When we write something like:
void f (C<? extends Object> c) {
c.save(new Object());
}
the compiler complains about the c.save line.
That's because, when applying the type restriction, the compile doesn't know whether the c reference points, in fact, to C<Object> or C<Number> or whatever, since both C<Object> and C<Number> are accepted as arguments for f.
Because of that, the compiler doesn't know whether the save method's argument is allowed or not, hence the error.

Confused with Spring Data JPA and generic types

Tables:
StudentHistory 1--->n Student
TeacherHistory 1--->n Teacher
I try to regroup the JPA behaviour of History because they do the same thing (retrieve the students/teacher from a given history for example).
Entities with generic types:
// Entities
public abstract class AbstractHistory <T> {}
public class StudentHistory extends AbstractHistory<Student> {}
public class TeacherHistory extends AbstractHistory<Teacher> {}
Repositories with genric types:
// repositories
public interface IHistoryRepository<T> extends CrudRepository<AbstractHistory<T>, Long> {
public AbstractHistory<T> findFirst();
}
public interface StudentHistoryRepository extends IHistoryRepository<Student> {}
public interface TeacherHistoryRepository extends IHistoryRepository<Teacher> {}
I though I could do:
StudentHistory stuHisto = new StudentHistoryRepository().findFirst();
But I get this error:
// err -> Type mismatch: cannot convert from AbstractHistory<Student> to StudentHistory
1/ Why can't I retrieve a 'StudentHistory' from my 'StudentHistoryRepository' ?
2/ How should I deal whith that?
You have this problem because your method explicitly returns an AbstractHistory and not the subtype.
You would need to cast...
... if only your repository implementation understood that each T you get a specific history.
You may try adding another type but I fear that it'll fail:
public interface IHistoryRepository<
T,
H extends AbstractHistory<T>
> extends CrudRepository<H, Long> {
public H findFirst();
}
public interface StudentHistoryRepository extends IHistoryRepository<Student, StudentHistory> {}
public interface TeacherHistoryRepository extends IHistoryRepository<Teacher, TeacherHistory> {}
I don't know what framework you are using, probably Spring Data from the names; while I had used it in the past, I don't know if it is able to do that.
After all, it needs to get the concrete class and since it is generics, type erasure may interfere (if the information about the concrete type representing H is lost in reflection then Spring Data won't probably be able to do much here, unless you help it with an annotation or something else).
Another solution that should work is to do that per each child interface instead:
public interface StudentHistoryRepository extends CrudRepository<StudentHistory, Long> {
StudentHistory findFirst();
}
Or with another interface:
public interface FindFirst<T> {
T findFirst();
}
public interface StudentHistoryRepository extends CrudRepository<StudentHistory, Long>, FindFirst<StudentHistory> {}

Generic function names in java 8

I have few entities in my app and I want to have a functionality where I can generically set few properties on them when user tries to save them.
For this, I have attempted writing a utility class as follows:
public class IrisUserUtil<T> {
IrisUserRepository irisUserRepository;
public IrisUserUtil(IrisUserRepository irisUserRepository){
this.irisUserRepository = irisUserRepository;
}
public <S extends T> S populate(S entity) {
//IrisUser irisuser = irisUserRepository.findOne(entity.getIrisUserId());
//entity.setFirstName(irisuser.getFirstName());
//entity.setLastName(irisuser.getLastName());
return entity;
}
}
This I will call just before my repository.save() ; Here I am looking for correct way to call functions like entity.getIrisUserId() etc on the passed parameter that is "entity". Of course, this is not working.
please suggest why?
maybe with <S extends Entity> and not T what is T here ?
How the compilater know that S is something extending the Entity class (or an other base class) ?
public <S extends T> S populate(S entity)
In your code S and T are somethings extending Object, the compilater can't known your methods getIrisUserId(), getFirstName() etc...
I think you have to remove your generic T in the class and just do
public <S extends Entity> S populate(S entity)
or (but useless and more complicated)
public class IrisUserUtil<T extends Entity> {
and
public <S extends T> S populate(S entity)
or in a simple way no need to use generic types just do (but you have to cast the returned entity with the concrete type)
public Entity populate(Entity entity) {
...
return entity;
}
in my example Entity is your entities abstract base class

How to define a Map whose key and value is enforced to have a related hierarchy?

I have a root class called Entity. I also have service classes that do CRUD operations on a subtypes of Entity. So, my service class definitions follow this pattern
class Entity {
//the root class
}
class Service<T extends Entity> {
//root service class containing common CRUD operations
}
class SomeEntity extends Entity {
}
class SomeService extends Service<SomeEntity> {
}
I maintain a Map of Entity class --> to their corresponding Service like
Map<Class<? extends Entity>, Service<? extends Entity>> = serviceMap
new HashMap<Class<? extends Entity>, Service<? extends Entity>>();
However, the above doesn't enforce that the class and its corresponding Service belong to the same hierarchy. Hence, even after casting, my client code have to cast it again. How to avoid the following ? And enforce a strict relation among the key and value ?
public <T extends Entity> Service<T> get(Class<T> clazz) {
return (Service<T>) serviceMap.get(clazz);
}
//example of client code
SomeService service = (SomeService) serviceRegistry.get(SomeEntity.class);
Unfortunately, you cannot do what you'd like to do. It just isn't possible!
However, you shouldn't really need to know what the Service implementation is though. Service should define provide all the necessary methods for the client code to invoke. Hello polymorphism! Personally, I'd just change your get method slightly to:
Service<? extends Entity> getService(Class<?> entityClass) {
return serviceMap.get(entityClass);
}
Client code shouldn't care too much about the actual implementation:
Service<? extends Entity> service = getService(SomeEntity.class);
service.save(myEntity);
Another option is if you know what the entity type is when you want the service then you can pass in the Service class and use that for the cast, eg:
<T extends Service<? extends Entity>> T getService(Class<?> entityClass, Class<T> serviceClass) {
return serviceClass.cast(serviceMap.get(entityClass));
}
Admittedly, neither of the above methods guarantee that SomeEntity.class is paired with a SomeService instance in the Map.

Categories