I'm trying to write this method using java generics :
public void trackUserActivity(String operation,TemplateEntity templateEntity,String user,,String entity, HistoryJpaRepository historyJpaRepository)
{
HistoryEntity historyEntity = new HistoryEntity();
historyEntity.setData(new java.sql.Date(Calendar.getInstance().getTimeInMillis()));
historyEntity.setAction(operation);
historyEntity.setEntity("TEMPLATE");
historyEntity.setEntityJson(null);
if(!operation.contentEquals("CREATE"))
{
ObjectWriter ow = new ObjectMapper().writer().withDefaultPrettyPrinter();
String json = null;
try {
json = ow.writeValueAsString(templateEntity);
} catch (JsonProcessingException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
historyEntity.setEntityJson(json.getBytes());
}
historyEntity.setIdEntity(templateEntity.getId());
historyEntity.setUser(user);
historyJpaRepository.save(historyEntity);
}
inside this method I have the getId() method which is common to multiple object types, I tried to create an interface with a default method with a generic type T that extends multiple classes :
public interface HistoryUtils <T extends LavorazioneDto,DizionarioDto,CampoDizionarioDto,FontDto,RegolaCampoDizionarioDto,TemplateEntity >{
public default void trackUserActivity(String operation,T t,String user,String entity, HistoryJpaRepository historyJpaRepository)
{
HistoryEntity historyEntity = new HistoryEntity();
historyEntity.setData(new java.sql.Date(Calendar.getInstance().getTimeInMillis()));
historyEntity.setAction(operation);
historyEntity.setEntity(entity);
historyEntity.setEntityJson(null);
if(!operation.contentEquals("CREATE"))
{
ObjectWriter ow = new ObjectMapper().writer().withDefaultPrettyPrinter();
String json = null;
try {
json = ow.writeValueAsString(t);
} catch (JsonProcessingException e) {
e.printStackTrace();
}
historyEntity.setEntityJson(json.toString().getBytes());
}
historyEntity.setIdEntity(t.getId());
historyEntity.setUser(user);
historyJpaRepository.save(historyEntity);
}
only that after I autowired my interface in one of the services and I try to call the interface method I get this error:
The method trackUserActivity(String, LavorazioneDto, String, String, HistoryJpaRepository)
in the type HistoryUtils is not applicable for the arguments (String, TemplateEntity, String, String, HistoryJpaRepository)
.
How can I write an interface that makes use of generics and extends all the classes I need?
CodePudding user response:
The declaration
<T extends LavorazioneDto,DizionarioDto,CampoDizionarioDto,FontDto,RegolaCampoDizionarioDto,TemplateEntity >
requires T
to extend LavorazioneDto
, and to extend DizionarioDto
, and so on. That is, it requires T to extend all these types, rather than any of these types. In type theoretic terms, T is an intersection type, not a union type. Java doesn't support union types in type bounds.
The usual solution would be to have the possible subtypes implement a common interface, and use that interface as a parameter type:
interface Identifiable {
String getId();
}
class LavorazioneDto implements Identifiable {
public String getId() {
return id;
}
}
interface HistoryUtils {
public default void trackUserActivity(String operation,Identifiable t,String user,String entity, HistoryJpaRepository historyJpaRepository) {
t.getId(); // works just fine
}
}
That is, I don't see why you are using generics in the first place, if plain old polymorphism does the job just fine.
CodePudding user response:
Instead of 'T t' you can pass reference to method t.getId, object class and obj
trackUserActivity(obj, ObjA::getId, ObjA.class);
trackUserActivity(obj, ObjB::getId, ObjB.class);
trackUserActivity(obj, ObjC::getId, ObjC.class);
and replace invocation inside method with:
clazz.cast(obj);
func.apply(above obj)