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();
}
}
}