LateInitCell starts with null inside, but insists that it doesn’t contain a null value! It will throw an exception if you try to get the contents and they are null.

The next cells are all built off the LateInitCell back, so they all share the following additional utility methods:

LateInitCell<Distance> late = new LateInitCell<>();
// attempting to get the data out now would throw an error
late.accept(Distances.mm(10));
late.getInitialised(); // true
// late.accept(null); this doesn't work
late.invalidate(); // now the cell is back to containing null
late.getInitialised(); // false
late.safeGet(); // will not throw an error if the contents are null
late.safeInvoke(ref -> { // only runs if the contents aren't null
   return ref.plus(Distances.mm(12));
}); // null

LazyCell

LazyCell will lazily evaluate its contents from a function whenever you attempt to obtain the contents and they are null.

Combine this with invalidate() in order to re-evaluate the contents using the function.

Several modules have their own wrapper of this, e.g. Core has the OpModeLazyCell and OpModeFreshLazyCell and Mercurial has the SubsytemObjectCell. These wrappers provide additional functionality tied to their specific domains.

// counter must be a field, not a local variable
int counter = 0;
LazyCell<Distance> lazy = new LazyCell<>(() -> {
	return Distances.mm(++counter);
});

lazy.get(); // now counter is 1, this is equivalent in mm
lazy.get(); // still 1
lazy.invalidate();
lazy.get(); // now counter is 2
lazy.get(); // still 2

InvalidatingCell

Invalidating Cell is like lazy cell, but has a built in self invalidation function as well.

InvalidatingCell<Distance> invalidating = new InvalidatingCell<>(() -> {
	return Distances.mm(++counter);
}, (self, contents) -> {
	return contents.lessThan(Distances.mm(10)) || self.getTimeSincelastSet() > 1;
});

Whenever the contents of the cell are accessed, the invalidating function is checked, to see if the cell should be re-evaluated.

The cell contents should not be accessed through the self parameter, as this will cause stack overflow, this is what the contents parameter is for.

StaleAccessCell

A time-based wrapper for InvalidatingCell.

StaleAccessCell<Distance> staleAccess = new StaleAccessCell<>(5.1, () -> {
	return Distances.mm(++counter);
});

This example will invalidate when it has been 5.1 seconds since last access.

StaleEvalCell

A time-based wrapper for InvalidatingCell

StaleEvalCell<Distance> staleEval = new StaleEvalCell<>(5.1, () -> {
	return Distances.mm(++counter);
});

This example will invalidate when it has been 5.1 seconds since last eval.