Coming across an pre-Java 8 example from jparsec, I wondered if I could leverage lambdas for a more expressive syntax.
Original example:
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;
}
}
}
where
public interface Map2<A, B, T> {
/** Maps {@code a} and {@code b} to the target object. */
T map(A a, B b);
}
A plain Map2 instance is easily created with e.g. (a,b)->a+b
, but is there a concise way to do the same for enums implementing a single function interface?
My current solution looks like this, but it is still verbose:
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);
}
}
The appropriate interface would be DoubleBinaryOperator
here. This interface is suited when you need to operate on two double
values and return a double
. It works on primitive double
instead of the wrapper class, thereby it doesn't have boxing overhead.
Your current solution is still the way to go. The code is clearly factored and there's no duplication. With the above interface, you can have:
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);
}
}
Implementing the DoubleBinaryOperator
interface can prove to be useful: this way you can use the enum values directly as DoubleBinaryOperator
, like:
DoubleStream.of(1, 2, 3).reduce(0, BinaryOperator.PLUS);
(This is just an example; there are better ways to do this specific instance.)
IMHO, much more concise would be to use an EnumMap
that maps every single Operation
value to its corresponding operation:
enum Operator { PLUS, MINUS, MUL, DIV; }
Then, you would need to initialize the 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);
Usage example:
double result = operations.get(Operator.MUL).applyAsDouble(2.0, 3.0); // 6.0
EDIT: As @Holger suggests in the comments, it would be nice to check that you've added all possible enum values to the map. That could be accomplished with an assert
:
assert operations.keySet().equals(EnumSet.allOf(Operator.class));
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With