考虑以下代码段…
public static UserStatus getEnum(int code) {
switch (code) {
case 0:
return PENDING;
case 1:
return ACTIVE;
case 2:
return SUSPENDED;
case 3:
return DELETED;
case 4:
return LOGIN_DISABLED;
default:
return null;
}
}
现在案例(案例3和案例4)中的数字3和4被SONAR检测为幻数。
为了避免这个问题,我改变了我的代码段如下…
public static UserStatus getEnum(int code) {
final int Pending=0;
final int Active=1;
final int Suspended=2;
final int Deleted= 3;
final int Login_details=4;
switch (code) {
case Pending:
return PENDING;
case Active:
return ACTIVE;
case Suspended:
return SUSPENDED;
case Deleted:
return DELETED;
case Login_details:
return LOGIN_DISABLED;
default:
return null;
}
}
在这种情况下,这是解决幻数问题的好方法吗?
我认为您希望避免在代码中使用整数字面值。您的解决方案不是特别有效,因为它只是将文字移到方法的顶部。它获得了一点好处,因为它为常量提供了有意义的名称,但这些名称对方法来说是私有的。
更好的方法是将数字定义为接口中的字段。然后,您可以静态地导入字段,并将它们用作常量的符号名。
如果枚举的声明顺序与常量相同:
enum UserStatus {PENDING, ACTIVE, SUSPENDED, DELETED, LOGIN_DISABLED}
你可以做另一个技巧:
public static UserStatus getEnum(int code) {
UserStatus[] values = UserStatus.values();
return (code >= 0 && code < values.length) ? values[code] : null;
}
但是,这会在常量值和枚举声明之间创建一个链接。这可能没有问题,具体取决于在调用getEnum
时生成实际参数值的位置。
问题是直接和明显的:当人们正在阅读您的代码时,它不是很明显为什么我将给PENDING。1是什么意思?
你应该赋予它语义意义。使用常量是通常应该做的:
(假设getEnum()
是UserService的一部分,代码应该是这样的)
public interface UserService {
public static final int USER_STATUS_VAL_PENDING = 1;
public static final int USER_STATUS_VAL_ACTIVE = 2;
UserStatus getUserStatus(int userStatusVal);
}
public class SimpleUserService implements UserService {
public UserStatus getUserStatus(int userStatusVal) {
switch userStatusVal {
case USER_STATUS_VAL_PENDING:
return UserStatus.PENDING;
case USER_STATUS_VAL_ACTIVE:
return UserStatus.ACTIVE;
//.....
}
}
正如另一个答案所建议的那样,您可以依赖enum的序数值来进行int-enum映射。但是,您必须意识到,如果重新排列enum的值,或者在末尾以外的位置添加新值,这种方式可能会导致问题。顺序值将被更改,并且您无法覆盖它。
另一件需要注意的事情是,你的代码中有些地方做得不对:
- 由于目的是通过使整数字面值成为常量来赋予其语义意义,因此它应该对调用者可见,因此局部final变量不是正确的方法。你应该使用ALL_CAP_UNDERSCORE_DELIMITED作为常量名