Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Java annotations on enum values: passing the annotation instance on to user code

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)?

like image 266
Axel Avatar asked Apr 22 '26 13:04

Axel


1 Answers

While I'm not answering directly onto your question about annotations, I'm answering on the core of your problem (as you wrote: Or is there a more elegant solution (that does not involve using a framework)?). I wouldn't agree that this case is good place for using annotations.

I would suggest you consider slightly more elegant way of achieving desired pattern:

public enum Foo {
    A(FF.F1, FF.F4),
    B(FF.F4),
    C();

    enum FF {
        F1, F2, F3, F4;
    }

    boolean f1;
    boolean f2;
    boolean f3;
    boolean f4;

    Foo(FF... flags) {
        for (FF ff : flags)
            switch (ff) {
                case F1:
                    f1 = true;
                    break;
                case F2:
                    f2 = true;
                    break;
                case F3:
                    f3 = true;
                    break;
                case F4:
                    f4 = true;
                    break;
            }
    }
}

or even more simple:

public enum Foo {
    A(FF.F1, FF.F4),
    B(FF.F4),
    C();

    enum FF {
        F1, F2, F3, F4;
    }

    boolean[] f = new boolean[FF.values().length];;

    Foo(FF... flags) {
        for (FF ff : flags) {
            f[ff.ordinal()] = true;
        }
    }
}
like image 178
Andremoniy Avatar answered Apr 24 '26 03:04

Andremoniy



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!