容易出错的 Java 枚举重构

Error-prone Java enum refactoring

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

我无法覆盖等号,因为 JLS 特别禁止这样做:

The equals method in Enum is a final method that merely invokes super.equals on its argument and returns the result, thus performing an identity comparison.

代码如下所示:

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 并抛出异常,但对于我的示例,它只会 return false。还有一个像 getGender 这样的方法,它是 returning 字符串,现在是 return 一个枚举,所以可能会有我遗漏的地方,并且将一个字符串与枚举

进行比较

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

您可以将最终的字符串变量移动到一个单独的 class 中,比如 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).

会更明显

我会这样做:

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个枚举,得到更可靠的结果

如评论中所述,Object#equals(...) 不是类型安全的。您无法阻止 API 的用户将错误类型的对象传递给它。在那种情况下,它应该只是 return false。如果有人这样做,最终他们会注意到它总是 returning 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";