Explain Codes LogoExplain Codes Logo

Java 8 method references: provide a Supplier capable of supplying a parameterized result

java
lambda
supplier
java-8
Nikita BarsukovbyNikita Barsukov·Nov 6, 2024
TLDR

Craft a Supplier for a parameterized result using a lambda that invokes the constructor:

Supplier<Foo> fooSupplier = () -> new Foo("fixed parameter"); Foo fooInstance = fooSupplier.get(); // Voila! A new Foo with "fixed parameter".

Keep in mind: The lambda will cleverly capture the parameter required by the Foo constructor.

Employing method references

For a no-arg constructor, simply use a method reference directly:

Supplier<Foo> fooSupplier = Foo::new; // Isn't this fun? Foo fooInstance = fooSupplier.get();

In case parameters are on the radar, make way for a lambda, as method references don't play well with parameters.

Building exceptions with parameters

To supply a custom exception with a constructor parameter, call upon a lambda expression:

Supplier<CustomException> exceptionSupplier = () -> new CustomException("Error message"); // Sad trombone sound

Afterwards, integrate it with orElseThrow():

Foo foo = someOptional.orElseThrow(exceptionSupplier);

Aid from helper methods

Enlist the help of a helper method or a utility class to craft a Supplier for exceptions. You get the bonus of centralized logic and reused code:

public class ExceptionSupplier { public static Supplier<CustomException> create(String message) { return () -> new CustomException(message); // Factory of sorrows } } // Usage Foo foo = someOptional.orElseThrow(ExceptionSupplier.create("Error message")); // Magic!

Fast-and-furious handling

While RuntimeException is indeed tempting, an upgrade to the freshest JDK might reveal newer, speedier ways to deal with exceptions.

Confirming proper instantiation

Ensure your lambda is not cooking up something nasty and indeed instantiates the Exception with the intended argument, especially when juggling with constructor overloads.

Keep verifying

Never quit testing your functionality against the latest JDK (it is worthy!) for optimum reliability and efficiency.

Advanced use: Factory patterns

School Suppliers in the factory pattern to create objects in tune with dynamic parameters:

Map<String, Supplier<Animal>> animalFactory = new HashMap<>(); animalFactory.put("dog", Dog::new); // Woof Woof animalFactory.put("cat", Cat::new); // Meow Supplier<Animal> animalSupplier = animalFactory.get(animalType); Animal pet = animalSupplier.get(); // Congrats, it's a ...!

Each Supplier in the factory can be tailor-made with the required construction operations.

Generics compatibility

Suppliers can play well with a generic type. Use a bounded wildcard can create more flexible APIs:

public <T extends Number> T createNumber(Supplier<? extends T> sup) { return sup.get(); // Didn't see that coming, did ya? }

This helps you encase the instantiation of T, which can then morph into any subclass of Number.

Potential pitfalls

Beware of sneaky closure state issues when the lambda captures mutable variables that may change like chameleons. Also, the type inference can sometimes be as clear as mud; you may need to explicitly specify the type to help out the compiler along the way.

Modernize or bust!

If you're not shackled by Java 8, check out var in Java 10 for local variable type inference, and API improvements in later Java versions to make your Suppliers more concise and efficient.