Immutable Money Class

Problem

Write an immutable Money class with a currency code and a whole amount (for example in cents). Operations never change an object; they return a new one. Commands: add C1 a1 C2 a2 and sub C1 a1 C2 a2 print C1 and the new amount (like USD 15); they print error: currency mismatch when the currencies differ, and the amount can become negative. mul C a n prints C and a * n. gt C1 a1 C2 a2 prints true if the first amount is bigger (false otherwise) and the same error line when the currencies differ.

Constraints

  • 1 <= commands <= 10^4
  • |amount| <= 10^9

Examples

Input

add USD 10 USD 5
add USD 10 EUR 5

Output

USD 15
error: currency mismatch

Solutions

Best approachTime O(n)Space O(n)

Immutable objects are safe to share and to use as map keys, and they are thread-safe without locks. The price is that every change creates a new object. Java records (Java 16) give the same effect with less code.

Same code works in Java810172125Basic features only, so nothing changes between versions.
/*
 * Problem : Process lines.
 * Input   : Several lines.
 * Output  : One result per command.
 * Approach: Immutable class.
 * Time    : O(n)
 * Space   : O(n)
 * Java    : 8 or newer.
 */
import java.util.Scanner;

public class Main {
        // Step 1: A final class with final fields cannot change after it is built. Nobody can break it by mistake.
        static final class Money {
            private final String currency;
            private final long amount;

            Money(String currency, long amount) {
                this.currency = currency;
                this.amount = amount;
            }

            // Step 2: Every operation returns a new object. The old objects stay as they are.
            Money add(Money other) {
                requireSameCurrency(other);
                return new Money(currency, amount + other.amount);
            }

            Money subtract(Money other) {
                requireSameCurrency(other);
                return new Money(currency, amount - other.amount);
            }

            Money multiply(long factor) {
                return new Money(currency, amount * factor);
            }

            boolean greaterThan(Money other) {
                requireSameCurrency(other);
                return amount > other.amount;
            }

            private void requireSameCurrency(Money other) {
                if (!currency.equals(other.currency)) {
                    throw new IllegalArgumentException("currency mismatch");
                }
            }

            @Override
            public String toString() {
                return currency + " " + amount;
            }
        }

    // This method solves the task. It receives the input and returns the answer.
    static String[] solve(String[] lines) {
        String[] output = new String[lines.length];
        for (int i = 0; i < lines.length; i++) {
            String[] part = lines[i].split(" ");
            try {
                // Step 3: A mismatch throws an exception. We catch it and print the error line.
                if (part[0].equals("mul")) {
                    output[i] = new Money(part[1], Long.parseLong(part[2])).multiply(Long.parseLong(part[3])).toString();
                    continue;
                }
                Money left = new Money(part[1], Long.parseLong(part[2]));
                Money right = new Money(part[3], Long.parseLong(part[4]));
                if (part[0].equals("add")) {
                    output[i] = left.add(right).toString();
                } else if (part[0].equals("sub")) {
                    output[i] = left.subtract(right).toString();
                } else {
                    output[i] = String.valueOf(left.greaterThan(right));
                }
            } catch (IllegalArgumentException error) {
                output[i] = "error: " + error.getMessage();
            }
        }
        return output;
    }

    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);
        java.util.List<String> all = new java.util.ArrayList<>();
        while (scanner.hasNextLine()) {
            String line = scanner.nextLine().trim();
            if (!line.isEmpty()) {
                all.add(line);
            }
        }
        String[] lines = all.toArray(new String[0]);
        System.out.println(String.join("\n", solve(lines)));
    }
}
Hints (1)
  1. Why is an immutable class safe to share between threads?