Inspired by some older questions, I decided to create my own postfix calculator using Java 8. I'd like to have all aspects reviewed.
public enum Operator implements DoubleBinaryOperator {
PLUS ("+", (l, r) -> l + r),
MINUS ("-", (l, r) -> l - r),
MULTIPLY("*", (l, r) -> l * r),
DIVIDE ("/", (l, r) -> l / r);
private final String symbol;
private final DoubleBinaryOperator binaryOperator;
private Operator(final String symbol, final DoubleBinaryOperator binaryOperator) {
this.symbol = symbol;
this.binaryOperator = binaryOperator;
}
public String getSymbol() {
return symbol;
}
@Override
public double applyAsDouble(final double left, final double right) {
return binaryOperator.applyAsDouble(left, right);
}
}
public class CalculationFailedException extends RuntimeException {
private static final long serialVersionUID = 6849565649585489467L;
public CalculationFailedException() {
super();
}
public CalculationFailedException(final String message) {
super(message);
}
public CalculationFailedException(final Throwable cause) {
super(cause);
}
public CalculationFailedException(final String message, final Throwable cause) {
super(message, cause);
}
}
public interface Calculator {
public double calculate(final String input);
}
public class PostfixCalculator implements Calculator {
private static final List<String> OPERATORS_LIST = Arrays.stream(Operator.values())
.map(Operator::getSymbol)
.collect(Collectors.toList());
private static final Map<String, Operator> STRING_TO_OPERATOR_MAPPING = Arrays.stream(Operator.values())
.collect(Collectors.toMap(op -> op.getSymbol(), op -> op));
private final Stack<Double> numberStack = new Stack<>();
@Override
public double calculate(final String input) throws IllegalArgumentException {
try {
Arrays.stream(input.split(" ")).forEach(this::processElement);
return safePop();
} catch (CalculationFailedException | NumberFormatException ex) {
throw new CalculationFailedException("Calculation has failed for expression: " + input);
}
}
private void processElement(final String element) {
Objects.requireNonNull(element);
if (OPERATORS_LIST.contains(element)) {
processOperator(STRING_TO_OPERATOR_MAPPING.get(element));
}
else {
processNumber(Double.parseDouble(element));
}
}
private void processOperator(final Operator operator) {
double right = safePop();
double left = safePop();
numberStack.push(operator.applyAsDouble(left, right));
}
private void processNumber(final double number) {
numberStack.push(number);
}
private double safePop() {
if (numberStack.isEmpty()) {
throw new CalculationFailedException();
}
return numberStack.pop();
}
}
Code can be called for example with:
Calculator calculator = new PostfixCalculator();
double result = calculator.calculate("3 4 +");
Which will give 7.0
as result.
Another example:
Calculator calculator = new PostfixCalculator();
double result = calculator.calculate("5 1 2 + 4 * + 3 -");
Which gives 14.0
as result.