Принять общий список в качестве параметра и использовать его в зависимости от его типа

#java #spring #list #generics

#java #spring #Список #общие

Вопрос:

Я разрабатываю веб-сайт для своей компании, и я использую Spring в качестве своего бэкэнда. Сейчас возникла ситуация, когда мне нужно использовать один из моих Utils-методов дважды, но для разных DAO.

Чтобы избежать дублирования кода, мне было интересно, как я могу использовать Java Generics, чтобы сделать этот метод пригодным для обоих случаев. Метод просто считает одно из полей, которое является общим для обоих DAO.

Метод Util :

 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;

}
  

Область 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;
}
  

Вот как я вызываю его из сервиса:

 SeverityCount severitiesCount = Utils.calculateSeveritiesCount(eventsList);  //EventsList could be list of EventDAO or EventsByAreaDAO
  

Комментарии:

1. можете ли вы создать для них общий интерфейс?

2. Код, который я показал, не работает из-за этой строки: EventDao::getSeverity, в методе utils. Мне нужно определить тип DAO на основе типа списка, который я получил

3. верно, я удалил этот комментарий… прочитайте другой еще раз, пожалуйста, и дайте нам знать

Ответ №1:

Вы можете изменить метод на

 SeverityCount calculateSeveritiesCount(List<? extends SeverityCalculable> events)
  

где SeverityCalculable

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

Пусть все ваши соответствующие классы реализуют этот интерфейс.

 public class EventDAO implements SeverityCalculable  {

    // ...

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

Теперь в вашем методе удалите приведения, и это должно стать чем-то вроде этого:

  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);
}
  

Ответ №2:

Поскольку оба DAO имеют severity свойство, они потенциально могли бы реализовать общий интерфейс, скажем SeverityAware :

 public interface SeverityAware {
  public String getSeverity();
}

public class EventsByRegionDAO implements SeverityAware { .. }

public class EventDAO implements SeverityAware { .. }
  

Кроме того, ваш метод теперь может принимать подтипы этого интерфейса:

 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);
}
  

Комментарии:

1. Я признаю, SeverityAware звучит лучше, чем SeverityCalculable

2. @MarkoPacak Как насчет «общего» именования [SomeThingItDoes] [able] -> Severityable 😉