Magic Number问题的解决方案......?

时间:2013-03-21 04:21:30

标签: java switch-statement sonarqube magic-numbers

考虑以下代码段...

 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;
    }
}

在这种情况下,这是解决幻数问题的好方法吗?

2 个答案:

答案 0 :(得分:7)

我认为你想避免在代码中使用整数文字。您的解决方案不是特别有效,因为它只是将文字移动到方法的顶部。它获得了一点点因为它为常量提供了有意义的名称,但这些名称对于该方法是私有的。

更好的方法是将数字定义为接口中的字段。然后,您可以静态导入字段并将其用作常量的符号名称。

如果枚举的声明顺序与常量相同:

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时生成实际参数值的位置。

答案 1 :(得分:1)

问题是直截了当且明显的:当人们阅读你的代码时,为什么1会给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映射。但是,您必须要注意,如果您重新生成枚举值,或者在结尾处添加新值,这种方式可能会导致问题。序数值将被更改,您无法覆盖它。

需要注意的另一件事是,你的代码中有些东西没有完成:

  1. 由于目的是通过使整数文字成为常量来赋予整数文字语义,因此它应该是调用者可见的内容,因此本地最终变量不是正确的方法。
  2. 您应该将ALL_CAP_UNDERSCORE_DELIMITED用于常量名称