Java. Лучшая практика работы с Enum



 

Enum

Решил объединить и написать все в одной статье про Enum, статья не совсем для новичков.

Enum - это отдельный тип в Java, иногда его называют перечисление. Проще говоря, это константы, которые связаны между собой - времена года, дни недели, планеты солнечной системы, роли в системе и т.д.

Содержание:

  1.  Пример простенького Enum
  2.  Emun и switch:
  3.  Методы Enum
  4.  Сравнение Enum
  5.  Проблема toString()
  6.  Конструкторы в Enum
  7.  Методы в Enum
  8.  Enum и stream()
  9.  EnumSet



Пример простенького Enum:

public enum UserTypeEnum {
ADMIN,
MAIN_MODERATOR,
MODERATOR,
SPECTATOR,
BUYER,
SELLER;

}
Вывести все значения Enum 
через простой цикл:
for (UserTypeEnum role : UserTypeEnum.values()) {
System.out.println(role);
}
Вывести все значения Enum 
через StreamAPI:
Arrays.stream(UserTypeEnum.values())
.forEach(role->{
System.out.println(role);
});

Emun может быть в switch:

public void switchEnum(UserTypeEnum type) {
switch (type) {
case ADMIN:
System.out.println("Выбрали админа");
break;
case MAIN_MODERATOR:
System.out.println("Выбрали главного модератора");
break;
case MODERATOR:
System.out.println("Выбрали модератора");
break;
case SPECTATOR:
System.out.println("Выбрали наблюдателя");
break;
default:
System.out.println("Роль не из упраления " + type);
break;
}

}

Важные методы:

1.name() - возвращает имя константы
 в String
UserTypeEnum c1 = UserTypeEnum.ADMIN; 
// так с1 это тип UserTypeEnum 
String c1 = UserTypeEnum.ADMIN.name(); 
//  так с1 это тип String
2.ordinal() - возвращает порядковый номер, начиная с 0
int num0 = UserTypeEnum.ADMIN.ordinal(); // вывод 0, 
//т.к. ADMIN первый в списке
int num1 = UserTypeEnum.MAIN_MODERATOR.ordinal();
 // вывод 1, 
//т.к. MAIN_MODERATOR второй в списке
3.values() - получаем массив 
всех значений в Enum
Примеры - выше, как перебрать все значения Enum
4.valueOf() - получить Enum по строке
UserTypeEnum admin = UserTypeEnum.valueOf("ADMIN");
System.out.println(admin.ordinal()); // будет выведено 0
Если значения нет, то получите java.lang.IllegalArgumentException
UserTypeEnum admin = UserTypeEnum.valueOf("ADMIn");
System.out.println(admin.ordinal());
Exception in thread "main" java.lang.IllegalArgumentException: 
No enum constant testEnum.UserTypeEnum.ADMIn

Сравнение Enum:

Enum можно сравнивать и через == и через метод equals
boolean b1 = UserTypeEnum.ADMIN.equals(UserTypeEnum.ADMIN);
System.out.println(b1); //true

boolean b2 = UserTypeEnum.ADMIN == UserTypeEnum.ADMIN;
System.out.println(b2); //true
Почему лучше использовать ==, а не equals
UserTypeEnum user = null;
boolean b = user.equals(UserTypeEnum.ADMIN);
Тут мы получим NullPointerException
Поэтому лучше сравнивать через ==

Опасность toString():

По дефолту toString возвращает тоже самое, что и name(), 
те имя константы в String
String adm = UserTypeEnum.ADMIN.toString();
System.out.println(adm); //ADMIN
Но, метод toString может быть переопределен в нашем Enum
@Override
public String toString() {
return "UserTypeEnum{" +
"roleId=" + roleId +
", groupRoles=" + groupRoles +
", isEditUserComments=" + isEditUserComments +
", isEditModeratorComments=" + isEditModeratorComments +
", access='" + access + '\'' +
'}';
}

И тогда:
String adm = UserTypeEnum.ADMIN.toString();
System.out.println(adm); //UserTypeEnum{roleId=1, groupRoles=ADMIN,
// isEditUserComments=true, isEditModeratorComments=true,
// access='полный доступ'}
Поэтому использовать toString необходимо ТОЛЬКО для для вывода данных
в консоль, файл и тп, но если вам надо получить имя константы
типа String то используйте name()

Конструкторы в Enum:

Enum могут выглядеть и сложно
public enum UserTypeEnum  {
// admins group
ADMIN(1, "admin", true, true, "полный доступ"),
MAIN_MODERATOR(2, "moderator", true, true, "полный доступ"),
MODERATOR(3, "moderator", true, true, "частичный доступ"),
SPECTATOR(4, "spectator", false, false, "только наблюдение"),
// users group
BUYER(5, "client", true, false, "раздел покупателей"),
SELLER(6, "client", false, false, "раздле продавцов");

private int roleId;
private String types;
private boolean isEditUserComments;
private boolean isEditModeratorComments;
private String access;

UserTypeEnum(int roleId, String types, boolean isEditUserComments, boolean isEditModeratorComments, String access) {
this.roleId = roleId;
this.types = types;
this.isEditUserComments = isEditUserComments;
this.isEditModeratorComments = isEditModeratorComments;
this.access = access;
}

public int getRoleId() {
return roleId;
}

public String getTypes() {
return types;
}

public boolean isEditUserComments() {
return isEditUserComments;
}

public boolean isEditModeratorComments() {
return isEditModeratorComments;
}

public String getAccess() {
return access;
}
}

Проверить, может ли SPECTATOR редактировать пользовательские 
комментарии:
boolean b = UserTypeEnum.SPECTATOR.isEditUserComments(); //false

Enum могут быть и приватными (когда находятся внутри другого Класса
 или Enum):

public enum UserTypeEnum  {
// admins group
ADMIN(1, GroupRoles.ADMIN, true, true, "полный доступ"),
MAIN_MODERATOR(2, GroupRoles.MODERATOR, true, true, "полный доступ"),
MODERATOR(3, GroupRoles.MODERATOR, true, true, "частичный доступ"),
SPECTATOR(4, GroupRoles.SPECTATOR, false, false, "только наблюдение"),
// users group
BUYER(5, GroupRoles.CLIENT, true, false, "раздел покупателей"),
SELLER(6, GroupRoles.CLIENT, false, false, "раздле продавцов");

private int roleId;
private GroupRoles groupRoles;
private boolean isEditUserComments;
private boolean isEditModeratorComments;
private String access;



private enum GroupRoles{
ADMIN, MODERATOR, SPECTATOR,CLIENT
}

UserTypeEnum(int roleId, GroupRoles groupRoles, boolean isEditUserComments, boolean isEditModeratorComments, String access) {
this.roleId = roleId;
this.groupRoles = groupRoles;
this.isEditUserComments = isEditUserComments;
this.isEditModeratorComments = isEditModeratorComments;
this.access = access;
}

public int getRoleId() {
return roleId;
}

public GroupRoles getGroupRoles() {
return groupRoles;
}

public boolean isEditUserComments() {
return isEditUserComments;
}

public boolean isEditModeratorComments() {
return isEditModeratorComments;
}

public String getAccess() {
return access;
}
}

Здесь у нас есть приватный Enum GroupRoles, те такой Enum в Enum.

Enum может содержать методы:

public static UserTypeEnum getUserTypeEnumByRoleId(int roleId) {
return Arrays.stream(UserTypeEnum.values()).filter(e -> e.getRoleId() == roleId).findFirst().
orElseThrow(() -> new IllegalStateException("Cant find roleId " + roleId));

}
Получаем Enum по id роли

public static Set<UserTypeEnum> userTypeFilter(Predicate<UserTypeEnum> p) {
return Arrays.stream(UserTypeEnum.values()).filter(p).collect(Collectors.toSet());
}

Или же можно просто передавать предикат, и фильтровать Enum 
как на захочется:
UserTypeEnum user = UserTypeEnum.getUserTypeEnumByRoleId(4);
System.out.println(user); //SPECTATOR
Set<UserTypeEnum> users = UserTypeEnum.userTypeFilter(u -> u.isEditUserComments());
System.out.println(users); //[MODERATOR, BUYER, MAIN_MODERATOR, ADMIN]

Упростить методы, благодаря методу,

который возвращает stream

Методы getUserTypeEnumByRoleId и userTypeFilter можно существенно
упростить, добавив метод stream:
private static Stream<UserTypeEnum> stream() {
return Arrays.stream(UserTypeEnum.values());
}
И наши методы теперь выглядят вот так (нам не надо писать длинную
конструкцию Arrays.stream(UserTypeEnum.values())):

public static UserTypeEnum getUserTypeEnumByRoleId(int roleId) {
return stream().filter(e -> e.getRoleId() == roleId).findFirst().
orElseThrow(() -> new IllegalStateException("Cant find roleId " + roleId));
}

public static Set<UserTypeEnum> userTypeFilter(Predicate<UserTypeEnum> p) {
return stream().filter(p).collect(Collectors.toSet());
}


Работа с EnumSet

EnumSet - это коллекция Set для работы с Enum.
Используйте EnumSet вместо HashSet Методы:
1.allOf - Enum содержит весь Enum:
EnumSet<UserTypeEnum>  allUserType = EnumSet.allOf(UserTypeEnum.class);
System.out.println(allUserType);
Вывод на консоль:
[ADMIN, MAIN_MODERATOR, MODERATOR, SPECTATOR, BUYER, SELLER]
2.range - создать подмножество Enum:
EnumSet<UserTypeEnum>  rangeUserType = EnumSet.range(UserTypeEnum.ADMIN, UserTypeEnum.SPECTATOR);
System.out.println(rangeUserType);
Вывод на консоль:
[ADMIN, MAIN_MODERATOR, MODERATOR, SPECTATOR]
3.complementOf- создать подмножество Enum, исключая другое
EnumSet<UserTypeEnum> userGroup = EnumSet.complementOf(rangeUserType);
System.out.println(userGroup);
Вывод на консоль:
[BUYER, SELLER]
4.of- создать подмножество Enum, указывая конкретные Enum:
EnumSet<UserTypeEnum> group = EnumSet.of(UserTypeEnum.SPECTATOR, UserTypeEnum.BUYER,UserTypeEnum.SELLER);
System.out.println(group);
Вывод на консоль:
[SPECTATOR, BUYER, SELLER]
5.noneOf - создать пустой EnumSet и туда добавить данные
можно через add:
EnumSet<UserTypeEnum> group = EnumSet.noneOf(UserTypeEnum.class);
group.add(UserTypeEnum.BUYER);
group.add(UserTypeEnum.SELLER);
System.out.println(group);
Вывод на консоль:
[BUYER, SELLER]
6. copyOf - для копирования всех элементов из любой коллекции
List<UserTypeEnum> listUserType = new ArrayList<>();
listUserType.add(UserTypeEnum.ADMIN);
EnumSet<UserTypeEnum> listUserCopy = EnumSet.copyOf(listUserType);
System.out.println(listUserCopy);
Вывод на консоль:
[ADMIN]

Комментарии

Популярные сообщения из этого блога

Java. Разбор класса Collections

Java. Порядок инициализации полей и блоков класса и Интерфейсов