Перечисление Java, переопределяющее toString ()

Я никогда раньше не использовал классы перечисления Java для постоянных значений, в прошлом я обычно использовал подход «общедоступный финал». Я начал использовать перечисление сейчас и переопределяю метод toString (), чтобы он возвращал значение, отличное от имени перечисления.

У меня есть код JPA, в котором я создаю TypedQuery с именованными параметрами, один из которых является строковым представлением значения перечисления. Если я просто устанавливаю параметр с помощью Status.ACTIVE, я получаю правильное значение «A», но возникает исключение, потому что его тип на самом деле является Status, а не String. Это работает, только если я явно вызываю метод toString (). Я думал, что простое переопределение метода toString () приведет к возврату типа String, независимо от типа класса.

Это перечисление:

public enum Status {
    ACTIVE ("A"),
    PENDING ("P"),
    FINISHED ("F");

    private final String value;

    Status(String value) {
        this.value = value;
    }

    public String toString() {
        return value;
    }
};

Это TypedQuery:

    TypedQuery<MechanicTimeEvent> query = entityManager().createQuery("SELECT o FROM MechanicTimeEvent o WHERE o.id.mechanicNumber = :mechanicNumber AND o.id.status = :status", MechanicTimeEvent.class);
    query.setParameter("mechanicNumber", mechanicNumber);
    query.setParameter("status", Status.ACTIVE.toString());

person Patrick Grimard    schedule 13.10.2011    source источник
comment
Можно добавить отображение этого поля к вопросу?   -  person Augusto    schedule 13.10.2011
comment
Если вы ищете более лаконичный способ его написания, query.setParameter("status", Status.ACTIVE+""); подойдет.   -  person Eric    schedule 13.10.2011
comment
Поле в вашем @Entity относится к типу Status и помечено ли оно @Enumerated(EnumType.STRING)? Если это так, вы должны иметь возможность использовать перечисления в запросах.   -  person Tomasz Nurkiewicz    schedule 13.10.2011
comment
Здесь и здесь подобные проблемы и ответы уже обсуждались.   -  person A.H.    schedule 13.10.2011
comment
Поле в моем @Entity - это просто строка, но это всегда будет только значение A, P или F, поэтому я создал перечисление для этих значений, чтобы в коде я мог использовать что-то вроде Status.ACTIVE и его было бы более наглядно. Я решил, что переопределение метода toString () для возврата фактического значения вернет тип String без явного вызова toString ().   -  person Patrick Grimard    schedule 13.10.2011


Ответы (6)


Является ли поле status bean-компонента MechanicTimeEvent перечисляемым типом? Если нет, я бы предложил изменить его на тип перечисления Status.

Вы можете аннотировать его с помощью @Enumerated(EnumType.STRING)

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

public enum Status {
   ACTIVE,
   PENDING,
   FINISHED;
}
person lauwie    schedule 13.10.2011
comment
Это не был тип перечисления, поэтому я изменил его на тип Status и добавил предложенную вами аннотацию. Я думаю, это помогло. Странно, что если по моему запросу не найдено ни одной записи, он работает нормально, но если он находит запись, OpenJPA выдает исключение ArgumentException и говорит мне проверить синтаксис моего запроса, не знаю почему. - person Patrick Grimard; 13.10.2011
comment
Вы использовали значение перечисления вместо строки ?: т.е. query.setParameter (status, Status.ACTIVE) - person lauwie; 13.10.2011
comment
На самом деле проблема в том, что, поскольку база данных хранит только значения, то есть: A, P или F, то при создании оператора select OpenJPA передает значение A, возвращенное из запроса, методу valueOf перечисления. Поскольку он не находит A в перечислении, он генерирует исключение. Метод valueOf является окончательным, поэтому я не могу его переопределить. - person Patrick Grimard; 13.10.2011
comment
О да. Возможно обратная сторона, но при использовании перечислений в сочетании с JPA учитываются только имена, а не значения. Лично я бы просто удалил свойство value и использовал только ключевые имена перечисления. См. Мой одобренный ответ ;-) - person lauwie; 14.10.2011
comment
Привет, Лауви, я понял, как с этим справиться. В итоге я переключился с OpenJPA на EclipseLink. С EclipseLink я могу определить свое перечисление так же просто, как и у вас, без конструктора, а затем в моем поле сущности я добавляю пару аннотаций EclipseLink, чтобы позаботиться о преобразовании. Проверьте Gist, который я только что опубликовал. gist.github.com/1287713 - person Patrick Grimard; 14.10.2011

Если я правильно понимаю ваш вопрос, вам следует сделать сопоставление перечисления наоборот. Таким образом, состояния сохраняются как состояния, и JPA будет обрабатывать перечисление на основе его имени A, P, F).

public enum Status {
    A("ACTIVE"),
    P("PENDING"),
    F("FINISHED");

Таким образом, вы можете просто передать Status, не вызывая метод toString () в JPA. Метод .name () в ENUM будет вызываться автоматически, чтобы получить код состояния для сохранения.

person java_mouse    schedule 13.10.2011
comment
Вы не должны сокращать имена полей до одной буквы только потому, что это форма, которую использует ваша база данных / что бы то ни было. Разве вы не можете просто переопределить .name()? - person Eric; 13.10.2011
comment
Метод Enum.name () является окончательным, поэтому его нельзя переопределить. - person java_mouse; 13.10.2011
comment
Цель того, чтобы сделать это так, как у меня, состоит в том, чтобы, если кто-то еще прочитает код, Status.ACTIVE более очевиден, чем Status.A. - person Patrick Grimard; 13.10.2011
comment
Как Java API docs выделено жирным шрифтом: большинству программистов следует использовать toString (). Правильный путь - это переопределение toString(). - person José Andias; 14.01.2015

toString - это просто обычный метод в Object, который явно вызывается некоторыми методами, такими как PrintStream.println (помните System.out.println), или учитывается во время конкатенации с использованием оператора +. Не каждый метод должен реализовывать такое поведение.

Я бы посоветовал вам использовать более информативное имя метода, например getValue, и вызывать его явно вместо замены toString

person Jagat    schedule 13.10.2011

Это или вы просто реализуете геттер для значения:

public String getValue()

И вы вызываете это в своем коде:

query.setParameter("status", Status.ACTIVE.getValue());
person Cygnusx1    schedule 13.10.2011

java.lang.Enum said clearly:
 /**
 * Returns the name of this enum constant, exactly as declared in its
 * enum declaration.
 * 
 * <b>Most programmers should use the {@link #toString} method in
 * preference to this one, as the toString method may return
 * a more user-friendly name.</b>  This method is designed primarily for
 * use in specialized situations where correctness depends on getting the
 * exact name, which will not vary from release to release.
 *
 * @return the name of this enum constant
 */
 public final String name()

как говорят чердак, вы можете использовать метод "name", чтобы получить имя. также вы можете использовать метод toString ().

конечно, это просто имя этой константы перечисления.

person zg_spring    schedule 11.04.2012

person    schedule
comment
Эта реализация toString () требует слишком большого обслуживания, особенно если имена перечислений когда-либо будут добавлены в будущем. лучше всего присвоить строковое значение при создании экземпляра и вместо этого вернуть его. - person initialZero; 01.08.2013
comment
... как предусмотрено в решении вопроса. Этот код более читабелен, а расположение между сопоставлением строк (ACTIVE ("A")) также позволяет избежать запуска кода с опечатками (например: case 3: name = "E" вместо case 2: name = "F"). Кроме того, значение переключателя по умолчанию не имеет смысла. - person José Andias; 14.01.2015