【问题标题】:Shorthand notation for Enum implementing lambda possible interfaceEnum 实现 lambda 可能接口的简写符号
【发布时间】:2016-03-02 15:04:07
【问题描述】:

看到一个来自 jparsec 的 pre-Java 8 示例,我想知道是否可以利用 lambdas 来获得更具表现力的语法。

原例:

enum BinaryOperator implements Map2<Double,Double,Double> {
  PLUS {
    public Double map(Double a, Double b) {
      return a + b;
    }
  },
  MINUS {
    public Double map(Double a, Double b) {
      return a - b;
    }
  },
  MUL {
    public Double map(Double a, Double b) {
      return a * b;
    }
  },
  DIV {
    public Double map(Double a, Double b) {
      return a / b;
    }
  }
}

在哪里

public interface Map2<A, B, T> {

  /** Maps {@code a} and {@code b} to the target object. */
  T map(A a, B b);
}

一个普通的 Map2 实例很容易创建,例如(a,b)-&gt;a+b,但是对于实现单个函数接口的枚举,是否有一种简洁的方法可以做到这一点?

我当前的解决方案看起来像这样,但它仍然很冗长:

enum BinaryOperator implements Map2<Double,Double,Double> {
    PLUS((a, b) -> a + b), MINUS((a, b) -> a - b), MUL((a, b) -> a * b), DIV((a, b) -> a / b);

    private final BiFunction<Double, Double, Double> f;

    private BinaryOperator(BiFunction<Double, Double, Double> f) {
        this.f = f;
    }

    @Override
    public Double map(Double a, Double b) {
        return f.apply(a, b);
    }
}

【问题讨论】:

  • 我不认为你能比你目前的解决方案更好。枚举常量之间的差异只能通过两种方式表达:实现方法或传递构造函数参数。第一个是我们想要离开的那个,所以构造函数参数是我们唯一的选择。构造函数参数必须放在一个字段中。枚举还必须实现map(),我们已经找到了您的解决方案。
  • 我看到八行常量样板代码和每个常量的代码量一样短,例如PLUS((a, b) -&gt; a + b),。你想简洁多少?相关:“Lambdas in the classical Operation enum example”

标签: java lambda enums java-8 enumeration


【解决方案1】:

这里的适当接口是DoubleBinaryOperator。当您需要对两个 double 值进行操作并返回一个 double 时,此接口适用。它适用于原始 double 而不是包装类,因此它没有装箱开销。

您当前的解决方案仍然是可行的方法。代码已明确分解,没有重复。通过上面的界面,你可以拥有:

enum BinaryOperator implements DoubleBinaryOperator {
    PLUS((a, b) -> a + b),
    MINUS((a, b) -> a - b),
    MUL((a, b) -> a * b),
    DIV((a, b) -> a / b);

    private final DoubleBinaryOperator f;

    private BinaryOperator(DoubleBinaryOperator f) {
        this.f = f;
    }

    @Override
    public double applyAsDouble(double a, double b) {
        return f.applyAsDouble(a, b);
    }

}

实现DoubleBinaryOperator 接口可以证明是有用的:这样您可以直接将枚举值用作DoubleBinaryOperator,例如:

DoubleStream.of(1, 2, 3).reduce(0, BinaryOperator.PLUS);

(这只是一个例子;有更好的方法来做这个特定的例子。)

【讨论】:

    【解决方案2】:

    恕我直言,更简洁的是使用 EnumMap 将每个 Operation 值映射到其相应的操作:

    enum Operator { PLUS, MINUS, MUL, DIV; }
    

    然后,你需要初始化EnumMap:

    EnumMap<Operator, DoubleBinaryOperator> operations = new EnumMap<>(Operator.class);
    operations.put(Operator.PLUS, (a, b) -> a + b);
    operations.put(Operator.MINUS, (a, b) -> a - b);
    operations.put(Operator.MUL, (a, b) -> a * b);
    operations.put(Operator.DIV, (a, b) -> a / b);
    

    使用示例:

    double result = operations.get(Operator.MUL).applyAsDouble(2.0, 3.0); // 6.0
    

    编辑:正如@Holger 在 cmets 中所建议的那样,最好检查一下您是否已将所有可能的枚举值添加到地图中。这可以通过assert 来完成:

    assert operations.keySet().equals(EnumSet.allOf(Operator.class));
    

    【讨论】:

    • 问题是,如果您忘记将特定键放入地图中,您不会注意到。好的,在初始化后添加assert operations.keySet().equals(EnumSet.allOf(Operator.class));之类的语句就可以解决这个问题了。
    • @Holger 我会将您的建议添加到我的回答中。谢谢!
    猜你喜欢
    • 1970-01-01
    • 2018-06-25
    • 2017-06-23
    • 2019-10-03
    • 2018-06-30
    • 1970-01-01
    • 1970-01-01
    • 2010-12-18
    • 1970-01-01
    相关资源
    最近更新 更多