We have an enum type with quite a lot entries. Each enum value has some boolean (or other) attributes, most have the same value in 90% of cases. We ended up with code like this:
enum Foo {
A(true, false, false, true),
B(false, false, false, true),
C(false, false, false, false);
final boolean f1;
final boolean f2;
final boolean f3;
final boolean f4;
Foo(boolean f1, boolean f2, boolean f3, boolean f4) {
this.f1 = f1;
this.f2 = f2;
this.f3 = f3;
this.f4 = f4;
}
}
This is a lot of code to write, and hard to read ("what was that boolean`s meaning at 3rd position again?").
This seems to be a use case for annotations:
@Retention(RetentionPolicy.RUNTIME)
@interface Flags {
boolean f1() default false;
boolean f2() default false;
boolean f3() default false;
boolean f4() default false;
}
enum Foo {
@Flags(f1=true)
A,
@Flags(f4=true)
B,
@Flags()
C;
final boolean f1;
final boolean f2;
final boolean f3;
final boolean f4;
Foo(boolean flag1, boolean f2, boolean f3, boolean f4) {
try {
Flags flags = Objects.requireNonNull(Foo.class.getField(name()).getAnnotation(Flags.class),
"Annotation for '"+getClass().getSimpleName()+"."+name()+"' is missing!");
this.f1 = flags.f1();
this.f2 = flags.f2();
this.f3 = flags.f3();
this.f4 = flags.f4();
} catch (NoSuchFieldException|SecurityException e) {
// should not happen
throw new RuntimeException(e);
}
}
}
But can't this be shorter?
enum Foo {
@Flags(f1=true)
A,
@Flags(f4=true)
B,
@Flags()
C;
final Flags flags;
Foo(boolean flag1, boolean f2, boolean f3, boolean f4) {
try {
this.flags = Objects.requireNonNull(Foo.class.getField(name()).getAnnotation(Flags.class),
"Annotation for '"+getClass().getSimpleName()+"."+name()+"' is missing!");
} catch (NoSuchFieldException|SecurityException e) {
// should not happen
throw new RuntimeException(e);
}
}
}
Of course, now we have to use bar.flags.f1() instead of bar.f1(). An example:
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.util.Objects;
public class Scratch {
@Retention(RetentionPolicy.RUNTIME)
@interface Flags {
boolean key() default false;
boolean visible() default true;
}
enum Foo {
@Flags(key = true)
A,
@Flags(visible = false)
B;
final Flags flags;
Foo() {
try {
this.flags = Objects.requireNonNull(Foo.class.getField(name()).getAnnotation(Flags.class),
"Annotation for '" + getClass().getSimpleName() + "." + name() + "' is missing!");
} catch (NoSuchFieldException | SecurityException e) {
// Sollte nicht vorkopmmen
throw new RuntimeException(e);
}
}
}
public static void main(String[] args) {
for (Foo bar : Foo.values()) {
System.out.format("%s: key=%-8svisible=%-8s%n", bar.name(), bar.flags.key(), bar.flags.visible());
}
}
}
And the output:
A: key=true visible=true
B: key=false visible=false
Apart from the different syntax, is there any reason not to pass the Annotation instance around? Or is there a more elegant solution (that does not involve using a framework)?
Foo.flagswhich is an object of annotation type. I just don't know if that's the intended use case.