В данной заметке будет рассказано про удаление из исходников повторяющихся кусков кода с помощью Reflection/Generics. Пусть в проекте содержится несколько перечислений для описания действий, в каждом из которых реализован метод,  возвращающий список значений:
| import lombok.AllArgsConstructor; import lombok.Getter; import java.util.AbstractMap; import java.util.Arrays; import java.util.List; import java.util.Map; import java.util.stream.Collectors; @Getter @AllArgsConstructor public enum TestAction { UP("up", "вверх"), DOWN("down", "вниз"); public static List<Map.Entry<String, String>> listOfActions() { return Arrays.stream(TestAction.values()) .map(a -> new AbstractMap.SimpleEntry<String, String>(a.name(), a.getDescription()) { }) .collect(Collectors.toList()); } private final String action; private final String description; } | 
Неообходимые пояснения. Используются две аннотации AllArgsConstructor и Getter из фреймворка lombok для генерации конструктора и геттеров.
Вызываем метод так:
| private static List<Map.Entry<String, String>> actions = TestAction.listOfActions(); | 
Но при этом возникает дублирование кода, поскольку приходится реализовывать метод в каждом из перечислений.
Провести рефакторинг в данном случае довольно неочевидно. Так как все перечисления в Java наследуются от класса Enum, то создать класс AbstractAction, объявить метод в нём и отнаследовать от него все классы действий не представляется возможным. И тут приходят на помощь Reflection и Generics.
Для получения поля класса без использования Reflection API необходимо вывести его в интерфейс:
| public interface IAction { String getDescription(); } | 
Обновлённая версия перечисления импленментирует интерфейс IAction и не содержит метода получения списка:
| import lombok.AllArgsConstructor; import lombok.Getter; @Getter @AllArgsConstructor public enum TestActionImproved implements IAction { UP("up", "вверх"), DOWN("down", "вниз"); private final String action; private final String description; } | 
Метод получения списка использует метод getEnumConstants() Reflection API и обрабатывает только классы, реализующие интерфейс IAction:
| import java.util.AbstractMap; import java.util.Arrays; import java.util.List; import java.util.Map; import java.util.stream.Collectors; public class Handler { /** * Получает из перечисления список объектов Map.Entry с наименованием элемента перечисления и значением поля description * * @param actionClass объект class перечисления, из которого нужно получить список * @return список объектов Map.Entry с наименованием элемента перечисления и значением поля description */ public static List<Map.Entry<String, String>> getListOfActions(Class<? extends IAction> actionClass) { return Arrays.stream(actionClass.getEnumConstants()) .map(c -> new AbstractMap.SimpleEntry<String, String>(c.toString(), c.getDescription()){}) .collect(Collectors.toList()); } } | 
Для нашего случая вызываем так:
| private static List<Map.Entry<String, String>> actions = Handler.getListOfActions(TestActionImproved.class); | 

 17 мая, 2018
 17 мая, 2018  Андрей Самойлов
 Андрей Самойлов  Posted in
 Posted in  Tags:
 Tags: