Сопоставить значения перечисления с полями класса - PullRequest
2 голосов
/ 16 января 2020

У меня есть классы enum и POJO в JAVA. В классе enum каждое значение enum совпадает с переменными классов POJO ... А затем я хочу создать связь между двумя классами.

Enum Class:

public enum MyEnum
{
   FIELD1,
   FIELD2,
   FIELD3,
   ...
}

POJO Class:

public class MyPojo
{
   private String field1;
   private String field2_not_ref;
   private String field3;
   ...
}

Затем, когда я пытаюсь сопоставить эти поля двух классов, я реализовал код наподобие:

public String matchMethod(MyEnum myenum)
{
  switch(myenum)
  {
   case FIELD1:
      return field1;
   case FIELD2:
      return field2_not_ref;
    ...
  }
}

Я думаю, что это не хорошее / понятное решение. Есть какие-нибудь предложения?

Ответы [ 6 ]

5 голосов
/ 09 февраля 2020

Чтобы избежать отражения, которое может быть медленным и сделать вещи более краткими, чем при определении метода для перечисления, вы можете создать перечисление с полем, которое является функцией, потребляющей MyPojo и возвращающей String:

public enum MyEnum {
    FIELD1(MyPojo::getField1),
    FIELD2(MyPojo::getField2),
    FIELD3(MyPojo::getField3);

    private final Function<MyPojo, String> getField;

    MyEnum(Function<MyPojo, String> getField) {
        this.getField = getField;
    }
}

Затем вы можете вызвать его следующим образом:

public static void main(String[] args) {
    MyPojo myPojo = new MyPojo("f1", "f2", "f3");
    System.out.println(MyEnum.FIELD2.getGetField().apply(myPojo));
}

Если вы не хотите использовать его как переменную Function с методом apply, вы можете создать единственная функция en enum, которая делает это:

public enum MyEnum {
    FIELD1(MyPojo::getField1),
    FIELD2(MyPojo::getField2),
    FIELD3(MyPojo::getField3);

    private final Function<MyPojo, String> getField;

    MyEnum(Function<MyPojo, String> getField) {
        this.getField = getField;
    }

    String getFieldFromMyPojo(MyPojo myPojo) { return getField.apply(myPojo); }
}

И вызывает это так:

public static void main(String[] args) {
    MyPojo myPojo = new MyPojo("f1", "f2", "f3");
    System.out.println(MyEnum.FIELD2.getFieldFromMyPojo(myPojo));
}

Для краткости опущены геттеры и сеттеры.

2 голосов
/ 09 февраля 2020

Вы можете использовать отражение. Вот пример:

public String matchMethod(MyEnum myenum, Map<MyEnum, String> enumToFieldMap) throws NoSuchFieldException, IllegalAccessException {
    String customFieldName = enumToFieldMap.get(myenum);
    if (customFieldName == null) { // custom field name not found, use default mapping
        return (String) this.getClass().getDeclaredField(myenum.name().toLowerCase()).get(this);
    } // custom field name found in config
    return (String) this.getClass().getDeclaredField(customFieldName).get(this);
}

public String matchMethod(MyEnum myEnum) throws NoSuchFieldException, IllegalAccessException {
    return matchMethod(myEnum, Collections.EMPTY_MAP);
}

Существуют некоторые недостатки использования отражения, такие как безопасность типов или прослеживаемость, однако в этом случае я думаю, что выбрал бы эту опцию.

Другой , гораздо более гибким вариантом является использование отражения в сочетании с пользовательской аннотацией:

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
public @interface MyEnumRef {
    MyEnum value();
}

И общий интерфейс:

public interface Pojo {
}

Объявление новых Pojo (s) становится намного проще и теперь чище и читабельнее Также очень очевидно, где выполняется фактическое сопоставление (конфигурация).

public class MyPojo implements Pojo {
    @MyEnumRef(MyEnum.FIELD1)
    private String field1;
    @MyEnumRef(MyEnum.FIELD2)
    private String field2;
    @MyEnumRef(MyEnum.FIELD3)
    private String field3;
}

public class MyOtherPojo implements Pojo {
    @MyEnumRef(MyEnum.FIELD1)
    private String field1;
    @MyEnumRef(MyEnum.FIELD2)
    private String field2;
}

Один простой способ управлять ими всеми:

public String matchMethod(MyEnum myEnum, Pojo myPojo) throws IllegalAccessException {
        for (Field field : myPojo.getClass().getDeclaredFields()) {
            if (field.isAnnotationPresent(MyEnumRef.class) && field.getAnnotation(MyEnumRef.class).value() == myEnum) {
                field.setAccessible(true);
                return (String) field.get(myPojo);
            }
        }
        return "";
    }

Не имеет значения, какая реализация Pojo вы используете. Нет никаких накладных расходов при добавлении новых Pojos. Пример:

private void run() throws IllegalAccessException {
    System.out.println(">>" + matchMethod(MyEnum.FIELD2, new MyPojo("f1", "f2", "f3")));
    System.out.println(">>" + matchMethod(MyEnum.FIELD1, new MyOtherPojo("o1", "o2")));
}
2 голосов
/ 09 февраля 2020

Существует еще один способ:

Определить абстрактный метод в перечислении и переопределении для каждого поля перечисления:

public enum MyEnum {
    FIELD1 {
        @Override
        public String getFromPojo(MyPojo myPojo) {
            return myPojo.getField1();
        }
    },
    FIELD2 {
        @Override
        public String getFromPojo(MyPojo myPojo) {
            return myPojo.getField2();
        }
    },
    FIELD3 {
        @Override
        public String getFromPojo(MyPojo myPojo) {
            return myPojo.getField3();
        }
    };

    public abstract String getFromPojo(MyPojo myPojo);
}

Для класса MyPojo определить получатели для полей:

Определите также метод matchMethod таким образом, чтобы он отвечал за перечисление (если вы sh, это не обязательно, поскольку перечисление может разрешить поле самостоятельно).

public class MyPojo {

    private String field1;
    private String field2;
    private String field3;

    public MyPojo(String field1, String field2, String field3) {
        this.field1 = field1;
        this.field2 = field2;
        this.field3 = field3;
    }

    public String getField1() {
        return field1;
    }

    public String getField2() {
        return field2;
    }

    public String getField3() {
        return field3;
    }

    public String matchMethod(MyEnum myEnum) {
        return myEnum.getFromPojo(this);
    }
}

Теперь в «основном» методе вы можете использовать следующий подход:

 MyPojo p1 = new MyPojo("p1", "p2", "p3");
 MyPojo p2 = new MyPojo("k1", "k2", "k3");

 System.out.println(MyEnum.FIELD1.getFromPojo(p1));
 System.out.println(MyEnum.FIELD2.getFromPojo(p1));
 System.out.println(MyEnum.FIELD3.getFromPojo(p1));

 System.out.println(MyEnum.FIELD1.getFromPojo(p2));
 System.out.println(MyEnum.FIELD2.getFromPojo(p2));
 System.out.println(MyEnum.FIELD3.getFromPojo(p2));

 // in addition, if you've defined 'matchMethod' on POJO
 System.out.println(p1.matchMethod(MyEnum.FIELD1));
 System.out.println(p1.matchMethod(MyEnum.FIELD2));
 System.out.println(p1.matchMethod(MyEnum.FIELD3));

 System.out.println(p2.matchMethod(MyEnum.FIELD1));
 System.out.println(p2.matchMethod(MyEnum.FIELD2));
 System.out.println(p2.matchMethod(MyEnum.FIELD3));     

Это печатает:

p1
p2
p3
k1
k2
k3
// and optionally...
p1
p2
p3
k1
k2
k3
1 голос
/ 09 февраля 2020

Вот еще один способ, он имеет один уровень косвенности (регистрации), но не изменяет Enum.

Для этого требуется Java 8+, поскольку он использует лямбда-выражения:

import java.util.EnumMap;
import java.util.function.Function;

enum MyEnum {
  FIELD1, FIELD2, FIELD3;
}

class MyPojo {
  private String field1, field2, field3;

  public MyPojo(String f1, String f2, String f3) {
    this.field1 = f1;
    this.field2 = f2;
    this.field3 = f3;
  }
  private static EnumMap<MyEnum, Function<MyPojo, String>> registry = new EnumMap(MyEnum.class);
  static {
    registry.put(MyEnum.FIELD1, p -> p.field1);
    registry.put(MyEnum.FIELD2, p -> p.field2);
    registry.put(MyEnum.FIELD3, p -> p.field3);
  }

  public String matchMethod(MyEnum e) {
    return registry.get(e).apply(this);
  }
}

class Main {
  public static void main(String[] args) {
    MyPojo p1 = new MyPojo("a", "b", "c");
    MyPojo p2 = new MyPojo("x", "y", "z");
    System.out.println(p1.matchMethod(MyEnum.FIELD1));
    System.out.println(p1.matchMethod(MyEnum.FIELD2));
    System.out.println(p1.matchMethod(MyEnum.FIELD3));

    System.out.println(p2.matchMethod(MyEnum.FIELD1));
    System.out.println(p2.matchMethod(MyEnum.FIELD2));
    System.out.println(p2.matchMethod(MyEnum.FIELD3));
  }
}

Печать:

a
b
c
x
y
z
1 голос
/ 16 января 2020

Это решение работает только для static полей, поскольку перечисления всегда static!

Один из способов, который я могу себе представить, заключается в следующем:

public enum MyEnum
{
   private String field;

   public String getField()
   { return this.field; }


   MyEnum(String field)
   {
      this.field = field;
   }

   FIELD1(field1),
   FIELD2(field2),
   FIELD3(field3),
   ...
}

Вы можете даже сделать его обобщенным c, если хотите:

public enum MyEnum<T>
{
   private T field;

   public T getField()
   { return this.field; }


   MyEnum(T field)
   {
      this.field = field;
   }

   FIELD1(field1),
   FIELD2(field2),
   FIELD3(field3),
   ...
}
0 голосов
/ 16 января 2020

Существуют различные варианты (в том числе использование отражение ), но в простом случае, когда все поля имеют один и тот же тип (здесь String), я бы предложил хранить интересующие значения в EnumMap вместо:

class MyPojo {
   private final EnumMap<MyEnum, String> fields = new EnumMap<>(MyEnum.class);

   public String matchMethod(MyEnum myenum) {
       return fields.get(myenum);
   }

   public void setField1(String value) {
       fields.put(MyEnum.FIELD1, value); // for example
   }
}
...