易出错的Java枚举重构
我正在重构一些旧代码,以使用易出错的Java枚举重构,java,enums,findbugs,Java,Enums,Findbugs,我正在重构一些旧代码,以使用enum,而不是String常量。我在查看代码时注意到,将enum与String进行比较不会引发异常。我无法删除旧常量,因为其他项目仍在使用它们 我无法覆盖equals,因为JLS明确禁止: Enum中的equals方法是仅调用 super.equals并返回结果,因此执行 身份比较 代码如下所示: public enum Gender{ MALE, FEMALE } // Constants for genders public static fi
enum
,而不是String
常量。我在查看代码时注意到,将enum
与String
进行比较不会引发异常。我无法删除旧常量,因为其他项目仍在使用它们
我无法覆盖equals,因为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)
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;
}
}
对于常规对象,我可以重写equals并抛出异常,但对于我的示例,它只返回false。还有一个像getGender这样的方法,它返回字符串,现在返回一个枚举,所以我可能会错过一些地方,并将字符串与枚举进行比较
这很容易出错。FindBugs也没有报告任何bug。
有什么我可以防止的吗?您可以将最后的字符串变量移动到一个单独的类中,比如
StringConstants
public class StringConstants {
public static final String MALE = "Male";
public static final String FEMALE = "Female";
}
那么在您的代码中,当有人试图将StringConstants.MALE
与Gender.MALE
进行比较时,这将是一个更明显的错误
另外,将枚举重命名为
GenderEnum
可能会有更大的帮助,因为这样做会更加明显,您不想执行GenderEnum.MALE.equals(StringConstants.MALE)
我会这样做:
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)
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);
通过将字符串转换为枚举的一个实例,然后比较这两个枚举,可以获得更可靠的结果。如注释中所述,
Object#equals(…)
不是类型安全的。您无法阻止API的用户向其传递错误类型的对象。在这种情况下,它只需返回false
。如果有人这样做了,最终他们会注意到它总是返回false并查找bug
您应该弃用字符串
常量,以提请注意首选的操作方式:
/**
* 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";
您可以将
==
与枚举一起使用。但是您在传递这些常量的地方没有进行类型检查吗?也就是说,期望String
的方法在编译时会大声说您正在向它们传递枚举?或者您只是在使用对象
或原始列表?@hoverCraftfullOfels是的,但对于常规对象,您可以在equals中捕获此项并抛出异常;对于一个枚举,我不能;它只会返回false“对于一个常规对象,您可以用equals捕捉它并抛出一个异常”——您可以,但不应该。当传递错误的类型时,重写的equals(…)
方法应该返回false
,就像enum
一样。从equals
引发异常(而不是NPE)肯定会打破最小惊讶原则(以及错误)。移动它们会破坏源代码兼容性。OP提到其他项目正在使用这些常量,因此这将是非常糟糕的。没错,但对于大多数现代IDE来说,找到字符串常量和prependStringConstants
的所有用法将非常简单。您不应该强迫其他开发人员这样做。一旦一个公共API失去了alpha,你就应该把它看成石头。改变它看起来非常不称职和不专业。您唯一应该对公共API进行突破性更改的时间是当您遇到主要版本号时。即使如此,也只在需要启用功能更改时才执行此操作,而不只是重新组织或重命名内容。使用javac-Xlint:deprecation
编译时,将对@Deprecated
项的所有使用发出警告(在Eclipse等中也有相应的选项),这将有助于消除未使用枚举替换字符串的遗漏事件。