Voltar aos Cursos
AI033 Professional

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.

4.8
63h
1093 estudantes
0 curtidas
Inteligência Artificial

Visão Geral do Curso

📚 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

  1. Analyze Java’s evolution within the programming ecosystem and its adaptation to multicore hardware.
  2. Implement behavior parameterization using method references and lambdas to pass code as a "first-class citizen."
  3. Distinguish between internal and external iteration and explain how the Streams API facilitates parallelism.
  4. Identify how behavior parameterization solves the problem of "coping with changing requirements" in software evolution.
  5. Implement the Strategy design pattern using Predicates to filter data by abstract criteria.
  6. Reduce code verbosity by transitioning from named classes to anonymous classes and finally to lambda expressions.
  7. Identify and write valid lambda expressions using both expression and block syntax styles.
  8. Implement the execute-around pattern to improve code reusability in resource-intensive tasks.
  9. Utilize standard functional interfaces and their primitive specializations to optimize performance.
  10. Define a Java Stream and identify its primary characteristics (Declarative, Composable, Parallelizable).

🔹 Lesson 1: Introduction to Modern Java (8-11)

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.

🔹 Lesson 2: Core Behavior Parameterization

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.

🔹 Lesson 3: Mastering 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.

🔹 Lesson 4: Introduction to Streams

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 IllegalStateException when violated.

🔹 Lesson 5: Intermediate Stream Operations

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 map and flatMap.
  • Utilize primitive stream specializations (IntStream, etc.) and Optional primitives to optimize performance and handle potential null values safely.

🔹 Lesson 6: Advanced Data Collection with Streams

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 Collector interface methods (supplier, accumulator, finisher, combiner) to create custom collection logic.

🔹 Lesson 7: Parallel Stream Processing

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 RecursiveTask class and the work-stealing algorithm.

🔹 Lesson 8: Collection API Enhancements

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 removeIf and replaceAll without ConcurrentModificationException.
  • Optimize Map interactions using getOrDefault, computeIfAbsent, and advanced merging patterns.

🔹 Lesson 9: Refactoring and Debugging Lambdas

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.

🔹 Lesson 10: Building DSLs with Lambdas

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.

🔹 Lesson 11: Using Optional for Null-Safety

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 Optional objects using appropriate factory methods based on value certainty.
  • Implement fluent data pipelines using map, flatMap, and filter to replace nested if-null blocks.

🔹 Lesson 12: The Modern Date and Time API

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) and Period (date-based) to model intervals.
  • Apply TemporalAdjusters for complex date manipulation and DateTimeFormatter for customized parsing and printing.

🔹 Lesson 13: Default Methods and Interface Evolution

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.

🔹 Lesson 14: The Java Module System (Project Jigsaw)

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) using requires, exports, transitive, and qualified exports.
  • Differentiate between the Java Module System (Jigsaw) and OSGi frameworks.

🔹 Lesson 15: Concepts of Asynchronous Programming

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.

🔹 Lesson 16: Composable Futures with CompletableFuture

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.

🔹 Lesson 17: Reactive Programming and Flow API

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.

🔹 Lesson 18: Thinking Functionally

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.

🔹 Lesson 19: Functional Programming Techniques

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.

🔹 Lesson 20: Java vs. Scala: Blending OOP and FP

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.

🔹 Lesson 21: The Future of Java

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.