Java 枚举覆盖 toString()方法

8
我以前并没有使用Java的枚举类来表示常量值,而是使用了“public final”方法。现在我开始使用枚举类,并且覆盖toString()方法以返回与枚举名称不同的值。
我有一些JPA代码,在其中使用命名参数创建TypedQuery,其中之一是枚举值的字符串表示形式。如果我仅使用Status.ACTIVE设置参数,则会得到正确的“A”值,但是会抛出异常,因为它的类型实际上是Status而不是字符串。只有当我显式地调用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());

可以将该字段的映射添加到问题中吗? - Augusto
1
如果你想要更简洁的写法,可以使用 query.setParameter("status", Status.ACTIVE+""); - Eric
1
你的 @Entity 中的字段是否为 Status 类型,并且是否使用了 @Enumerated(EnumType.STRING) 注解?如果是,那么你应该可以在查询中很好地使用枚举。 - Tomasz Nurkiewicz
类似的问题和答案已经在这里和这里讨论过了,它们都与如何在JPA中使用枚举有关。 - A.H.
我在@Entity中的字段只是一个字符串,但它只能是A、P或F这些值之一,所以我为这些值创建了枚举,这样在代码中,我可以使用像Status.ACTIVE这样的东西,这将更具描述性。我发现重写toString()方法返回实际值将返回一个String类型,而不需要明确调用toString()。 - Patrick Grimard
6个回答

5
public enum Status {
    ACTIVE,
    PENDING,
    FINISHED;

    @Override
    public String toString() {
        String name = "";
        switch (ordinal()) {
        case 0:
            name = "A";
            break;
        case 1:
            name = "P";
            break;
        case 2:
            name = "F";
            break;
        default:
            name = "";
            break;
        }
        return name;
    }
};

8
如果将来枚举名称有所添加,这个toString()实现需要太多的维护工作。最好在实例化时分配一个字符串值并返回该值。 - initialZero
...就像问题解决方案所提供的那样。那段代码更易读,并且在"String mapping"(ACTIVE ("A"))之间的局部性也避免了因拼写错误而运行错误的代码(例如:case 3: name = "E"而不是case 2: name = "F")。此外,switch语句的默认值是没有意义的。 - José Andias

3
如果我正确理解您的问题,那么您应该以另一种方式进行枚举映射。这样状态将被存储为状态,并且JPA将根据其名称A、P、F处理枚举。
public enum Status {
    A("ACTIVE"),
    P("PENDING"),
    F("FINISHED");

这样你就可以直接传递Status而不必调用toString()方法到JPA。ENUM上的.name()方法将自动被调用以获取持久化状态码。


1
你不应该仅仅因为数据库或其他使用这种形式,就将字段名称缩短为一个字母。难道你不能重写.name()吗? - Eric
2
Enum.name() 方法是 final 的,因此无法被覆盖。 - java_mouse
2
我这样做的目的是为了让其他人阅读代码时,Status.ACTIVE比Status.A更明显。 - Patrick Grimard
正如Java API文档中粗体字所指出的:大多数程序员应该使用toString()。覆盖toString()是正确的方法。 - José Andias

3

MechanicTimeEvent bean的字段status是枚举类型吗?如果不是,我建议将其更改为枚举类型Status

您可以使用注释@Enumerated(EnumType.STRING)进行注释。

此外,我建议删除枚举的值部分,只使用名称,例如:

public enum Status {
   ACTIVE,
   PENDING,
   FINISHED;
}

它不是枚举类型,所以我将其更改为Status类型,并添加了您建议的注释。我认为这样做就可以了。但现在奇怪的是,如果我的查询未找到记录,则它可以正常工作,但如果它找到一条记录,则OpenJPA会抛出ArgumentException并告诉我检查查询语法,不确定为什么。 - Patrick Grimard
你是否使用枚举值而不是字符串?例如:query.setParameter("status", Status.ACTIVE) - lauwie
实际上,问题在于由于数据库仅存储值,即A、P或F,因此在创建选择语句时,OpenJPA将从查询返回的值A传递给枚举的valueOf方法。由于它在枚举中找不到A,所以它会抛出异常。valueOf方法是final的,因此我无法覆盖它。 - Patrick Grimard
啊,是的。也许有一个缺点,但是当将枚举与JPA结合使用时,只有名称才有用,而不是值。个人建议只删除value属性,并仅使用枚举的键名。请参见我的bjoetified答案;-) - lauwie
嘿lauwie,我搞清楚怎么处理它了。最终我从OpenJPA切换到了EclipseLink。使用EclipseLink,我可以像你那样简单地定义我的枚举,不需要构造函数,然后在我的实体字段上添加一些EclipseLink注释以处理转换。请查看我刚发布的Gist。https://gist.github.com/1287713 - Patrick Grimard

0

或者你可以简单地实现一个获取值的方法:

public String getValue()

然后在你的代码中调用:

query.setParameter("status", Status.ACTIVE.getValue());

0

toString 是 Object 中的一个普通方法,有一些方法会显式地调用它,例如 PrintStream.println(记得 System.out.println),或者在使用 + 操作符连接字符串时隐式调用它。并非每个方法都需要实现这种行为。

我建议您使用更具描述性的方法名称,例如 getValue 并显式调用它,而不是重写 toString


-1
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()方法。

当然,这只是此枚举常量的名称。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接