
class Lazy<T> implements Supplier<T> {
private final Supplier<? extends T> supplier;
private Optional<T> cache; // cannot be final
private Lazy(Supplier<? extends T> supplier) {
this.supplier = supplier;
this.cache = Optional.<T>empty();
}
public T get() {
return this.cache.orElseGet(() -> caching());
}
// attempt 1
private T caching() {
this.cache = this.supplier.get();
}
// attempt 2
private T caching() {
T v = this.supplier.get();
// calls foo() and prints
// foo() returns 1 so now v = 1
this.cache = Optional.<T>of(v); // stores 1 in cache
return v; // returns the same 1 in v
}

Lazy is observably immutable The internal state does change (the cache field goes from Optional.empty() to Optional.of(v)) BUT from the caller’s perspective, the behaviour is always the same — get() always returns the same value.
Strictly/technically immutable means: All fields are final No setters or methods that modify fields

When I call map, we just create Lazy<R>(() → mapper.apply(this.get())) We are just adding a function declaration, that isn’t called yet. “this.get()” isn’t called yet because the lambda isn’t activated.
How it works
- lazyint.map(x → x + 1) → creates new Lazy wrapping the lambda, nothing runs
- .get() is called → now the lambda runs → calls this.get() on lazyint → calls foo() → returns 1 → mapper applies x + 1 → returns 2
- lazyint has 1 cached. So this.get() returns the cached value!
An eager implementation
<R> Lazy<R> map(Function<? super T, ? extends R> mapper) {
R r = mapper.apply(this.get());
return new Lazy<R>(() -> r);
}

lazy_add
Lazy<Integer> lazy_add(Lazy<Integer> x, Lazy<Integer> y) {
return x.flatMap(a -> y.flatMap(b -> Lazy.of(() -> foo2(a + b))));
}
or
Lazy<Integer> lazy_add2(Lazy<Integer> x, Lazy<Integer> y) {
return x.flatMap(a -> y.map(b -> foo2(a + b)));
}
jshell> r2.get()
foo2 method evaluated: 1
foo2 method evaluated: 2
foo2 method evaluated: 3
$.. ==> 3
x, y, and the addition are all unevaluated until .get() is called When .get() finally runs, it evaluates in order: foo2(1) — evaluates x foo2(2) — evaluates y foo2(3) — evaluates the addition
norm_add
Integer norm_add(Integer x, Integer y) {
return foo2(x + y);
}
jshell> norm_add(foo2(10), foo2(20))
foo2 method evaluated: 10
foo2 method evaluated: 20
foo2 method evaluated: 30
$.. ==> 30
eagerly evaluates both arguments before even entering the function body.
The big idea
flatMap lets you chain lazy computations together while keeping everything deferred until .get()