Modern Java in Action
A comprehensive guide to the features of modern Java (versions 8, 9, 10, and 11), focusing on lambdas, streams, functional programming, and reactive programming techniques.
Lessons
Lesson
This lesson explores the evolution of Java in response to the shift from single-core to multicore processor architectures. Students will learn to leverage modern features like the Stream API, Lambda expressions, and the Java Platform Module System to transition from sequential processing to efficient, parallelized data execution.
This lesson explores the limitations of hardcoding business logic and introduces behavior parameterization as a strategy to reduce code duplication and technical debt. Students will learn to move beyond simple value parameterization to create more flexible, maintainable, and reusable code structures.
This lesson explores the transition from verbose anonymous classes to concise lambda expressions in Java, focusing on the functional paradigm and behavioral parameterization. Students will learn to implement lambda syntax, utilize functional interfaces, and refactor imperative code to improve readability and reduce boilerplate.
This lesson explores the evolution of Java data processing, highlighting the shift from imperative external iteration to declarative internal iteration using Streams. Students will learn how Streams improve code readability, enable efficient parallelization, and utilize lazy evaluation to optimize data computation.
This lesson explores the transition from external to internal iteration in Java, focusing on how the Stream API enables declarative data processing. You will learn to master transformation operations like filter and map while understanding how lazy evaluation and library-led optimizations improve performance and code readability.
This lesson explores mutable reduction in Java Streams, focusing on how the collect operation efficiently accumulates stream elements into mutable containers using predefined collectors. Students will learn to replace imperative loops with declarative stream pipelines to improve code maintainability, performance, and thread safety when processing large datasets.
This lesson explores the transition from sequential to parallel stream processing in Java, focusing on how internal iteration and declarative parallelism allow the JVM to distribute tasks across multi-core architectures. Students will learn to evaluate the trade-offs between parallel execution and overhead, while mastering the functional reduction process and the mechanics of the ForkJoinPool.
This lesson explores modern Java Collection API enhancements, focusing on the shift from verbose external iteration to efficient internal iteration using factory methods and Spliterators. Students will learn to leverage these tools to improve code readability, ensure data immutability, and optimize performance through parallel processing.
This lesson explores the transition from imperative, boilerplate-heavy Java to a declarative style using lambda expressions and internal domain-specific languages (DSLs). Students will learn how to refactor traditional design patterns and improve code maintainability by prioritizing domain logic over structural ceremony.
This lesson explores the design of Domain-Specific Languages (DSLs) in Java, focusing on bridging the semantic gap between technical implementation and business requirements. Students will learn to maximize code readability by utilizing lambda expressions and fluent APIs to minimize syntactic noise while enforcing domain-specific constraints.
This lesson explores the "billion-dollar mistake" of null references in Java, explaining how they subvert the type system and cause fragile, error-prone code. Students will learn to identify the risks of null-based logic and prepare to replace defensive null-checks with the more robust Optional API.
This lesson explores the evolution of Java’s date and time handling, contrasting the flawed, mutable legacy classes with the robust, thread-safe, and immutable API introduced in Java 8. Students will learn how to utilize modern classes like LocalDate and ZonedDateTime while understanding how interface evolution through default and static methods ensures backward compatibility.
This lesson explores the evolution of Java interfaces, focusing on how default methods solve the challenges of maintaining binary compatibility in legacy systems. Students will learn how to implement default and static methods to safely update interfaces without breaking existing implementations or causing runtime errors.
This lesson introduces the Java Module System (Project Jigsaw), which replaces the monolithic JDK structure with a modular architecture to improve security, encapsulation, and application footprint. Students will learn how to configure module-info.java descriptors and use the jlink tool to create optimized, custom runtime images.
This lesson explores the transition from sequential to asynchronous programming in modern application architectures, focusing on the shift toward non-blocking service orchestration. It also covers Java 9 modularization, detailing how the module system improves dependency management and encapsulation through explicit declarations.
This lesson explores the limitations of synchronous, blocking code and introduces asynchronous programming as a solution for building responsive, resilient, and elastic systems. Students will learn to implement non-blocking execution in Java using CompletableFuture to optimize performance by running independent tasks in parallel.
This lesson explores the shift from imperative, blocking I/O models to a declarative, push-based reactive paradigm designed to handle high-concurrency data streams. Students will learn to implement custom Publishers and Subscribers using the Flow API while mastering techniques to manage backpressure and avoid thread exhaustion.
This lesson explores the risks of shared mutable data, explaining how it leads to unpredictable "spooky action at a distance," architectural erosion, and concurrency issues. Students will learn to mitigate these problems by adopting functional programming principles, such as immutability and pure functions, to build more robust and thread-safe applications.
This lesson explores the transition from imperative to functional programming in Java, focusing on the core concept of referential transparency. Students will learn how to design deterministic, side-effect-free functions to improve code maintainability, thread safety, and performance through techniques like memoization.
This lesson explores how Scala reduces Java's boilerplate through a blend of object-oriented and functional programming features, such as concise class definitions, singletons, and string interpolation. Students will learn how these declarative syntax improvements and the emphasis on immutability better support modern multicore development while maintaining full interoperability with the Java ecosystem.
This lesson explores the evolution of Java, focusing on how functional programming, the Stream API, and the Java Module System (JPMS) enable modern, scalable, and reactive application design. Students will learn to leverage these features to improve performance, enhance modularity, and effectively manage data streams in cloud-native environments.
Course Overview
📚 Content Summary
A comprehensive guide to the features of modern Java (versions 8, 9, 10, and 11), focusing on lambdas, streams, functional programming, and reactive programming techniques.
Master the art of modern Java with lambdas, streams, and functional-style programming.
Author: Raoul-Gabriel Urma, Mario Fusco, Alan Mycroft
Acknowledgments: Supported by the Manning Early Access Program (MEAP) readers, development editors Kevin Harreld and Dennis Sellinger, and various technical reviewers including Jason Lee and William Wheeler.
🎯 Learning Objectives
- Analyze Java’s evolution within the programming ecosystem and its adaptation to multicore hardware.
- Implement behavior parameterization using method references and lambdas to pass code as a "first-class citizen."
- Distinguish between internal and external iteration and explain how the Streams API facilitates parallelism.
- Identify how behavior parameterization solves the problem of "coping with changing requirements" in software evolution.
- Implement the Strategy design pattern using Predicates to filter data by abstract criteria.
- Reduce code verbosity by transitioning from named classes to anonymous classes and finally to lambda expressions.
- Identify and write valid lambda expressions using both expression and block syntax styles.
- Implement the execute-around pattern to improve code reusability in resource-intensive tasks.
- Utilize standard functional interfaces and their primitive specializations to optimize performance.
- Define a Java Stream and identify its primary characteristics (Declarative, Composable, Parallelizable).
Lessons
Overview: Modern Java (versions 8 through 11) marks a significant evolution in the language's history, shifting toward a functional programming style to better utilize multicore processors. This lesson covers the three pillars of this change: the Streams API for declarative data processing, behavior parameterization (passing code to methods), and structural improvements like default methods and modules that enable the ecosystem to evolve without breaking existing codebases.
Learning Outcomes:
- Analyze Java’s evolution within the programming ecosystem and its adaptation to multicore hardware.
- Implement behavior parameterization using method references and lambdas to pass code as a "first-class citizen."
- Distinguish between internal and external iteration and explain how the Streams API facilitates parallelism.
Overview: This lesson explores behavior parameterization, a software development pattern that allows a method to receive multiple different behaviors as parameters. By progressing through the "Farmer Inventory Problem," students will learn how to cope with changing requirements by evolving code from rigid, hardcoded methods to flexible, abstract, and concise implementations using anonymous classes and lambda expressions.
Learning Outcomes:
- Identify how behavior parameterization solves the problem of "coping with changing requirements" in software evolution.
- Implement the Strategy design pattern using Predicates to filter data by abstract criteria.
- Reduce code verbosity by transitioning from named classes to anonymous classes and finally to lambda expressions.
Overview: This lesson covers the transition from boilerplate-heavy anonymous classes to concise Lambda expressions in Java. Students will explore the structural components of lambdas, the critical role of functional interfaces and their descriptors, and how to apply the "execute-around" pattern for cleaner resource management. The lesson concludes with advanced techniques including method references and the composition of functions, predicates, and comparators to build complex logic from simple building blocks.
Learning Outcomes:
- Identify and write valid lambda expressions using both expression and block syntax styles.
- Implement the execute-around pattern to improve code reusability in resource-intensive tasks.
- Utilize standard functional interfaces and their primitive specializations to optimize performance.
Overview: This lesson introduces Java Streams as a sequence of elements from a source that supports data-processing operations. Students will explore how streams differ from collections—moving from "eager" storage to "on-demand" computation—and learn the fundamental shift from external iteration (manual) to internal iteration (library-managed).
Learning Outcomes:
- Define a Java Stream and identify its primary characteristics (Declarative, Composable, Parallelizable).
- Differentiate between collections and streams using the DVD vs. Internet Streaming metaphor.
- Explain the "traversable only once" rule and identify the
IllegalStateExceptionwhen violated.
Overview: This lesson explores advanced data processing patterns using the Java Stream API, moving beyond basic iteration to complex functional transformations. It covers sophisticated filtering and slicing techniques, mapping strategies to handle nested data, specialized numeric streams for performance optimization, and various methods for building streams from external sources including files and infinite generators.
Learning Outcomes:
- Apply advanced filtering and slicing operations (distinct, limit, skip, takeWhile, dropWhile) to precisely control stream data flow.
- Transform and flatten complex data structures using
mapandflatMap. - Utilize primitive stream specializations (
IntStream, etc.) andOptionalprimitives to optimize performance and handle potential null values safely.
Overview: This lesson explores the collect terminal operation as an advanced reduction mechanism in Java Streams. It transitions from basic predefined collectors for summarization and string joining to complex data structures through multilevel grouping and partitioning. Finally, it provides a deep dive into the Collector interface, empowering developers to build custom, high-performance collectors for specialized data processing tasks.
Learning Outcomes:
- Implement predefined collectors to perform summarization (max, min, sum, average) and string concatenation.
- Construct complex, nested data structures using multilevel grouping and partitioning techniques.
- Deconstruct and implement the
Collectorinterface methods (supplier,accumulator,finisher,combiner) to create custom collection logic.
Overview: This lesson covers the transition from sequential to parallel data processing in Java. It explores how to leverage multi-core architectures using parallel streams, the underlying Fork/Join framework, and the Spliterator interface. Learners will gain the ability to measure performance accurately and identify when parallelization is beneficial versus when it introduces overhead or correctness issues.
Learning Outcomes:
- Convert sequential streams to parallel and understand the global impact of pipeline configuration.
- Identify and avoid common pitfalls of parallel processing, such as shared mutable state and inefficient data structures.
- Implement custom parallel logic using the
RecursiveTaskclass and the work-stealing algorithm.
Overview: This lesson covers the modernizing features introduced in the Java Collection API (Java 8 and 9). You will learn how to use factory methods for creating concise, immutable collections and explore new idiomatic methods for modifying Lists and Sets and managing complex Map operations like computing and merging values.
Learning Outcomes:
- Create immutable Lists, Sets, and Maps using concise factory methods.
- Safely modify collections using
removeIfandreplaceAllwithoutConcurrentModificationException. - Optimize Map interactions using
getOrDefault,computeIfAbsent, and advanced merging patterns.
Overview: This lesson focuses on modernizing Java applications by refactoring legacy code into a functional style using Lambda expressions and Streams. It explores how to simplify common object-oriented design patterns, improve code flexibility through deferred execution, and implement robust testing and debugging strategies specifically tailored for functional programming constructs in Java.
Learning Outcomes:
- Transform imperative data processing and anonymous classes into readable Streams and method references.
- Apply functional interfaces to refactor classic design patterns (Strategy, Observer, etc.), reducing boilerplate code.
- Develop unit tests for high-order functions and complex lambda behaviors.
Overview: This lesson explores the design and implementation of Domain-Specific Languages (DSLs) within the Java ecosystem, focusing on how modern Java features like lambdas and method references reduce boilerplate. Students will learn to distinguish between internal and external DSLs and master patterns such as method chaining, nested functions, and lambda sequencing to create readable, maintainable domain models. The lesson also examines how these principles are applied in industry-standard tools like jOOQ, Cucumber, and Spring Integration.
Learning Outcomes:
- Distinguish between Internal and External DSLs, evaluating their respective pros and cons.
- Implement various DSL patterns in Java, including method chaining, nested functions, and lambda-based builders.
- Analyze the Stream API and Collectors as functional internal DSLs for data manipulation.
Overview: This lesson transitions developers from traditional defensive null-checking—often cited as the "billion-dollar mistake"—to a more expressive and robust functional approach using java.util.Optional. It covers the lifecycle of an Optional object, from creation and transformation via map/flatMap to safe unwrapping and filtering. By the end of this module, learners will be able to model the absence of values explicitly, reducing the risk of NullPointerException (NPE) and improving code readability.
Learning Outcomes:
- Identify the architectural weaknesses of null references and the "deep doubts" pattern of defensive checking.
- Construct
Optionalobjects using appropriate factory methods based on value certainty. - Implement fluent data pipelines using
map,flatMap, andfilterto replace nestedif-nullblocks.
Overview: This lesson covers the modern Java Date and Time API introduced to replace the legacy java.util.Date and Calendar classes. It focuses on immutable, thread-safe classes for representing dates and times (both human-readable and machine-readable), calculating time intervals, and handling the complexities of time zones and alternative calendar systems.
Learning Outcomes:
- Create and manipulate human-readable dates (
LocalDate,LocalTime,LocalDateTime) and machine-readable timestamps (Instant). - Differentiate between and implement
Duration(time-based) andPeriod(date-based) to model intervals. - Apply
TemporalAdjustersfor complex date manipulation andDateTimeFormatterfor customized parsing and printing.
Overview: This lesson explores how Java 8 revolutionized interface design by allowing implementation code within interfaces via default and static methods. It focuses on the mechanisms for evolving APIs without breaking existing client code and provides a rigorous framework—the Three Resolution Rules—for handling the complexities of multiple inheritance of behavior and the "diamond problem."
Learning Outcomes:
- Explain how default methods enable backward-compatible API evolution.
- Apply the three resolution rules to determine which method implementation is selected in complex inheritance hierarchies.
- Implement explicit disambiguation when multiple interfaces provide conflicting default methods.
Overview: This lesson covers the transition from the traditional Java classpath to the Java Module System (Project Jigsaw). It explores the architectural principles of modularity—specifically separation of concerns and information hiding—while addressing the historical limitations of "JAR Hell." Learners will master the syntax of module-info.java, including dependency management and access control, and understand how to compile, package, and integrate legacy code via automatic modules.
Learning Outcomes:
- Analyze the limitations of the traditional classpath and the benefits of modularity (SoC and information hiding).
- Synthesize module descriptors (
module-info.java) usingrequires,exports,transitive, and qualified exports. - Differentiate between the Java Module System (Jigsaw) and OSGi frameworks.
Overview: This lesson explores the evolution of Java concurrency, moving from manual thread management to higher-level abstractions like the box-and-channel model and reactive programming. It focuses on maximizing hardware utilization by distinguishing between concurrency and parallelism, avoiding the pitfalls of blocking operations (like Thread.sleep), and implementing data-driven flows using Future-style and Reactive-style APIs.
Learning Outcomes:
- Distinguish between concurrency as a programming property and parallelism as a hardware execution property.
- Compare and contrast Future-style APIs and Reactive-style (callback) APIs for asynchronous task management.
- Design non-blocking systems using the box-and-channel model and the Publish-Subscribe protocol.
Overview: This lesson guides Java developers through the transition from traditional synchronous programming to modern asynchronous patterns using CompletableFuture. It covers the creation of nonblocking APIs, error propagation, the orchestration of complex task pipelines, and the optimization of resource utilization through custom executors and timeout management.
Learning Outcomes:
- Differentiate between synchronous and asynchronous APIs and convert blocking methods into nonblocking ones.
- Implement robust error handling and timeout strategies within asynchronous pipelines.
- Optimize application throughput by configuring custom Executors based on the thread-pool sizing formula.
Overview: This lesson explores the shift from traditional blocking programming to the reactive paradigm, beginning with the foundational principles of the Reactive Manifesto. Students will learn to implement asynchronous, non-blocking data streams using the Java Flow API (Publisher, Subscriber, Subscription, and Processor) while managing data flow via backpressure. Finally, the lesson introduces RxJava to demonstrate the practical creation, transformation, and combination of Observables in modern Java applications.
Learning Outcomes:
- Define the four pillars of the Reactive Manifesto and distinguish between reactive systems and reactive application-level programming.
- Implement the Java Flow API interfaces to create a functional publish-subscribe relationship with backpressure support.
- Utilize RxJava to create Observables and apply transformation and combination operators to manage asynchronous event streams.
Overview: This lesson explores the transition from traditional imperative programming to a functional approach in Java. It addresses the challenges of shared mutable data and contrasts the "how-to-solve" (imperative) versus "what-to-solve" (declarative) mindsets. Students will learn the formal definition of functional programming, the importance of referential transparency, and how to optimize recursive logic using tail-call optimization.
Learning Outcomes:
- Identify the risks associated with shared mutable data in complex systems.
- Distinguish between imperative and declarative programming styles using Java examples.
- Apply the principles of referential transparency and functional purity to method design.
Overview: This lesson covers advanced functional programming paradigms designed to modernize Java development. It explores how treating functions as first-class citizens through higher-order functions and currying enables modularity, while persistent data structures and functional updates ensure referential transparency. Finally, it introduces efficiency and expressiveness through lazy evaluation, combinators, and pattern matching.
Learning Outcomes:
- Identify and implement higher-order functions that take or return other functions.
- Apply currying to transform multi-argument functions into sequences of unary functions for partial application.
- Implement persistent data structures that use functional updates to share state without mutation.
Overview: This lesson explores the transition from Java to Scala, highlighting how Scala serves as a powerful hybrid of Object-Oriented and Functional Programming. Students will learn the syntax and architectural differences in collection handling, the flexibility of first-class functions and closures, and the advanced modularity offered by Scala traits compared to Java interfaces.
Learning Outcomes:
- Contrast Scala’s immutable collection types (List, Set, Map, Tuple) with Java’s collection framework.
- Implement first-class functions, closures, and curried functions using Scala’s concise syntax.
- Evaluate the advantages of Scala traits over Java interfaces for behavior parameterization and state management.
Overview: This lesson explores the evolution of Java from the foundational shifts in Java 8 and 9 to the modern enhancements of Java 10 and beyond. It focuses on how the language is addressing modern hardware constraints and developer productivity through features like local variable type inference, value types (Project Valhalla), and a modernized, faster release cadence.
Learning Outcomes:
- Summarize the core functional and modular advancements introduced in Java 8 and 9.
- Apply Java 10 local variable type inference (
var) to reduce boilerplate code. - Explain the conceptual benefits of declaration-site variance, pattern matching, and reified generics in future Java versions.