A Functional Approach to Java

Book description

Java developers usually tackle the complexity of software development through object-oriented programming (OOP). But not every problem is a good match for OOP. The functional programming (FP) paradigm offers you another approach to solving problems, and Java provides easy-to-grasp FP tools such as lambda expressions and Streams. If you're interested in applying FP concepts to your Java code, this book is for you.

Author Ben Weidig highlights different aspects of functional programming and shows you how to incorporate them into your code without going "fully functional." You'll learn how, when, and why to use FP concepts such as immutability and pure functions to write more concise, reasonable, and future-proof code. Many developers seek to expand their horizons by using OOP and FP together. It's no longer either-or; it's both.

In this book, you will:

  • Get a high-level overview of functional programming, including the types already available to Java developers
  • Explore different FP concepts and learn how to use them
  • Learn how to augment your code and use Java's new functional features in your daily work without going fully functional
  • Develop a functional mindset and improve your programming skills regardless of language or paradigm

Publisher resources

View/Submit Errata

Table of contents

  1. Preface
    1. New Hardware Needs a New Way of Thinking
    2. Java Can Be Functional, Too
    3. Why I Wrote This Book
    4. Who Should Read This Book
    5. What You Will Learn
    6. What about Android?
      1. A Functional Approach to Android
    7. Navigating This Book
    8. Conventions Used in This Book
    9. Using Code Examples
    10. O’Reilly Online Learning
    11. Acknowledgments
  2. I. Functional Basics
  3. 1. An Introduction to Functional Programming
    1. What Makes a Language Functional?
    2. Functional Programming Concepts
      1. Pure Functions and Referential Transparency
      2. Immutability
      3. Recursion
      4. First-Class and Higher-Order Functions
      5. Functional Composition
      6. Currying
      7. Partial Function Application
      8. Lazy Evaluation
    3. Advantages of Functional Programming
    4. Disadvantages of Functional Programming
    5. Takeaways
  4. 2. Functional Java
    1. What Are Java Lambdas?
      1. Lambda Syntax
      2. Functional Interfaces
      3. Lambdas and Outside Variables
      4. What about Anonymous Classes?
    2. Lambdas in Action
      1. Creating Lambdas
      2. Calling Lambdas
      3. Method References
    3. Functional Programming Concepts in Java
      1. Pure Functions and Referential Transparency
      2. Immutability
      3. First-Class Citizenship
      4. Functional Composition
      5. Lazy Evaluation
    4. Takeaways
  5. 3. Functional Interfaces of the JDK
    1. The Big Four Functional Interface Categories
      1. Functions
      2. Consumers
      3. Suppliers
      4. Predicates
    2. Why So Many Functional Interface Variants?
      1. Function Arity
      2. Primitive Types
      3. Bridging Functional Interfaces
    3. Functional Composition
    4. Extending Functional Support
      1. Adding Default Methods
      2. Implementing Functional Interfaces Explicitly
      3. Creating Static Helpers
    5. Takeaways
  6. II. A Functional Approach
  7. 4. Immutability
    1. Mutability and Data Structures in OOP
    2. Immutability (Not Only) in FP
    3. The State of Java Immutability
      1. java.lang.String
      2. Immutable Collections
      3. Primitives and Primitive Wrappers
      4. Immutable Math
      5. Java Time API (JSR-310)
      6. Enums
      7. The final Keyword
      8. Records
    4. How to Achieve Immutability
    5. Common Practices
    6. Takeaways
  8. 5. Working with Records
    1. Data Aggregation Types
      1. Tuples
      2. A Simple POJO
      3. From POJO to Immutability
      4. From POJO to Record
    2. Records to the Rescue
      1. Behind the Scenes
      2. Record Features
      3. Missing Features
    3. Use Cases and Common Practices
      1. Record Validation and Data Scrubbing
      2. Increasing Immutability
      3. Creating Modified Copies
      4. Records as Local Nominal Tuples
      5. Better Optional Data Handling
      6. Serializing Evolving Records
      7. Record Pattern Matching
      8. Sealed Types as Components
    4. Final Thoughts on Records
    5. Takeaways
  9. 6. Data Processing with Streams
    1. Data Processing with Iteration
      1. External Iteration
      2. Internal Iteration
    2. Streams as Functional Data Pipelines
      1. Stream Features
      2. Spliterator, the Backbone of Streams
    3. Building Stream Pipelines
      1. Creating a Stream
      2. Doing the Work
      3. Terminating the Stream
      4. The Cost of Operations
      5. Modifying Stream Behavior
    4. To Use a Stream, or Not?
    5. Takeaways
  10. 7. Working with Streams
    1. Primitive Streams
    2. Iterative Streams
    3. Infinite Streams
      1. Random Numbers
      2. Memory Isn’t Infinite
    4. From Arrays to Streams and Back
      1. Object-Type Arrays
      2. Primitive Arrays
    5. Low-Level Stream Creation
    6. Working with File I/O
      1. Reading Directory Contents
      2. Depth-First Directory Traversal
      3. Searching the Filesystem
      4. Reading Files Line-By-Line
      5. Caveats of File I/O Streams
    7. Dealing with Date and Time
      1. Querying Temporal Types
      2. LocalDate-Range Streams
    8. Measuring Stream Performance with JMH
    9. More about Collectors
      1. Downstream Collectors
      2. Creating Your Own Collector
    10. Customizing Intermediate Operations (Java 22+)
      1. Custom Distinct Operation without Gatherer
      2. Creating a Custom Distinct Gatherer
    11. Final Thoughts on (Sequential) Streams
    12. Takeaways
  11. 8. Parallel Data Processing with Streams
    1. Concurrency versus Parallelism
    2. Streams as Parallel Functional Pipelines
    3. Parallel Streams in Action
    4. When to Use and When to Avoid Parallel Streams
      1. Choosing the Right Data Source
      2. Number of Elements
      3. Stream Operations
      4. Stream Overhead and Available Resources
      5. Example: War and Peace (revisited)
      6. Example: Random Numbers
    5. Parallel Streams Checklist
    6. Takeaways
  12. 9. Handling null with Optionals
    1. The Problem with null References
    2. How to Handle null in Java (Before Optionals)
      1. Best Practices for Handling null
      2. Tool-Assisted null Checks
      3. Specialized Types Like Optional
    3. Optionals to the Rescue
      1. What’s an Optional?
      2. Building Optional Pipelines
    4. Optionals and Streams
      1. Optionals as Stream Elements
      2. Terminal Stream Operations
    5. Optional Primitives
    6. Caveats
      1. Optionals Are Ordinary Types
      2. Identity-Sensitive Methods
      3. Performance Overhead
      4. Special Considerations for Collections
      5. Optionals and Serialization
    7. Final Thoughts on null References
    8. Takeaways
  13. 10. Functional Exception Handling
    1. Java Exception Handling in a Nutshell
    2. The try-catch block
      1. The Different Types of Exceptions and Errors
    3. Checked Exceptions in Lambdas
      1. Safe Method Extraction
      2. Un-Checking Exceptions
      3. Sneaky Throws
    4. A Functional Approach to Exceptions
      1. Not Throwing Exceptions
      2. Errors as Values
      3. The Try/Success/Failure Pattern
    5. Final Thoughts on Functional Exception Handling
    6. Takeaways
  14. 11. Lazy Evaluation
    1. Laziness Versus Strictness
    2. How Strict Is Java?
      1. Short-Circuit Evaluation
      2. Control Structures
      3. Lazy Types in the JDK
    3. Lambdas and Higher-Order Functions
      1. An Eager Approach
      2. A Lazier Approach
      3. A Functional Approach
    4. Delayed Executions with Thunks
      1. Creating a Simple Thunk
      2. A Thread-Safe Thunk
    5. Final Thoughts on Laziness
    6. Takeaways
  15. 12. Recursion
    1. What Is Recursion?
      1. Head Versus Tail Recursion
      2. Recursion and the Call Stack
    2. A More Complex Example
      1. Iterative Tree Traversal
      2. Recursive Tree Traversal
    3. Recursion-Like Streams
    4. Final Thoughts on Recursion
    5. Takeaways
  16. 13. Asynchronous Tasks
    1. Synchronous versus Asynchronous
    2. Java Futures
    3. Designing Asynchronous Pipelines with CompletableFutures
      1. Promising a Value
      2. Creating a CompletableFuture
      3. Compositing and Combining Tasks
      4. Exception Handling
      5. Terminal Operations
      6. Creating a CompletableFuture Helper
    4. Manual Creation and Completion
      1. Manual Creation
      2. Manual Completion
      3. Use Cases for Manually Created and Completed Instances
    5. About Thread Pools and Timeouts
    6. Final Thoughts on Asynchronous Tasks
    7. Takeaways
  17. 14. Functional Design Patterns
    1. What Are Design Patterns?
    2. (Functional) Design Patterns
      1. Factory Pattern
      2. Decorator Pattern
      3. Strategy Pattern
      4. Builder Pattern
    3. Final Thoughts on Functional Design Patterns
    4. Takeaways
  18. 15. A Functional Approach to Java
    1. OOP Versus FP Principles
    2. A Functional Mindset
      1. Functions Are First-Class Citizens
      2. Avoiding Side Effects
      3. Functional Data Processing with Map/Filter/Reduce
      4. Abstractions Guide Implementations
      5. Building Functional Bridges
      6. Parallelism and Concurrency Made Easy
      7. Be Mindful of Potential Overhead
    3. Functional Architecture in an Imperative World
      1. From Objects to Values
      2. Separation of Concerns
      3. The Different Sizes of an FC/IS
      4. Testing an FC/IS
    4. Final Thoughts on a Functional Approach to Java
    5. Takeaways
  19. Index
  20. About the Author

Product information

  • Title: A Functional Approach to Java
  • Author(s): Ben Weidig
  • Release date: May 2023
  • Publisher(s): O'Reilly Media, Inc.
  • ISBN: 9781098109929