Pre-requisite : Java : Enumeration Handling
Below is example of enumeration by using all implicit methods of enumeration.
Here used wrong value of enumeration as “Saturday” while using month name here that’s why causing this issue.
package enm; public class EnumTest { enum Month{JANNUARY,FEBRUARY,MARCH,APRIL,MAY,JUNE,JULY,AUGUST,SEPTEMBER,OCTOBER,NOVEMBER,DECEMBER}; enum MonthDetail{ JANNUARY("January",1), FEBRUARY("February",2), MARCH("March",3), APRIL("April",4), MAY("May",5), JUNE("June",6), JULY("July",7), AUGUST("August",8), SEPTEMBER("September",9), OCTOBER("October",10), NOVEMBER("November",11), DECEMBER("December",12); public String monthName=""; public int index; //Constructor will always private private MonthDetail(String monthName,int index) { this.monthName=monthName; this.index=index; } //Method public void showMonthDetail() { System.out.println(this.index +" : "+this.monthName); } }; public static void main(String[] args) { for(Month month:Month.values()) { //Add one because by default enum indexing start from 0 System.out.println((month.ordinal()+1) +" : "+month.name()); } //Every enum Class provide values method to get list of enums for(MonthDetail monthDetail:MonthDetail.values()) { monthDetail.showMonthDetail(); } try { MonthDetail mnth=MonthDetail.valueOf("Saturday"); } catch(Exception ex) { ex.printStackTrace(); } } }
Output
1 : JANNUARY 2 : FEBRUARY 3 : MARCH 4 : APRIL 5 : MAY 6 : JUNE 7 : JULY 8 : AUGUST 9 : SEPTEMBER 10 : OCTOBER 11 : NOVEMBER 12 : DECEMBER 1 : January 2 : February 3 : March 4 : April 5 : May 6 : June 7 : July 8 : August 9 : September 10 : October 11 : November 12 : December Exception in thread "main" java.lang.IllegalArgumentException: No enum constant enm.EnumTest.MonthDetail.Saturday at java.lang.Enum.valueOf(Enum.java:238) at enm.EnumTest$MonthDetail.valueOf(EnumTest.java:1) at enm.EnumTest.main(EnumTest.java:49)
Solutions:
Always use valid constant values to resolve this issue and while trying to call this enum.valueOf() method always handle exception so that any exception happen then your program will not terminate.
try { MonthDetail mnth=MonthDetail.valueOf("August"); } catch(Exception ex) { ex.printStackTrace(); }
To learn more on Enumeration follow below link: Java : Enumeration Handling
“Learn From Others Experience»
Данная статья:
- написана командой Vertex Academy. Надеемся, что она Вам будет полезна. Приятного прочтения!
- это одна из статей из нашего «Самоучителя по Java»
- Данная статья предполагает, что Вы уже хорошо знаете ООП.
В предыдущей статье (Урок 66: Перечисления Enum в Java) мы рассмотрели что такое Enum в Java. Абстрактный класс Enum появился в 5-й версии Java. Теперь, благодаря абстрактному классу Enum мы можем быстро и легко создавать перечисления без написания повторяющегося кода.
В этой статье мы рассмотрим на конкретных примерах следующее:
1. К Enum можно применять методы:
- name() — возвращает имя
- ordinal() — возвращает порядковый номер
- equals()
- hashCode()
- toString()
- finalize()
- clone()
- values()
- valueOf()
2. Enum реализовывает интерфейс Comparable
3. Enum реализовывает интерфейс Serializable
Иииииии сразу в бой 🙂 Создадим простой enum Color
enum Color { RED, GREEN, BLUE } |
Методы name() и ordinal()
У каждого enum есть имя и порядковый номер. Получить их можно с помощью методов name() и ordinal()
System.out.println(Color.RED.name()); //output: RED System.out.println(Color.RED.ordinal()); //output: 0 |
Посмотрим как это реализовано в классе Enum
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
public abstract class Enum<E extends java.lang.Enum<E>> implements Comparable<E>, Serializable { private final String name; public final String name() { return name; } private final int ordinal; public final int ordinal() { return ordinal; } protected Enum(String name, int ordinal) { this.name = name; this.ordinal = ordinal; } .... } |
Пояснения:
- Конструктор невидим для разработчиков, но используется самой Java для корректной работы перечислений.
Возникает вопрос, а где же ключевое слово extends возле декларации перечисления Color? Все дело в ключевом слове enum, именно оно даёт понять программе, что вы хотите не просто класс, а именно перечисление, что и дает Вам уйму возможностей.
Методы equals(), hashcode(), toString(), finalize() и clone()
Enum переопределяет базовые методы класса Object. Так что их можно использовать сразу же в наших перечислениях.
Пример 1: equals()
boolean isEqualToItself = Color.RED.equals(Color.RED); boolean isEqualToDifferentColor = Color.RED.equals(Color.GREEN); System.out.println(isEqualToItself); //output: true System.out.println(isEqualToDifferentColor);//output: false |
Пример 2: hashCode()
int hashOfRed = Color.RED.hashCode(); int hashOfGreen = Color.GREEN.hashCode(); System.out.println(hashOfRed); //output would be different every time: 366712642 System.out.println(hashOfGreen); //output would be different every time: 1829164700 |
Поскольку мы использовали hashCode(), каждый раз будет выводиться разное значение, сгенерированное автоматически. Когда мы запускали код, получили числа 366712642 и 1829164700. Вы наверняка получите другие числа.
Пример 3: toString()
String red = Color.RED.toString(); System.out.println(red); //output: RED |
Посмотрим как они реализованы в классе Enum
public String toString() { return name; } public final boolean equals(Object other) { return this==other; } public final int hashCode() { return super.hashCode(); } |
Пояснения:
- метод toString() возвращает имя значения перечисления. Назвали значение WHITE, это же значение и получим при вызове toString() или name();
- метод equals() сравнивает значения перечислений по ссылкам. Почему? Потому, что значения в перечислениях являются константными (уникальными), существует всего один экземпляр цвета RED, один цвета GREEN и один BLUE, значит ссылка на этот экземпляр будет всего одна, значит их можно сравнивать с помощью ==. Вы можете сами убедиться в этом, написав Color.RED == Color.RED или Color.GREEN == COLOR.BLUE;
- метод hashCode() использует стандартную реализацию из класса Object.
Пример 4: finalize(), clone()
protected final void finalize() { } protected final Object clone() throws CloneNotSupportedException { throw new CloneNotSupportedException(); } |
Пояснения:
- метод finalize() пустой, а это значит, что не нужно закрывать «ресурсы» перед сборщиком мусора. Мы говорим о тех «ресурсах», которые используются в try-with-resources. Да и вообще метод finalize() пережиток прошлых лет и в Java 9 данный метод уже помечен как @Deprecated (устаревший метод, который уберут в последующих реализациях);
- метод clone() мы можем вызвать только внутри самого перечисления т.к. он помечен ключевым словом protected. Но даже если мы попытаемся сделать это, то ничего мы не получим, кроме CloneNotSupportedException. Нужно это для того чтобы нельзя было создать несколько экземпляров одного и того же перечисления. Ведь в реальной жизни у нас нет двух цифр «1», нет двух значений скорости света, так и с перечислениями.
Метод values() — позволяет получить массив всех значений Enum
Сделать это можно с помощью метода values()
Color[] colors = Color.values(); System.out.println(Arrays.toString(colors)); //output: [RED, GREEN, BLUE] |
Посмотрим на реализацию метода values() в классе Enum
public static E[] values(); |
Пояснения:
- полной реализации метода в классе Enum нет, так как он синтетический (искусственно добавляется во время компиляции). Из документации узнаем, что метод просто возвращает массив всех значений перечисления в порядке их объявления.
Метод valueOf() — позволяет получить значения перечисления по его строковому представлению
Для получения значение перечисления по его строковому представлению у Enums есть метод valueOf(). Посмотрим на его использование
System.out.println(Color.valueOf(«RED»).ordinal()); //output: 0 |
Если же такого значения в перечислении нет, то мы получим IllegalStateException
Color.valueOf(«BLACK»); //output: java.lang.IllegalArgumentException: No enum constant Color.BLACK |
Посмотрим на реализацию метода valueOf() в классе Enum
public static E valueOf(String name); |
Пояснения:
- как и в случае с values() этот метод тоже синтетический и поэтому полной реализации данного метода в классе Enum нет. В официальной документации написано, что метод просто возвращает значение перечисления по его строковому представлению. Проверка строгая, поэтому никаких пробелов вначале, в конце или между буквами не должно быть.
Есть еще один способ получить значение перечисления
System.out.println(Enum.valueOf(Color.class, «BLUE»).ordinal()); //output: 2 |
но он является менее распространённым.
Рассмотрим его подробнее:
public static <T extends Enum<T>> T valueOf(Class<T> enumType, String name) { T result = enumType.enumConstantDirectory().get(name); if (result != null) return result; if (name == null) throw new NullPointerException(«Name is null»); throw new IllegalArgumentException( «No enum constant « + enumType.getCanonicalName() + «.» + name); } |
Пояснения:
- метод enumConstantDirectory() возвращает Map, где ключ — строковое значение перечисления, а значение — реальное значение перечисления;
- если мы нашли нужное значение в Map — возвращаем его;
- если мы передали null в метод valueOf() — обратно получим NullPointerException;
- если же значения, которое мы указали в valueOf() нет — мы получим IllegalArgumentException.
Enum реализовывает интерфейс Comparable
Что такое интерфейс Comparable мы рассматривали в статье «Интерфейсы Comparable и Comparator».
Зачем же Enum реализовывает интерфейс Comparable? Сделано это для того, чтобы перечисления можно было сравнивать друг с другом при сортировке. При этом сравнение происходит по ordinal() перечисления. Вспомним порядок объявления значений в Color
enum Color { RED, GREEN, BLUE } |
Теперь посмотрим на сравнение элементов перечисления с помощью метода compareTo()
System.out.println(Color.GREEN.compareTo(Color.RED)); //output: 1 System.out.println(Color.GREEN.compareTo(Color.GREEN)); //output: 0 System.out.println(Color.GREEN.compareTo(Color.BLUE)); //output: -1 System.out.println(Color.RED.compareTo(Color.BLUE)); //output: -2 |
Результат показывает как располагаются значения перечисления относительно друг друга:
- Число 1 означает, что значение GREEN находится правее на одну позицию от значения RED
- Число 0 означает, что значение GREEN равно само себе
- Число -1 означает, что значение GREEN находится левее от значения BLUE на одну позицию
- Число -2 означает, что значение RED находится левее от значения BLUE на две позиции
А теперь посмотрим на использование в коллекции List
List<ColorEnum> colors = new ArrayList<>(List.of(Color.GREEN, Color.RED, Color.BLUE)); System.out.println(colors); //output: [GREEN, RED, BLUE] Collections.sort(colors); System.out.println(colors); //output: [RED, GREEN, BLUE] |
Метод Collections.sort(colors) отсортировал список colors благодаря тому, что Enum реализовывают интерфейс Comparable. Посмотрим на реализацию метода compareTo() в классе Enum
public final int compareTo(E o) { Enum<?> other = (Enum<?>)o; Enum<E> self = this; if (self.getClass() != other.getClass() && self.getDeclaringClass() != other.getDeclaringClass()) throw new ClassCastException(); return self.ordinal — other.ordinal; } |
Пояснения:
- сравнивать перечисления можно только между своими типами. Нельзя сравнивать перечисления типа Color с перечислением типа Car. Мало того, что компилятор не даст вам это сделать с помощью своих подсказок так еще и в самом методе есть проверка на тип класса перечислений.
Enum реализовывает интерфейс Serializable
Но как и в случае с clone() воспользоваться мы им не можем
private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException { throw new InvalidObjectException(«can’t deserialize enum»); } private void readObjectNoData() throws ObjectStreamException { throw new InvalidObjectException(«can’t deserialize enum»); } |
Пояснения:
- причина того, что эти методы приватные да и к тому же бросают исключения при их вызове так же, что и в случае с clone(). Если бы эта возможность была открыта, тогда легко можно было бы сохранить перечисление в файл, затем считать его обратно и получить на выходе два экземпляра одного значения перечисления. Этот как два значения числа «1»;
На этом урок заканчивается
В следующей статье мы разберем:
- конструкторы, переменные и собственные методы в перечислениях;
- специальные коллекции для перечислений.
3-ю статью по Enum можно прочитать здесь.
Спасибо, что были с нами! 🙂
Надеемся, что наша статья была Вам полезна. Можно записаться к нам на курсы по Java на сайте.
Этот вопрос в основном является расширением моего предыдущего вопроса. Я задал предыдущий вопрос, чтобы убедиться, что константы Enum заполняются, когда класс загружается. Вот мой класс снова с добавлением простого метода getByName
:
public enum PropName {
CONTENTS("contents"),
USE_QUOTES("useQuotes"),
ONKEYDOWN("onkeydown"),
BROWSER_ENTIRE_TABLE("browseEntireTable"),
COLUMN_HEADINGS("columnHeadings"),
PAGE_SIZE("pageSize"),
POPUP_TITLE("popupTitle"),
FILTER_COL("filterCol"),
SQL_SELECT("sqlSelect"),
;
private String name;
private PropName(String name) {
this.name = name;
}
public String getName() {
return name;
}
public static PropName getByName(String name){
return PropName.valueOf(name);
}
}
Вызов метода getByName("columnHeadings")
вызывает java.lang.IllegalArgumentException: No enum const class labware.web.component.limsgrid.PropName.columnHeadings
, но если я заменил этот метод на следующий код, он просто работает.
public static PropName getByName(String name){
for(PropName prop : values()){
if(prop.getName().equals(name)){
return prop;
}
}
throw new IllegalArgumentException(name + " is not a valid PropName");
}
Любые идеи относительно того, что я делаю неправильно здесь?
Ответ 1
Enum.valueOf()
проверяет только имя константы, поэтому вам нужно передать его "COLUMN_HEADINGS"
вместо «columnHeadings». Ваше свойство name
не имеет ничего общего с внутренностями Enum.
Чтобы ответить на вопросы/проблемы в комментариях:
Enum «встроенный» (неявно объявленный) метод valueOf(String name)
будет искать константу перечисления с таким точным именем. Если вы вводите «columnHeadings», у вас есть (как минимум) три варианта:
- Немного забудь о соглашениях по именованию и просто назови свои константы, так как это имеет смысл:
enum PropName { contents, columnHeadings, ...}
. Это, очевидно, самое удобное. - Преобразуйте входные данные camelCase в UPPER_SNAKE_CASE перед вызовом
valueOf
, если вы действительно любите соглашения об именах. - Реализуйте свой собственный метод поиска вместо встроенного
valueOf
, чтобы найти соответствующую константу для входа. Это наиболее целесообразно, если существует несколько возможных отображений для одного и того же набора констант.
Ответ 2
Это потому, что вы определили свою собственную версию name
для вашего перечисления, а getByName
не использует это.
getByName("COLUMN_HEADINGS")
, вероятно, будет работать.
Ответ 3
Вместо определения: COLUMN_HEADINGS("columnHeadings")
Попробуйте определить его как: COLUMNHEADINGS("columnHeadings")
Затем, когда вы вызываете getByName(String name) method
, вызовите его с строкой с верхним острием следующим образом: getByName(myStringVariable.toUpperCase())
У меня была такая же проблема, как и вы, и это сработало для меня.
Ответ 4
У меня была проблема с анализом enum, когда я пытался передать Nullable Enum, который мы получили из Backend.
Конечно, это работало, когда мы получали значение, но это было проблемой, когда ноль появлялся.
java.lang.IllegalArgumentException: нет константы перечисления
Также проблема заключалась в том, что когда мы в момент чтения Parcelize пишем несколько коротких if.
Мое решение для этого было
1.Создать сопутствующий объект с помощью метода синтаксического анализа.
enum class CarsType {
@Json(name = "SMALL")
SMALL,
@Json(name = "BIG")
BIG;
companion object {
fun nullableValueOf(name: String?) = when (name) {
null -> null
else -> valueOf(name)
}
}
}
2. В Parcerable read place используйте это так
data class CarData(
val carId: String? = null,
val carType: CarsType?,
val data: String?
) : Parcelable {
constructor(parcel: Parcel) : this(
parcel.readString(),
CarsType.nullableValueOf(parcel.readString()),
parcel.readString())