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
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.
/*
* 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)
- Why is an immutable class safe to share between threads?