I need help with Java Generics. My model is: I have some classes that extends a Dto (Data Transfer Object) and some classes that extends Entity (The model of my object to DB). I have
interface Mapper<D extends Dto, E extends Entity>{
//Convert a Entity to Dto.
D toDto(E entity);
And I have some classes that implements this interface (i.e PersonMapper, BookMapper and so far and so on).
@Component
public class PersonMapper implements Mapper<PersonDto, PersonEntity> {
//implementation
}
@Component
public class BookMapper implements Mapper<BookDto, BookEntity> {
//implementation
}
What I want to do is to use Factory Pattern in order to select at runtime my Mapper, that depends from a String that I pass in input.
@Autowired
private PersonMapper personMapper;
@Autowired
private BookMapper bookMapper;
public <D extends Dto, E extends Entity> Mapper<D, E> selectMapper(String entity){
if ("Person".equalsIgnoreCase(entity))
return personMapper;
if("Book".equalsIgnoreCase(entity))
return bookMapper;
...
}
With this situation I have the following compile error:
Type mismatch: cannot convert from PersonMapper to Mapper<D,E>
My solutions:
1)
return (Mapper<D, E>) personMapper;
but I have a Warning:
Type Safety: `Unchecked class from personMapper to Mapper<D,H>`
2)
Using WildCard and castingb
public Mapper<Dto, Entity> selectMapper(String entity){
Mapper<? extends Dto, ? extends Entity> toReturn = null;
if ("Person".equalsIgnoreCase(entity))
toReturn = personMapper;
else if("Book".equalsIgnoreCase(entity))
toReturn = bookMapper;
...
return (Mapper<Dto, Entity>) toReturn;
}
But in this case but I have another time a Warning:
Type safety: Unchecked cast from Mapper<capture#29-of ? extends Dto,capture#30-of ? extends Entity> to Mapper<Dto,Entity>
It works but it doesn't seems to be a clean solution
3) Using wildcard as return type:
public Mapper<? extends Dto, ? extends HistoryEntity> selectMapper(String entity)
but you know, using wildcard as return type is not recommended at all and also doesn't help me because I would like to use this mapper and call mapper.toDto ensuring that the return type is an something that extends Dto.
====================================================================
I don't explain why If I write a class constructor like that
public Service<D extends Dto, E extends Entity>{
public Service(Mapper<D,E> mapper){
this.mapper = mapper;
}
}
and than I inject (for example) bookMapper it works.
If, instead, the Mapper<D,E> is in return type I cannot do such a kind of operation.
====================================================================
The help that I ask to you is:
how can I write a solution using clean code principles (avoiding compile warnings, sonarlint issue etc.) in order to implement this kind of logic?
Thank you very much, I appreciate a lot if you dedicate a little bit of your time helping me to solve my problem.