容易出错的Java枚举重构

时间:2015-08-10 20:27:52

标签: java enums findbugs

我正在重构一些旧代码以使用enum代替String常量。当我发现比较enumString不会抛出异常时,我正在审核我的代码。我无法删除旧常量,因为其他项目仍在使用它们。

我无法覆盖等于,因为JLS明确禁止这样做:

  

Enum中的equals方法是一个仅调用的最终方法   对其参数进行super.equals并返回结果,从而执行   身份比较。

代码如下所示:

public enum Gender{
    MALE,
    FEMALE
}

// Constants for genders
public static final String MALE = "Male";
public static final String FEMALE = "Female";

//following are obviously false
MALE.equals(Gender.MALE) 
Gender.MALE.equals(MALE)

对于常规对象,我可以覆盖equals并抛出异常,但对于我的示例,它将返回false。还有一个类似getGender的方法,它返回一个字符串,现在返回一个枚举,所以我可以错过一些地方,并将一个字符串与枚举进行比较

这很容易出错。 FindBugs也没有报告任何错误。 无论如何我可以防止这种情况吗?

3 个答案:

答案 0 :(得分:5)

如评论中所述,Object#equals(...)不是类型安全的。您无法阻止API的用户将其传递给错误类型的对象。在那种情况下,它应该只返回false。如果有人这样做,最终他们会注意到它总是返回错误并去寻找错误。

您应该弃用String常量以引起对首选方式的注意:

/**
 * New code should use {@link Gender#MALE}.
 */
@Deprecated
public static final String MALE = "Male";
/**
 * New code should use {@link Gender#FEMALE}.
 */
@Deprecated
public static final String FEMALE = "Female";

答案 1 :(得分:2)

我会做这样的事情:

public enum Gender {
    MALE("Male"),
    FEMALE("Female");

    private final String val;

    Gender(String val) {
        this.val = val;
    }

    public static Gender getEnum(String value) {
        for (Gender a : values()) {
            if (a.getVal().equalsIgnoreCase(value)) {
                return a;
            }
         }
         return throw new IllegalArgumentException("no gender known");
    }

     public String getVal() {
         return val;
     }
}

它允许您从字符串创建枚举的实例,然后比较枚举

Gender genderFromString = Gender.getEnum(someString);
genderFromString.equals(Gender.MALE);

通过将字符串转换为枚举的实例然后比较2个枚举,可以获得更可靠的结果

答案 2 :(得分:1)

您可以将最终的String变量移动到一个单独的类中,比如StringConstants

public class StringConstants {
    public static final String MALE = "Male";
    public static final String FEMALE = "Female";
}

然后在您的代码中,当有人试图将StringConstants.MALEGender.MALE进行比较时,这将是一个更明显的错误。

此外,将枚举重命名为GenderEnum可能会有所帮助,因为这样更明显的是你不想做GenderEnum.MALE.equals(StringConstants.MALE)