【问题标题】:Java annotations on enum values: passing the annotation instance on to user code枚举值上的 Java 注释:将注释实例传递给用户代码
【发布时间】:2017-01-25 14:05:20
【问题描述】:

我们有一个包含很多条目的枚举类型。每个枚举值都有一些布尔(或其他)属性,大多数在 90% 的情况下具有相同的值。我们最终得到了这样的代码:

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;
    }
}

要编写的代码很多,而且难以阅读(“第三个位置的布尔值又是什么意思?”)。

这似乎是注释的用例:

@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);
        }
    }
}

但这不能更短吗?

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);
        }
    }
}

当然,现在我们必须使用bar.flags.f1() 而不是bar.f1()。一个例子:

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());
    }
  }
}

还有输出:

答:key=true 可见=true

B: key=false 可见=false

除了语法不同之外,还有什么理由不传递 Annotation 实例吗?还是有更优雅的解决方案(不涉及使用框架)?

【问题讨论】:

  • 您不是在传递“注释实例”,而是在传递一个类型,这几乎是标准的 Java。我不确定我是否理解这个问题。
  • @DaveNewton 我绕过Foo.flags,这是一个注释类型的对象。我只是不知道这是否是预期的用例。
  • 对,你在传递一个类型。这与传递任何其他类型没有什么不同。

标签: java enums annotations


【解决方案1】:

虽然我没有直接回答您关于注释的问题,但我正在回答您问题的核心(正如您所写:或者是否有更优雅的解决方案(不涉及使用框架) ?)。我不同意这种情况是使用注释的好地方。

我建议您考虑更优雅的方式来实现所需的模式:

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;
            }
    }
}

甚至更简单:

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;
        }
    }
}

【讨论】:

    【解决方案2】:

    枚举不需要是不可变的:

    enum Foo {
      A(){{f1(); f2();}},
      B(){{f4();}},
      C;
    
      private boolean f1;
      private boolean f2;
      private boolean f3;
      private boolean f4;
    
      private void f1() {f1 = true;}
      private void f2() {f2 = true;}
      private void f3() {f3 = true;}
      private void f4() {f4 = true;}
    }
    

    这或多或少地完成了你的意图,同时隐藏了细节。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2016-07-22
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2020-11-29
      相关资源
      最近更新 更多