有一个带有三个enum类型参数的构造函数:
public SomeClass(EnumType1 enum1,EnumType2 enum2, EnumType3 enum3)
{...}
类型枚举的三个参数不允许与所有可能的值组合使用:
示例:
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);
...
}
}
我需要在编译时运行该检查,因为在我的项目中,组合在运行时始终是正确的,这是非常重要的。
我想知道是否有可能使用用户定义的注释运行该检查?
赞赏每一个想法:)
答案 0 :(得分: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
为了更好地澄清它,请查看此帖:Using nested enum types in Java
答案 1 :(得分: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的类,恶意程序无法扩展部分构造的对象并造成严重破坏。
答案 2 :(得分:2)
另一个想法是编写一些自动化测试来捕获它,并在打包/部署应用程序之前将它们作为必修步骤挂钩到构建过程中。
如果您考虑一下您要在此处捕获的内容,那么代码是合法但错误。虽然你可以在编译阶段捕捉到这一点,但这正是测试的意义所在。
这符合您无法使用非法组合构建任何代码的要求,因为构建仍然会失败。并且可以说,除了编写自己的注释处理器之外,其他开发人员更容易理解......
答案 3 :(得分: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(); EnumType3 type3(); }
请注意目标是PARAMETER,保留值是RUNTIME和SOURCE。
RUNTIME允许在运行时读取此注释,而SOURCE允许创建可在运行时验证参数的注释处理器。
现在公共构造函数将调用3参数private construcor:
public SomeClass(Placeholder p){ this(readAnnotation(EnumType1.class),readAnnotation(EnumType2.class),readAnnotation(EnumType3.class),) }
我这里没有实现readAnnotation()
:它应该是静态方法,它接受堆栈跟踪,返回3个元素(对公共构造函数的调用者)并解析注释TypeAnnotation
。
现在是最有趣的部分。您必须实现注释处理器。 请查看here以获取说明,并here查看注释处理器的示例。
您必须将此批注处理器的用法添加到构建脚本和(可选)IDE的用法。在这种情况下,当违反兼容性规则时,您将收到真正的编译错误。
我相信这个解决方案看起来太复杂了,但如果你真的需要这个,你就可以做到这一点。可能需要一天左右的时间。祝你好运。
答案 4 :(得分:0)
好吧,我不知道编译时间检查但我不认为这是可能的,因为编译器如何知道将哪个值传递给构造函数(如果你的枚举变量的值是在运行时计算的(例如,通过If子句)? 这只能在运行时通过使用为枚举类型实现的验证器方法进行验证。
示例:
如果您的代码中有这样的内容:
EnumType1 enumVal;
if (<some condition>) {
enumVal = EnumType2.VALUE_SIX;
} else {
enumVal = EnumType2.VALUE_ONE;
}
编译器无法知道将哪个值分配给enumVal,因此在if块被评估之前,它无法验证传递给构造函数的内容(这只能在运行时完成)