Functional Programming in Java, Second Edition: "FinanceData" on page 157: do not use "double brace" trick

On page 157, the “double-brace” syntactic trick is used to initialize FinanceData (applying/fpij/FinanceData.java)

public class FinanceData2 {
    public static BigDecimal getPrice(final String ticker) {
        Map<String, String> fakePrices = new HashMap<>() {  <--- "double brace" start
            {
                put("AMD", "81"); put("HPQ", "33"); put("IBM", "135");
                put("TXN", "150"); put("VMW", "116"); put("XRX", "15");
                put("AAPL", "131"); put("ADBE", "360"); put("AMZN", "106");
                put("CRAY", "130"); put("CSCO", "43"); put("SNE", "72");
                put("GOOG", "2157"); put("INTC", "36"); put("INTU", "369");
                put("MSFT", "247"); put("ORCL", "67"); put("TIBX", "24");
                put("VRSN", "157"); put("RIVN", "26");
            }
        }; <---- "double brace" end
        try { Thread.sleep(200); } catch(Exception ex) {} //simulate a call delay
        return new BigDecimal(fakePrices.get(ticker));
    }
}

This is the construction of an anonymous subclass (in this case of HashMap) with its own initializer.

However, this “trick”, meant to pretend that Java has Map initializers, is confusing (I had to look it up) and suspect (it creates a reference from the inner anymous class to the outer class for one, leading to garbage collection problems).

See also Lukas Eder’s response here:

Every time someone uses double brace initialisation, a kitten gets killed.

Suggesting a standard initialization (FinanceData below also sports several additional modifications in getPrice() to make it more interesting):

// The original "applying/fpij/FinanceData.java" on page 157
// But:
// 1) Without double-brace initialization
//    See https://stackoverflow.com/questions/1958636/what-is-double-brace-initialization-in-java
// 2) More fun with Random numbers to mock up network fetches
//    with random delays and random failures.
// 3) getPrice() returns Optional<BigDecimal> instead of BigDecimal as
//    a mocked network fetch may now fail.
// 4) Code that does not swallow the InterruptedException (see comments below).

class FinanceData {

    final static Map<String, String> fakePrices;

    static {
        Map<String, String> map = new HashMap<>();
        map.put("AMD", "81");
        map.put("HPQ", "33");
        map.put("IBM", "135");
        map.put("TXN", "150");
        map.put("VMW", "116");
        map.put("XRX", "15");
        map.put("AAPL", "131");
        map.put("ADBE", "360");
        map.put("AMZN", "106");
        map.put("CRAY", "130");
        map.put("CSCO", "43");
        map.put("SNE", "72");
        map.put("GOOG", "2157");
        map.put("INTC", "36");
        map.put("INTU", "369");
        map.put("MSFT", "247");
        map.put("ORCL", "67");
        map.put("TIBX", "24");
        map.put("VRSN", "157");
        map.put("RIVN", "26");
        fakePrices = Collections.unmodifiableMap(map);
    }

    // Let's have fun with random numbers. Random is threadsafe, so no need to synchronize.

    private final static Random rand = new Random();

    public static Optional<BigDecimal> getPrice(final String ticker) {
        try {
            // simulate a normally-distributed delay (N.B. 0 is accepted)
            long delay_ms = (long) (Math.abs(rand.nextGaussian() * 200));
            Thread.sleep(delay_ms);
            // In 25% of the cases we also fail
            if (rand.nextFloat() > 0.75) {
                return Optional.empty();
            } else {
                // if "ticker" is unknown, this will throw
                return Optional.of(new BigDecimal(fakePrices.get(ticker)));
            }
        } catch (InterruptedException ex) {
            // We *should* leave the InterruptedException up the stack because someone
            // out there wants us to stop processing.
            // https://stackoverflow.com/questions/3976344/handling-interruptedexception-in-java
            // But we can't because Java the language does not allow "checked exceptions" 
            // to be thrown from inside streams!
            // So just set the "interrupted" flag of the thread again and return empty.
            Thread.currentThread().interrupt();
            return Optional.empty();
        }
    }
}