Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Accept generic List as parameter and use it base on its type

I'm developing a website for my company, and I use Spring as my backend. There is a situation now, where I need to use one of my Utils method twice, but for different DAOs.

In order to avoid code duplication, I was wondering how can I use Java Generics in order to make this method usable for both cases. The method just count one of the fields which is common for both DAOs.

Util method :

SeverityCount calculateSeveritiesCount(List<?> events){

        if(null == events){
            return new SeverityCount();
        }

        if(events.get(1) instanceof EventDAO){
            events = (List<EventDAO>)events;
        }
        else if (events.get(1) instanceof EventsByAreaDAO) {
            events = (List<EventsByAreaDAO>)events;
        }

        Map<String, Long> severityCountMap = events.stream().collect(
            Collectors.groupingBy(
                EventDAO::getSeverity,  //It should be EventDAO or EventsByAreaDAO. both has severity field.
                Collectors.counting())
        );

        return mapper.convertValue(severityCountMap, SeverityCount.class);
    }

Event DAO:

@Data
@NoArgsConstructor
@AllArgsConstructor
@Entity
@Table(name = "events")
public class EventDAO {
    @Id @Column(name = "uid")
    private String uID;

    private String date;

    private String severity;

}

Area DAO:

@Data
@NoArgsConstructor
@AllArgsConstructor
@Entity
@Table(name = "events")
public class EventsByRegionDAO {

     @Id @Column(name = "uid")
    private String uID;

    private String date;

    private String type;

    private String severity;

    private String area;

    private String server;
}

This is how I call it from the service:

SeverityCount severitiesCount = Utils.calculateSeveritiesCount(eventsList);  //EventsList could be list of EventDAO or EventsByAreaDAO
like image 379
user3819295 Avatar asked Apr 03 '19 09:04

user3819295


2 Answers

You can change the method to

SeverityCount calculateSeveritiesCount(List<? extends SeverityCalculable> events)

where SeverityCalculable

interface SeverityCalculable {
    String getSeverity(); // implemente getter in all subclasses
}

Have all your relevant classes implement this interface.

public class EventDAO implements SeverityCalculable  {

    // ...

    @Override
    public String getSeverity() {
        return this.severity;
    }
}

Now in your method, remove the casts and it should become something like this:

 SeverityCount calculateSeveritiesCount(List<? extends SeverityCalculable> events) {

    if(null == events){
        return new SeverityCount();
    }

    Map<String, Long> severityCountMap = events.stream().collect(
        Collectors.groupingBy(
            SeverityCalculable::getSeverity,
            Collectors.counting()
        )
    );

    return mapper.convertValue(severityCountMap, SeverityCount.class);
}
like image 197
Marko Pacak Avatar answered Sep 24 '22 00:09

Marko Pacak


Since both DAO's have a severity property, they could potentially implement a common interface, say SeverityAware:

public interface SeverityAware {
  public String getSeverity();
}

public class EventsByRegionDAO implements SeverityAware { .. }

public class EventDAO implements SeverityAware { .. }

Further, your method could now accept subtypes of this interface:

SeverityCount calculateSeveritiesCount(List<? extends SeverityAware> events){

  if (null == events){
    return new SeverityCount();
  }

  Map<String, Long> severityCountMap = events.stream().collect(Collectors.groupingBy(
    SeverityAware::getSeverity,Collectors.counting())
  );

  return mapper.convertValue(severityCountMap, SeverityCount.class);
}
like image 36
Konstantin Yovkov Avatar answered Sep 25 '22 00:09

Konstantin Yovkov