Home > Back-end >  How i can write this method using Java Generics?
How i can write this method using Java Generics?

Time:01-01

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)
  • Related