Извлечь все истинные свойства и добавить в список

#java #list

#java #Список

Вопрос:

У меня есть класс Java с 3 логическими свойствами, подобными этому

 boolean isActive;
boolean isEnable;
boolean isNew;
 

каждое свойство связано с перечислением (например, ACTIVE, ENABLE,NEW).
Я хочу иметь 2 списка перечислений. Тот, который имеет только перечисления, связанные с истинным значением свойства, и один для ложного.

просто для ясности. используя оператор if-else, я мог бы

 Set<FlagEnum> flagSet = new HashSet<>();
Set<FlagEnum> falseFlagSet = new HashSet<>();

if (object.isActive()) {
    flagSet.add(ACTIVE);
} else {
    falseFlagSet.add(ACTIVE);
}
if (object.isEnable()) {
    flagSet.add(ENABLE);
} else {
    falseFlagSet.add(ENABLE);
}
if (object.isNew()) {
    flagSet.add(NEW);
} else {
    falseFlagSet.add(NEW);
}

 

есть ли способ избежать всех этих if-else?

Я пытался с чем-то вроде

 
Map<boolean, List<Pair<boolean, FlagEnum>>> res = Stream.of(
new Pair<>(object.isActive(), ACTIVE),
new Pair<>(object.isNew(), NEW),
new Pair<>(object.isEnable(), ENABLE))
.collect(Collectors.partitioningBy(Pair::getKey));

 

но полученная структура представляет собой дополнительную сложность, которой я хотел бы избежать.

В моем реальном случае у меня более 15 логических свойств…

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

1. Если вы реализовали свое перечисление как an EnumSet , вы могли бы немного упростить проблему, выведя в falseFlagSet качестве дополнения к trueFlagSet (или просто реализуйте свой собственный метод дополнения). Конечно, это не помогает при сопоставлении логической структуры с частью перечисления.

Ответ №1:

Вы можете упростить это различными способами. Какие из них имеют смысл, зависит от ваших точных требований.

  1. Вы можете получить falseFlagSet тривиально из flagSet using EnumSet.complementOf после заполнения flagSet :
     EnumSet<FlagEnum> falseFlagSet = EnumSet.complementOf(flagSet);
     

    Предполагается, что все FlagEnum значения имеют соответствующие флаги. Если это не так, то вам нужно создать a EnumSet со всеми перечислениями, у которых есть флаги, и вычесть flagSet из этого using removeAll .

  2. # 1 уже устраняет необходимость else в вашем каскаде, упрощая код до
     if (object.isActive()) {
        flagSet.add(ACTIVE);
    }
    if (object.isEnable()) {
        flagSet.add(ENABLE);
    }
    if (object.isNew()) {
        flagSet.add(NEW);
    }
     
  3. Если у вас достаточно разных флагов, вы можете создать сопоставление из метода getter для FlagEnum значения, подобного этому:
     Map<Function<YourClass,Boolean>,FlagEnum> GETTERS = Map.of(
        YourClass::isActive, FlagEnum.ACTIVE,
        YourClass::isNew, FlagEnum.NEW,
        YourClass::isEnable, FlagEnum.ENABLE);
     

    Затем вы можете использовать это, чтобы сделать весь процесс управляемым данными:

     EnumSet<FlagEnum> getFlagSet(YourClass yourObject) {
      EnumSet<FlagEnum> result = EnumSet.noneOf(FlagEnum.class);
      for (Map.Entry<Function<YourClass,Boolean>, FlagEnum> getter : GETTERS.entrySet()) {
        if (getter.getKey().apply(yourObject)) {
          result.add(getter.getValue());
        }
      }
      return resu<
    }
     
  4. Если количество флагов очень велико, вы можете полностью переключиться на отражение и динамически определять флаги и соответствующие геттеры с помощью сравнения строк, но я бы не стал предлагать такой подход. Если вам нужно что-то подобное, вам, вероятно, следует переключиться на фреймворк, который поддерживает такую функцию, а не реализовывать ее самостоятельно.

Очевидно, что последние два имеют смысл только тогда, когда количество флагов велико. Если на самом деле это всего лишь 3 флага, то я бы не возражал и просто использовал 3 простых if утверждения.

В качестве небольшого касательного: GETTERS выше определенно должна быть неизменяемая карта (оберните ее Collections.unmodifiableMap или используйте что-то вроде Guava ImmutableMap ), и можно утверждать, что то же самое относится и к возвращаемому значению getFlagSet метода. Я оставил их для краткости.

Ответ №2:

Для этого вы можете использовать частный вспомогательный метод.

 private void addFlagSet(boolean condition, FlagEnum flagEnum,
                       Set<FlagEnum> flagSet, Set<FlagEnum> falseFlagSet) {

    Set<FlagEnum> chosenFlagSet = condition ? flagSet: falseFlagSet;
    chosenFlagSet.add(flagEnum);
}
 

Назовите это как:

 addFlagSet(object.isActive(), FlagEnum.ACIVE, flagSet, falseFlagSet);
addFlagSet(object.isNew(), FlagEnum.NEW, flagSet, falseFlagSet);
addFlagSet(object.isEnable(), FlagEnum.ENABLE, flagSet, falseFlagSet);
 

Ответ №3:

Вероятно, вы могли бы использовать отражение для получения всех методов, а затем проверить, есть ли getReturnType() == boolean.class . Проблема заключается в связи между именем метода и перечислением. Если каждый из них назван как метод без «is», вы можете использовать FlagEnum.valueOf() для извлечения значения enum из имени метода и использовать его.

Ответ №4:

Я думаю, что это может быть самым простым и понятным способом сделать то, что мне нужно

 
Map<Boolean, Set<FlagEnum>> flagMap = new HashMap<>();
        flagMap.computeIfAbsent(object.isActive(), h -> new HashSet()).add(ACTIVE);
        flagMap.computeIfAbsent(object.isEnabled(), h -> new HashSet()).add(ENABLE);
        flagMap.computeIfAbsent(object.isNew(), h -> new HashSet()).add(NEW);

//to get TRUE set simply :
flagMap.get(true);

 

что вы думаете?