Skip to content

Replace class methods with lambda expressions, is it bad practice?

I had a few questions that I’m having trouble finding the answer to, at least in Java.

I saw a Class definition in a tutorial where lambdas were being used similar to methods. So I’m curious as to if there is any benefit apart from concise code and style preference.

Example:

public class Blocks {
    private Deque<Block> entries;

    public Blocks() {
        this.entries = new ArrayDeque<>();
    }

    public Deque<Block> getEntries() {
        return entries;
    }

    public void setEntries(Deque<Block> entries) {
        this.entries = new ArrayDeque<>(entries);
    }

    public Predicate<Block> push = entries::offerLast;

    public Supplier<Optional<Block>> peek = () -> Optional.ofNullable(entries.peekLast());

    public BooleanSupplier isEmpty = entries::isEmpty;

    public Supplier<String> lastHash = () -> peek.get().map(Block::hash).orElse("0");

    public LongSupplier size = entries::size;
}

Is this a valid class definition? Is this a bad practice? If so, why?

One thing I noticed is that in the Predicate push, I get the following warning:

Dereference of 'entries' will produce 'NullPointerException'

Why am I getting this warning if entries should be instantiated? The warning seems to affect any function with a method reference. If instead:

public Predicate<Block> push = block -> entries.offerLast(block);

then the warning disappears.

Answer

The field initialisers of a class run in textual order, before the constructor. Therefore, the fields push, isEmpty etc are going to be initialised before entries gets initialised in the constructor.

When a method reference expression such as entries::offerLast is evaluated, entries is evaluated first to determine on which object should the method be called. However, since entries is not initialised at the time when the method reference expression is evaluated, entries evaluates to null. This causes a NullPointerException to be thrown. JLS 15.13.3

First, if the method reference expression begins with an ExpressionName or a Primary, this subexpression is evaluated. If the subexpression evaluates to null, a NullPointerException is raised, and the method reference expression completes abruptly.

To fix this, you can either move the initialisation of the entires field out of the constructor, and inline with its declaration:

private Deque<Block> entries = new ArrayDeque<>();

Or, you can use a lambda expression instead of a method reference:

public Predicate<Block> push = e -> entries.offerLast(e);