- Lab
-
Libraries: If you want this lab, consider one of these libraries.
- Core Tech
Guided: Java SE Functional Interfaces and Lambda Expressions
Practice applying Java SE's built-in functional interfaces and lambda expressions to write concise, modern code. In this Guided Code Lab, you'll simplify tasks such as filtering, transforming, and processing collections using functional programming techniques.
Lab Info
Table of Contents
-
Challenge
Introduction
Welcome to this Guided Code Lab on Java Functional Interfaces and Lambda Expressions!
In this lab, you will take a standard Java application that manages a product inventory and refactor it step-by-step to use modern, functional programming techniques. This refactoring improves code clarity and maintainability.
You will work in the
ProductInventory.javafile and implement methods that filter, transform, and process a list of products using functional interfaces and lambda expressions.It's time to get started! info> This lab experience was developed by the Pluralsight team using Forge, an internally developed AI tool utilizing Gemini technology. All sections were verified by human experts for accuracy prior to publication. For issue reporting, please contact us.
-
Challenge
Filtering Collections with Predicate
Filtering Collections with
PredicateOne of the most common operations on a collection is filtering, or selecting a subset of elements that meet certain criteria.
The
java.util.function.Predicate<T>functional interface is designed for exactly this. It has a single abstract method,boolean test(T t), which returnstrueif the input argument matches the predicate, andfalseotherwise.Before lambda expressions were introduced, developers implemented this logic by using anonymous inner classes.
First, you will implement using an anonymous inner class to see how it used to work. Then, you will refactor the code to use a lambda expression and apply it with modern Java APIs such as
List.removeIf. ### Removing Elements withremoveIfThe
Listinterface includes default methods that accept functional interfaces. One of these methods isremoveIf, which removes all elements that match a givenPredicate. -
Challenge
Transforming Data with Function
Transforming Data with
FunctionAnother common task you perform in Java is transforming data from one form to another, such as extracting a specific field from an object. The
java.util.function.Function<T, R>interface is designed for this purpose. ItsR apply(T t)method takes a value of typeTand returns a value of typeR.In this step, you will first implement a data transformation with an anonymous class. Then, you will refactor the code to use a lambda expression and simplify it further with a method reference, which is a shorthand syntax for lambdas that call a single method. You use a
Functionto transform an input of one type into an output of another. Here, you transformProductobjects intoStringnames using an anonymous class first. -
Challenge
Consuming and Supplying Data
Using
ConsumerandSupplierIn this step, you work with two additional core functional interfaces:
ConsumerandSupplier.The
java.util.function.Consumer<T>interface represents an operation that accepts a single input argument and returns no result. It defines thevoid accept(T t)method. You use aConsumerwhen you need to perform an action on an element, like printing it, saving it to a database, or modifying one of its properties.The
java.util.function.Supplier<T>interface represents a supplier of results. It defines theT get()method, which takes no arguments and returns a value. You can use aSupplierfor lazy initialization or generating default values.In the following tasks, you will use a
Consumerto apply a discount to products and aSupplierto create a default product instance. AConsumerperforms an action on an input but doesn't return anything. It's perfect for tasks like printing, logging, or modifying an object.The
List.forEachmethod accepts aConsumer. ASuppliertakes no arguments but produces a value. It's useful for lazy generation of objects. Here, you create aSupplierthat provides a defaultProduct. -
Challenge
Chaining Operations with Streams
Building Stream Pipelines with the Stream API
The true power of functional programming in Java is realized when you start chaining these operations together using the Stream API.
A stream is a sequence of elements that supports sequential and parallel aggregate operations.
A stream pipeline consists of:
- A source, such as a
Collection - Zero or more intermediate operations, such as
filter()ormap(), which are lazy and return a new stream - A terminal operation, such as
collect()orforEach(), which triggers the computation and produces a result or side-effect
In this step, you will build your first stream pipeline to see how these concepts connect.
- A source, such as a
-
Challenge
Advanced Operations with Maps
Using Functional Interfaces with
MapFunctional programming isn't just for lists. The
Mapinterface has also been enhanced with powerful methods that leverage functional interfaces.In this step, you will explore two of them:
-
computeIfAbsent(K key, Function<K, V> mappingFunction): If the specified key is not already associated with a value, this method attempts to compute its value using the given mapping function and enters it into the map. This is useful for grouping elements from a list into a map. -
forEach(BiConsumer<K, V> action): This method performs the given action for each entry in the map. ABiConsumeris just like aConsumerbut takes two arguments (in this case, the key and the value). TheMapinterface also gained powerful functional methods.
You use
computeIfAbsentto create and store values in a map only when a key is missing. The method checks whether the specified key is already associated with a value. If it is not, the map calls the providedFunctionto compute the value, stores it, and returns the result. Finally, process the entries in your newly created map.Use
Map.forEachto process each key-value pair in a map. The method accepts aBiConsumer, which takes two arguments representing the key and the value, and applies an action to each entry in the map. ### How to RunTo compile and run the application run the following command in the Terminal:
mvn compile exec:java -Dexec.mainClass="com.pluralsight.codelab.Main" -
-
Challenge
Conclusion
Congratulations on completing the lab!
You have successfully transformed a traditional Java application into a modern, functional programming showcase.
Throughout this lab, you've learned how to:
- Use
Predicateinterfaces with lambda expressions to filter collections - Transform data using
Functioninterfaces and method references - Process elements with
Consumerand generate values withSupplier - Chain operations together using the Stream API
- Leverage functional methods on
Mapinterfaces for grouping and processing
By applying these functional programming concepts, you can write code that is more concise, readable, and expressive. These patterns are central to modern Java development and will support you in creating cleaner, more maintainable solutions as you continue building your skills.
- Use
About the author
Real skill practice before real-world application
Hands-on Labs are real environments created by industry experts to help you learn. These environments help you gain knowledge and experience, practice without compromising your system, test without risk, destroy without fear, and let you learn from your mistakes. Hands-on Labs: practice your skills before delivering in the real world.
Learn by doing
Engage hands-on with the tools and technologies you’re learning. You pick the skill, we provide the credentials and environment.
Follow your guide
All labs have detailed instructions and objectives, guiding you through the learning process and ensuring you understand every step.
Turn time into mastery
On average, you retain 75% more of your learning if you take time to practice. Hands-on labs set you up for success to make those skills stick.