【问题标题】:Java enum autogenerate getInstance method?Java枚举自动生成getInstance方法?
【发布时间】:2016-01-04 16:59:35
【问题描述】:

假设我有以下 java 枚举

public enum Color {
  RED(10),
  GREEN(22),
  BLUE(33);

  private int value;

  Color(int value) {
    this.value = value;
  }

  public int intValue() {
    return value;
  }
}

为了能够通过给定的整数值获取颜色实例,我需要添加如下方法:

public static Color getInstance(int value) {
  switch (value) {
    case 10: return RED;
    case 22: return GREEN;
    case 33: return BLUE;
    default: throw new IllegalArgumentException("Invalid color value");
  }
}

是否可以从 IDE 自动生成此方法? (最好是 IntelliJ)?

【问题讨论】:

  • 您一生中预计会写多少这样的enums?
  • 我在一个项目中工作,其中要引入许多枚举,其中包含三个以上的值。手动编写所有这些东西真的很浪费时间。
  • 看在上帝的份上,请使用通用颜色代码。这将使您的生活以及将追随您的开发人员变得更加轻松。这样,你就不用处理这个枚举的事情了。
  • @WeareBorg 完全同意 :)

标签: java intellij-idea enums auto-generate


【解决方案1】:

不必在每个enum 中重复代码,您可以为具有int 值的所有类型的枚举实现一个中心实用程序方法。这是可能的,因为enums 可以实现接口,因此您可以定义访问int 值的统一方式:

public interface IntValued {
    int intValue();
}
/** get the particular {@link IntValued} {@code enum} constant. */
public static <T extends Enum<T>&IntValued> T get(Class<T> type, int value) {
    return type.cast(GET.get(type).apply(value));
}
private static ClassValue<IntFunction<Enum>> GET = new ClassValue<IntFunction<Enum>>() {
    protected IntFunction<Enum> computeValue(Class<?> type) {
        return prepare(type);
    }
};
// invoked only once per enum type
static IntFunction<Enum> prepare(Class<?> type) {
    Enum[] values=type.asSubclass(Enum.class).getEnumConstants();
    if(values.length==0) return i -> null;
    IntSummaryStatistics s=Arrays.stream(values)
        .mapToInt(o -> ((IntValued)o).intValue())
        .summaryStatistics();
    int min=s.getMin(), max=s.getMax();
    if((max-min+1)<s.getCount()*2) {
        Enum[] linear=new Enum[max-min+1];
        for(Enum e: values) linear[((IntValued)e).intValue()-min]=e;
        return i -> i<min||i>max? null: linear[i-min];
    }
    Map<Integer, Enum> map = Arrays.stream(values).collect(
        Collectors.toMap(o -> ((IntValued)o).intValue(), Function.identity()));
    return map::get;
}

这使用ClassValue,这是一种 JRE 提供的将自定义元数据关联到类的方法。 ClassValue 只负责调用一次初始化代码,并且仍然允许垃圾收集/类卸载。上面的代码动态确定要使用的查找结构。如果特定enum 类型的int 值密集,则使用数组,否则使用Map

您可以按如下方式使用它:

public enum Color implements IntValued {
    RED(10),
    GREEN(22),
    BLUE(33);

    private int value;

    Color(int value) {
      this.value = value;
    }

    public int intValue() {
      return value;
    }
}

 

Color color=get(Color.class, 22);
System.out.println(color);

请注意,与生成的 switch 语句不同,此解决方案在这些 int 值发生更改时不会中断。

【讨论】:

  • 不错!我相信是你前段时间向我指出Class&lt;T&gt; 有一个cast 方法。 return type.cast(GET.get(type).apply(value)); 会避免 evil 演员表。
  • @OldCurmudgeon:你是对的;在这里,Netbeans 不会在其默认配置中生成各种未经检查的警告,这适得其反。
  • 这似乎不必要地复杂。为什么不只是一个带有 public static &lt;T extends IntValued&gt; Map&lt;Integer, T&gt; mapByValue(Collection&lt;T&gt; values) 这样的签名的静态方法,它的主体是你倒数第二个语句?它甚至不需要特定于枚举类型。
  • @VGR:重点在于,每次调用get 方法时,这段代码不会填充地图。对于具体的enum,它最多只这样做一次,但是,正如已经解释过的,如果int 的值很密集,它甚至可以使用更快的基于数组的访问器。每个后续调用都将重用在第一次调用时创建的访问器函数。目标是与问题的switch 陈述一样快。
  • @VGR:这是问题的前提。如果您查看那里的 cmets,我已经问过,这些 enum 类型中有多少是预期的。此解决方案是针对 OP 的假设,即数量如此之大以至于每个 enum 自动生成的代码是合理的。因此,我提供了一个解决方案,它无需为每个 enum 类型添加代码,但与原始 switch 解决方案的性能相当(tableswitch 用于密集数字,lookupswitch 用于其他类型)。每种类型的映射不会有太大问题,但这种解决方案也是为了避免 每次调用 映射。
【解决方案2】:

如果您使用的是 Java 8,则可以利用 interface 中的 default 方法向任何类添加功能 - 甚至是 enums。

看看here,我将反向查找添加到enum,只需将其设为implement ReverseLookup

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-03-31
    • 2012-01-28
    • 2021-07-11
    • 1970-01-01
    • 1970-01-01
    • 2013-09-23
    相关资源
    最近更新 更多