当一个API发展时,处理"int枚举"模式与Java枚举共存的最佳方法是什么?

jpd*_*gle 5 java api enums

假设您正在维护最初几年前发布的API(在Java获得enum支持之前),并且它定义了一个枚举值为int的类:

public class VitaminType {
 public static final int RETINOL = 0;
 public static final int THIAMIN = 1;
 public static final int RIBOFLAVIN = 2;
}
Run Code Online (Sandbox Code Playgroud)

多年来,API已经发展并获得了Java 5特有的功能(通用接口等).现在您要添加一个新的枚举:

public enum NutrientType {
 AMINO_ACID, SATURATED_FAT, UNSATURATED_FAT, CARBOHYDRATE;
}
Run Code Online (Sandbox Code Playgroud)

"旧式"int-enum模式没有类型安全性,不可能添加行为或数据等,但它已发布并正在使用中.我担心混合两种枚举方式对API的用户来说是不一致的.

我看到三种可能的方法:

  • 放弃并定义新的枚举(NutrientType在我的虚构示例中)作为类的一系列整数VitaminType.你得到了一致性,但你没有利用类型安全和其他现代功能.

  • 决定在已发布的API中存在不一致:保持VitaminType原样,并添加NutrientTypeenum.采用a的VitaminType方法仍然被声明为采用int,采用a的方法NutrientType被声明为采用这种方法.

  • 弃用VitaminType该类并引入新的VitaminType2枚举.将new定义NutrientType为枚举.
    恭喜,在接下来的2 - 3年内,您可以杀死已弃用的类型,您将要处理每个方法的弃用版本,这些方法VitaminType将int作为int并添加每个方法的新foo(VitaminType2 v)版本.您还需要为每个已弃用的foo(int v)方法及其相应的foo(VitaminType2 v)方法编写测试,因此您只需将QA工作倍增.

什么是最好的方法?

McD*_*ell 6

API消费者将VitaminType与NutrientType混淆的可能性有多大?如果不太可能,那么维持API设计的一致性可能会更好,特别是如果建立了用户群并且您希望最小化客户所需的工作/学习的增量.如果可能出现混淆,那么NutrientType应该可能成为一个枚举.

这不一定是一夜之间的彻底改变; 例如,您可以通过枚举公开旧的int值:

public enum Vitamin {

    RETINOL(0), THIAMIN(1), RIBOFLAVIN(2);

    private final int intValue;

    Vitamin(int n) {
        intValue = n;
    }

    public int getVitaminType() {
        return intValue;
    }

    public static Vitamin asVitamin(int intValue) {
        for (Vitamin vitamin : Vitamin.values()) {
            if (intValue == vitamin.getVitaminType()) {
                return vitamin;
            }
        }
        throw new IllegalArgumentException();
    }

}

/** Use foo.Vitamin instead */
@Deprecated
public class VitaminType {

    public static final int RETINOL = Vitamin.RETINOL.getVitaminType();
    public static final int THIAMIN = Vitamin.THIAMIN.getVitaminType();
    public static final int RIBOFLAVIN = Vitamin.RIBOFLAVIN.getVitaminType();

}
Run Code Online (Sandbox Code Playgroud)

这允许您更新API并使您可以控制何时弃用旧类型以及在内部依赖于旧类型的任何代码中安排切换.

需要注意保持文字值与可能已经与旧的消费者代码内联的值保持同步.


Ste*_* B. 3

个人观点是,可能不值得尝试转换。一方面,“public static final int”这一惯用法不会很快消失,因为它在 JDK 中随处可见。另一方面,追踪原始整数的使用可能真的很不愉快,因为你的类将编译掉引用,所以你可能不知道你已经破坏了任何东西,直到为时已晚(我的意思是

class A
   {
       public static final int MY_CONSTANT=1
   }

   class B
   {
           ....
           i+=A.MY_CONSTANT; 
   }
Run Code Online (Sandbox Code Playgroud)

被编译成

i+=1
Run Code Online (Sandbox Code Playgroud)

因此,如果您重写 A,您可能永远不会意识到 B 已损坏,直到您稍后重新编译 B。

这是一个众所周知的习语,保留它可能并没有那么可怕,当然比其他选择更好。