10

有一个带有三个枚举类型参数的构造函数:

public SomeClass(EnumType1 enum1,EnumType2 enum2, EnumType3 enum3)
{...}

enum 类型的三个参数不允许与所有可能的值组合:

例子:

EnumType1.VALUE_ONE、EnumType2.VALUE_SIX、EnumType3.VALUE_TWENTY 是有效组合。

但以下组合无效:

EnumType1.VALUE_TWO、EnumType2.VALUE_SIX、EnumType3.VALUE_FIFTEEN

每个 EnumTypes 都知道它可以与哪些值组合:

EnumType1 和另外两个实现 isAllowedWith() 方法来检查如下:

public enum EnumType1 {

VALUE_ONE,VALUE_TWO,...;

    public boolean isAllowedWith(final EnumType2 type) {
    switch (this) {
        case VALUE_ONE:
            return type.equals(Type.VALUE_THREE);
        case VALUE_TWO:
            return true;
        case VALUE_THREE:
            return type.equals(Type.VALUE_EIGHT);
        ...
    }
}

我需要在编译时运行该检查,因为在我的项目中,组合在运行时始终正确是极其重要的。

我想知道是否有可能使用用户定义的注释运行该检查?

每个想法都值得赞赏:)

4

5 回答 5

4

上面的帖子没有带来编译时检查的解决方案,这是我的:

为什么不使用嵌套 Enum的概念。

您将EnumType1包含它自己的值 + 一个嵌套的EnumType2和这个嵌套的EnumType3

你可以用你有用的组合来组织整体。您最终可能会得到 3 个类(EnumType1、2 和 3),并且每个相关值中的每一个都包含具有允许的关联值的其他值。

你的电话看起来像这样(假设你想EnumType1.VALUE_ONE与 关联EnumType2.VALUE_FIFTEEN):

EnumType1.VALUE_ONE.VALUE_FIFTEEN  //second value corresponding to EnumType2

因此,您还可以:( EnumType3.VALUE_SIX.VALUE_ONE其中 SIX 由 type3 知道,ONE 由 type1 知道)。

您的电话将更改为:

public SomeClass(EnumType1 enumType)

=> 示例:

SomeClass(EnumType1.VALUE_ONE.VALUE_SIX.VALUE_TWENTY) //being a valid combination as said

为了更好地澄清它,请查看这篇文章:在 Java 中使用嵌套枚举类型

于 2012-10-16T15:38:57.420 回答
3

所以最简单的方法是 1) 定义文档来解释有效的组合和
2) 在构造函数中添加检查

如果构造函数抛出异常,那是调用者的责任。基本上你会做这样的事情:

public MyClass(enum foo, enum bar, enum baz)  
{  
    if(!validateCombination(foo,bar,baz))
    {  
        throw new IllegalStateException("Contract violated");
    }  
} 


private boolean validateCombination(enum foo, enum bar, enum baz)  
{  
    //validation logic
} 

现在这部分是绝对关键的。将类标记为final,部分构造的对象可能会被恢复并滥用以破坏您的应用程序。对于标记为 final 的类,恶意程序无法扩展部分构造的对象并造成严重破坏。

于 2012-10-16T15:21:05.390 回答
2

另一种想法是编写一些自动化测试来捕捉这一点,并将它们作为打包/部署应用程序之前的强制性步骤连接到您的构建过程中。

如果您考虑一下您要在这里捕获的内容,那是合法错误的代码。虽然您可以在编译阶段发现这一点,但这正是测试的目的。

这将符合您不能使用非法组合构建任何代码的要求,因为构建仍然会失败。可以说,其他开发人员比编写自己的注释处理器更容易理解......

于 2012-10-16T15:58:30.623 回答
1

我知道的唯一方法是使用注释。

这就是我的意思。现在您的构造函数接受 3 个参数:

public SomeClass(EnumType1 enum1,EnumType2 enum2, EnumType3 enum3){}

所以你这样称呼它:

SomeClass obj = new SomeClass(EnumTupe1.VALUE1, EnumTupe2.VALUE2, EnumTupe1.VALUE3)

将构造函数更改为私有。创建接受 1 个您想要的任何类型的参数的公共构造函数。它可能只是一个假参数。

public SomeClass(Placeholder p)

现在,您必须要求在每个参数都使用特殊注释进行注释时调用此构造函数。让我们称之为TypeAnnotation

SomeClass obj = new SomeClass(TypeAnnotation(
    type1=EnumType1.VALUE1, 
    type2=EnumTupe2.VALUE2,
    type3=EnumTupe1.VALUE3)
    p3);

该调用更冗长,但这是我们必须为编译时验证支付的费用。

现在,如何定义注解?

@Documented @Retention({RetentionPolicy.RUNTIME, RetentionPolicy.SOURCE}) @Target(PARAMETER) @interface TypeAnnotation { EnumType1 type1(); EnumType2 type3(); 枚举类型3 type3(); }

请注意,target 是 PARAMETER,保留值是 RUNTIME 和 SOURCE。

RUNTIME 允许在运行时读取此注解,而 SOURCE 允许创建可以在运行时验证参数的注解处理器。

现在公共构造函数将调用 3 参数私有构造函数:

public SomeClass(Placeholder p) { this(readAnnotation(EnumType1.class), readAnnotation(EnumType2.class), readAnnotation(EnumType3.class), ) }

我没有readAnnotation()在这里实现:它应该是获取堆栈跟踪、返回 3 个元素(到公共 costructor 的调用者)并解析 annotation 的静态方法TypeAnnotation

现在是最有趣的部分。您必须实现注释处理器。看看这里的说明和这里的注释处理器的例子。

您必须将此注释处理器的使用添加到您的构建脚本和(可选)到您的 IDE。在这种情况下,当您的兼容性规则被违反时,您将得到真正的编译错误。

我相信这个解决方案看起来太复杂了,但如果你真的需要这个,你可以这样做。可能需要一天左右的时间。祝你好运。

于 2012-10-16T18:04:23.773 回答
0

好吧,我不知道编译时检查,但我认为这是不可能的,因为编译器如何知道哪个值将传递给构造函数(如果您的枚举变量的值是在运行时计算的(例如,通过If 子句) ? 这只能在运行时通过使用为枚举类型实现的验证器方法来验证。

例子 :

如果在你的代码中你有这样的东西:

EnumType1 enumVal;

if (<some condition>) {
  enumVal = EnumType2.VALUE_SIX;
} else {
  enumVal = EnumType2.VALUE_ONE;
}

编译器无法知道哪些值将分配给 enumVal,因此在评估 if 块之前它无法验证传递给构造函数的内容(只能在运行时完成)

于 2012-10-16T15:23:14.353 回答