📄 Page
1
W eid ig A Functiona l A p p roa ch to Ja va A Functiona l A p p roa ch to Ja va Ben Weidig A Functional Approach to Java Augmenting Object-Oriented Java Code with Functional Principles
📄 Page
2
JAVA A Functional Approach to Java Twitter: @oreillymedia linkedin.com/company/oreilly-media youtube.com/oreillymedia 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 US $74.99 CAN $93.99 ISBN: 978-1-098-10992-9 “The functional paradigm is a significant change in Java programming, but it doesn’t mean abandoning OOP completely. A Functional Approach to Java is a must-read for any programmer looking to improve their skills and stay current in programming. —A N M Bazlur Rahman Software Engineer, Java Champion, Author, JUG Leader Ben Weidig runs a Java-based SaaS company. He has almost two decades of experience in professional web, mobile, and systems programming in various languages. Ben is also an active open source contributor. W eid ig A Functiona l A p p roa ch to Ja va A Functiona l A p p roa ch to Ja va
📄 Page
3
Ben Weidig A Functional Approach to Java Augmenting Object-Oriented Code with Functional Principles Boston Farnham Sebastopol TokyoBeijing
📄 Page
4
978-1-098-10992-9 [LSI] A Functional Approach to Java by Ben Weidig Copyright © 2023 Benjamin Weidig. All rights reserved. Printed in the United States of America. Published by O’Reilly Media, Inc., 1005 Gravenstein Highway North, Sebastopol, CA 95472. O’Reilly books may be purchased for educational, business, or sales promotional use. Online editions are also available for most titles (http://oreilly.com). For more information, contact our corporate/institutional sales department: 800-998-9938 or corporate@oreilly.com. Acquisitions Editor: Brian Guerin Development Editor: Rita Fernando Production Editor: Ashley Stussy Copyeditor: Liz Wheeler Proofreader: Piper Editorial Consulting, LLC Indexer: WordCo Indexing Services, Inc. Interior Designer: David Futato Cover Designer: Karen Montgomery Illustrator: Kate Dullea May 2023: First Edition Revision History for the First Edition 2023-05-09: First Release See http://oreilly.com/catalog/errata.csp?isbn=9781098109929 for release details. The O’Reilly logo is a registered trademark of O’Reilly Media, Inc. A Functional Approach to Java, the cover image, and related trade dress are trademarks of O’Reilly Media, Inc. The views expressed in this work are those of the author, and do not represent the publisher’s views. While the publisher and the author have used good faith efforts to ensure that the information and instructions contained in this work are accurate, the publisher and the author disclaim all responsibility for errors or omissions, including without limitation responsibility for damages resulting from the use of or reliance on this work. Use of the information and instructions contained in this work is at your own risk. If any code samples or other technology this work contains or describes is subject to open source licenses or the intellectual property rights of others, it is your responsibility to ensure that your use thereof complies with such licenses and/or rights.
📄 Page
5
Table of Contents Preface. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . xi Part I. Functional Basics 1. An Introduction to Functional Programming. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 3 What Makes a Language Functional? 3 Functional Programming Concepts 6 Pure Functions and Referential Transparency 6 Immutability 7 Recursion 8 First-Class and Higher-Order Functions 8 Functional Composition 9 Currying 9 Partial Function Application 10 Lazy Evaluation 11 Advantages of Functional Programming 12 Disadvantages of Functional Programming 12 Takeaways 14 2. Functional Java. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 15 What Are Java Lambdas? 15 Lambda Syntax 15 Functional Interfaces 17 Lambdas and Outside Variables 19 What about Anonymous Classes? 23 iii
📄 Page
6
Lambdas in Action 26 Creating Lambdas 26 Calling Lambdas 28 Method References 28 Functional Programming Concepts in Java 32 Pure Functions and Referential Transparency 32 Immutability 35 First-Class Citizenship 36 Functional Composition 36 Lazy Evaluation 37 Takeaways 38 3. Functional Interfaces of the JDK. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 39 The Big Four Functional Interface Categories 39 Functions 40 Consumers 40 Suppliers 41 Predicates 41 Why So Many Functional Interface Variants? 42 Function Arity 42 Primitive Types 44 Bridging Functional Interfaces 46 Functional Composition 47 Extending Functional Support 49 Adding Default Methods 49 Implementing Functional Interfaces Explicitly 51 Creating Static Helpers 52 Takeaways 56 Part II. A Functional Approach 4. Immutability. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 59 Mutability and Data Structures in OOP 59 Immutability (Not Only) in FP 61 The State of Java Immutability 63 java.lang.String 63 Immutable Collections 65 Primitives and Primitive Wrappers 69 Immutable Math 69 iv | Table of Contents
📄 Page
7
Java Time API (JSR-310) 70 Enums 71 The final Keyword 71 Records 72 How to Achieve Immutability 73 Common Practices 74 Takeaways 75 5. Working with Records. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 77 Data Aggregation Types 77 Tuples 77 A Simple POJO 78 From POJO to Immutability 80 From POJO to Record 82 Records to the Rescue 82 Behind the Scenes 83 Record Features 84 Missing Features 88 Use Cases and Common Practices 95 Record Validation and Data Scrubbing 96 Increasing Immutability 97 Creating Modified Copies 97 Records as Local Nominal Tuples 101 Better Optional Data Handling 104 Serializing Evolving Records 105 Record Pattern Matching (Java 19+) 108 Final Thoughts on Records 110 Takeaways 110 6. Data Processing with Streams. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 113 Data Processing with Iteration 113 External Iteration 114 Internal Iteration 116 Streams as Functional Data Pipelines 117 Stream Features 121 Spliterator, the Backbone of Streams 125 Building Stream Pipelines 127 Creating a Stream 128 Doing the Work 129 Terminating the Stream 137 The Cost of Operations 151 Table of Contents | v
📄 Page
8
Modifying Stream Behavior 154 To Use a Stream, or Not? 155 Takeaways 157 7. Working with Streams. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 159 Primitive Streams 159 Iterative Streams 161 Infinite Streams 163 Random Numbers 164 Memory Isn’t Infinite 165 From Arrays to Streams and Back 166 Object-Type Arrays 167 Primitive Arrays 167 Low-Level Stream Creation 168 Working with File I/O 169 Reading Directory Contents 170 Depth-First Directory Traversal 170 Searching the Filesystem 172 Reading Files Line-By-Line 173 Caveats of File I/O Streams 175 Dealing with Date and Time 176 Querying Temporal Types 176 LocalDate-Range Streams 177 Measuring Stream Performance with JMH 177 More about Collectors 178 Downstream Collectors 179 Creating Your Own Collector 188 Final Thoughts on (Sequential) Streams 190 Takeaways 191 8. Parallel Data Processing with Streams. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 193 Concurrency versus Parallelism 193 Streams as Parallel Functional Pipelines 195 Parallel Streams in Action 197 When to Use and When to Avoid Parallel Streams 200 Choosing the Right Data Source 200 Number of Elements 202 Stream Operations 202 Stream Overhead and Available Resources 206 Example: War and Peace (revisited) 207 Example: Random Numbers 208 vi | Table of Contents
📄 Page
9
Parallel Streams Checklist 211 Takeaways 212 9. Handling null with Optionals. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 213 The Problem with null References 213 How to Handle null in Java (Before Optionals) 216 Best Practices for Handling null 217 Tool-Assisted null Checks 219 Specialized Types Like Optional 220 Optionals to the Rescue 220 What’s an Optional? 221 Building Optional Pipelines 224 Optionals and Streams 231 Optionals as Stream Elements 232 Terminal Stream Operations 233 Optional Primitives 235 Caveats 236 Optionals Are Ordinary Types 237 Identity-Sensitive Methods 237 Performance Overhead 238 Special Considerations for Collections 239 Optionals and Serialization 239 Final Thoughts on null References 240 Takeaways 241 10. Functional Exception Handling. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 243 Java Exception Handling in a Nutshell 243 The try-catch block 244 The Different Types of Exceptions and Errors 244 Checked Exceptions in Lambdas 247 Safe Method Extraction 248 Un-Checking Exceptions 250 Sneaky Throws 251 A Functional Approach to Exceptions 253 Not Throwing Exceptions 253 Errors as Values 254 The Try/Success/Failure Pattern 259 Final Thoughts on Functional Exception Handling 266 Takeaways 267 Table of Contents | vii
📄 Page
10
11. Lazy Evaluation. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 269 Laziness Versus Strictness 269 How Strict Is Java? 271 Short-Circuit Evaluation 271 Control Structures 272 Lazy Types in the JDK 273 Lambdas and Higher-Order Functions 275 An Eager Approach 275 A Lazier Approach 276 A Functional Approach 276 Delayed Executions with Thunks 278 Creating a Simple Thunk 278 A Thread-Safe Thunk 280 Final Thoughts on Laziness 283 Takeaways 284 12. Recursion. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 285 What Is Recursion? 285 Head Versus Tail Recursion 287 Recursion and the Call Stack 288 A More Complex Example 289 Iterative Tree Traversal 291 Recursive Tree Traversal 292 Recursion-Like Streams 294 Final Thoughts on Recursion 294 Takeaways 296 13. Asynchronous Tasks. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 297 Synchronous versus Asynchronous 297 Java Futures 298 Designing Asynchronous Pipelines with CompletableFutures 300 Promising a Value 301 Creating a CompletableFuture 301 Compositing and Combining Tasks 302 Exception Handling 306 Terminal Operations 308 Creating a CompletableFuture Helper 309 Manual Creation and Completion 314 Manual Creation 315 Manual Completion 315 viii | Table of Contents
📄 Page
11
Use Cases for Manually Created and Completed Instances 316 About Thread Pools and Timeouts 320 Final Thoughts on Asynchronous Tasks 321 Takeaways 322 14. Functional Design Patterns. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 325 What Are Design Patterns? 325 (Functional) Design Patterns 326 Factory Pattern 326 Decorator Pattern 329 Strategy Pattern 335 Builder Pattern 337 Final Thoughts on Functional Design Patterns 342 Takeaways 343 15. A Functional Approach to Java. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 345 OOP Versus FP Principles 345 A Functional Mindset 346 Functions Are First-Class Citizens 347 Avoiding Side Effects 348 Functional Data Processing with Map/Filter/Reduce 355 Abstractions Guide Implementations 356 Building Functional Bridges 357 Parallelism and Concurrency Made Easy 362 Be Mindful of Potential Overhead 363 Functional Architecture in an Imperative World 364 From Objects to Values 366 Separation of Concerns 366 The Different Sizes of an FC/IS 368 Testing an FC/IS 369 Final Thoughts on a Functional Approach to Java 370 Takeaways 372 Index. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 373 Table of Contents | ix
📄 Page
12
(This page has no text content)
📄 Page
13
1 Originally specified in 1958, Lisp is the second-oldest high-level programming language still in common use. It also builds the foundation of a variety of programming languages, like Emacs Lisp, or the functional JVM language Clojure. Preface A mind that is stretched by a new experience can never go back to its old dimensions. —Oliver Wendell Holmes, Jr. Developing software is quite a complex endeavor. As Java developers, we usually try to tame this complexity with object-oriented programming (OOP) as a metaphor to represent the things we are developing, such as data structures, and use a primarily imperative-focused coding style to handle our program’s state. Although OOP is a well-known and battle-tested approach to developing sensible software, not every problem is a good match for it. We might introduce a certain amount of unnecessary complexity by forcing OOP principles on every problem instead of using more appropriate tools and paradigms also available to us. The functional programming (FP) paradigm offers an alternative approach to solving problems. Functional programming isn’t a new idea. In fact, it’s even older than object-oriented programming! It first appeared in the early years of computing, in the 1950s, in the Lisp1 programming language and has been used quite commonly in academia and niche fields. In recent years, however, there has been an increasing interest in functional paradigms. Many new functional languages have emerged, and non-functional languages are including functional features to various degrees. The ideas and concepts behind FP are now adopted in almost every mainstream multiparadigm and general-purpose language, allowing us to use some form of functional programming regardless of the context and chosen language. Nothing stops us from taking the best parts of FP and xi
📄 Page
14
2 Moore’s law was coined in 1965 by the cofounder of Intel, Gordon Moore, as the observation of transistor counts doubling every two years and, therefore, the performance per core available to us. Chris Edwards, “Moore’s Law: What Comes Next?” Communications of the ACM, Vol. 64, No. 2 (Feb. 2021): 12-14. 3 N. C. Thompson and Svenja Spanuth, “The Decline of Computers as a General-Purpose Technology,” Com‐ munications of the ACM, Vol. 64, No. 3 (Mar. 2021): 64-72. augmenting our existing way of programming and software development tools—and that’s what this book is about! In this book, you’ll learn the fundamentals of functional programming and how to apply this knowledge to your daily work using Java. New Hardware Needs a New Way of Thinking Hardware is evolving in a new direction. For quite some time, single-core perfor‐ mance improvements haven’t been as significant as with each previous processor generation. Moore’s law2 seems to slow down, but such a slowdown doesn’t mean that hardware isn’t improving anymore. But instead of primarily focussing on single-core performance and even higher GHz numbers, the manufacturers favor more and more cores.3 So, for modern workloads to reap all the benefits of new hardware that favors more cores rather than faster ones, we need to adopt techniques that can use more cores to their advantage without compromising productivity or introducing additional complexity. Scaling your software horizontally through parallelism isn’t an easy task in OOP. Not every problem is a good fit for parallelism. More painters might paint a room faster, but you can’t speed up pregnancy by involving more people. If the problem consists of serial or interdependent tasks, concurrency is preferable to parallelism. But paral‐ lelism really shines if a problem breaks down into smaller, non-related subproblems. That’s where functional programming comes in. The stateless and immutable nature of idiomatic FP provides all the tools necessary to build small, reliable, reusable, and higher-quality tasks that elegantly fit into parallel and concurrent environments. Adopting a functional mindset adds another set of tools to your toolbelt that will allow you to tackle your daily development problems in a new way and scale your code more easily and safely than before. Next, let’s look at why Java can be a good choice for functional programming. Java Can Be Functional, Too Many programming languages out there are great for functional programming. Haskell is a favorite if you prefer a pure functional language with almost no support for an imperative coding style. Elixir is another exciting option that leverages the xii | Preface
📄 Page
15
4 Erlang is a functional and concurrency-oriented programming language that is known for building low- latency, distributed, and fault-tolerant systems. 5 Dean Wampler shows in his book Functional Programming for Java Developers (O’Reilly) in detail how to implement and facilitate the missing functional programming features in Java all by yourself. He describes many techniques that weren’t easily feasible before version 8. But now, many of the shortcomings and gaps in the JDK are closed up, and it provides many of the tools necessary to incorporate FP concisely and more straightforwardly. 6 Oracle introduced a faster release schedule for Java with the release of versions beyond 9 with a fixed release cadence of six months. To meet such a tight schedule, not every release is considered “long-term-support,” in favor of releasing features faster than before. Erlang Virtual Machine.4 However, you don’t have to leave the vast JVM ecosystem behind to find FP-capable languages. Scala shines in combining OOP and FP para‐ digms into a concise, high-level language. Another popular choice, Clojure, was designed from the ground up as a functional language with a dynamic type system at heart. In a perfect world, you’d have the luxury of choosing the perfect functional language for your next project. In reality, you might not have a choice at all about what language to use, and you’ll have to play the cards you’re dealt. As a Java developer, you’d use Java, which was historically seen as not ideal for functional programming. Before we continue, though, I need to stress that you can implement most functional principles in Java, regardless of deeply integrated language-level support5. Still, the resulting code won’t be as concise and easy to rea‐ son with as it would in other languages that allow a functional approach in the first place. This caveat scares many developers away from even trying to apply functional principles to Java, despite the fact that it might have provided a more productive approach or better overall solution. In the past, many people thought of Java as a slow-moving behemoth, a “too big to become extinct” enterprise language, like a more modern version of COBOL or Fortran. And in my opinion that’s partially true, or at least it was in the past. The pace didn’t pick up until Java 9 and the shortened release timeframes6. It took Java five years to go from version 6 to 7 (2006-2011). And even though there were significant new features, like try-with-resources, none of them were “groundbreaking.” The few and slow changes in the past led to projects and developers not adopting the “latest and greatest” Java Development Kit (JDK) and missing out on many language improvements. Three years later, in 2014, the next version, Java 8, was released. This time, it introduced one of the most significant changes to Java’s future: lambda expressions. A better foundation for functional programming had finally arrived in arguably the most prominent object-oriented programming language of the world, changing the language and its idioms significantly: Preface | xiii
📄 Page
16
Runnable runnable = () -> System.out.println("hello, functional world!"); The addition of lambda expressions was monumental in making it possible to finally use functional programming in Java as an integrated language and runtime feature. Not only that, but a whole new world of ideas and concepts was made available to Java developers. Many of the JDK’s new features, like Streams, the Optional type, or CompletableFuture, are only possible in such a concise and straightforward way thanks to language-level lambda expressions and Java’s other functional additions. These new idioms and new ways of doing things with FP in Java may seem strange and might not come naturally, especially if you’re primarily accustomed to OOP. Throughout this book, I’ll show you how to develop a mindset that’ll help you apply FP principles to your code and how to make it better without needing to go “fully functional.” Why I Wrote This Book After using another multipurpose language with excellent functional programming support—Swift—and seeing the benefits firsthand, I gradually introduced more and more functional principles in my Java-based projects, too. Thanks to lambda expres‐ sions and all the other features introduced in Java 8 and later, all the tools necessary were readily available. But after using these tools more frequently and discussing them with my colleagues, I realized something: How to use lambdas, Streams, and all the other functional goodies provided by Java is easy to grasp. But without a deeper understanding of why and when you should use them—and when not to—you won’t unlock their full potential, and it will just be “new wine in old wineskins.” So I decided to write this book to highlight the different concepts that make a lan‐ guage functional, and how you can incorporate them into your Java code, either with the tools provided by the JDK or by creating them yourself. A functional approach to your Java code will most likely challenge the status quo and go against best practices you were using before. But by embracing a more functional way of doing things, like immutability and pure functions, you will be able to write more concise, more reasonable, and future-proof code that is less prone to bugs. Who Should Read This Book This book is for you if you are curious about functional programming and want to know what all the fuss is about and apply it to your Java code. You might already be using some functional Java types but desire a more profound knowledge of why and how to apply them more effectively. There is no need to be an expert on OOP, but the book is not a beginner’s guide to Java or OOP. You should already be familiar with the Java standard library. No prior xiv | Preface
📄 Page
17
knowledge of functional programming is required. Every concept is introduced with an explanation and examples. The book covers Java 17 as the latest Long-Term-Support (LTS) version available at publication. Knowing that many developers need to support projects with earlier versions, the general baseline will be the previous LTS, Java 11. But even if you’re stuck on Java 8, many of the discussed topics are relevant. However, some chapters will rely on newer features, like Records, which were introduced in Java 14. This book might not be for you if you are looking for a compartmentalized, recipe- style book presenting “ready-to-implement” solutions. Its main intention is to intro‐ duce functional concepts and idioms and teach you how to incorporate them into your Java code. What You Will Learn By the end of this book, you will have a fundamental knowledge of functional programming and its underlying concepts and how to apply this knowledge to your daily work. Every Java functional type will be at your disposal, and you will be able to build anything missing from the JDK by yourself, if necessary. You will learn about the concepts and importance of the following aspects of func‐ tional programming: Composition Build modular and easy composable blocks. Expressiveness Write more concise code that clearly expresses its intent. Safer code Create safer data structures without side effects that don’t need to deal with race conditions or locks, which are hard to use without introducing bugs. Modularity Break down larger projects into more easily manageable modules. Maintainability Use smaller functional blocks with less interconnection to make changes and refactoring safer without breaking other parts of your code. Data manipulation Build efficient data manipulation pipelines with less complexity. Performance Immutability and predictability allow you to scale horizontally with parallelism without much thought about it. Preface | xv
📄 Page
18
7 The Android Open Source Project provides a good overview of the features and the reasoning behind Android’s runtime. Even without going fully functional, your code will benefit from the concepts and idioms presented in this book. And not only your Java code. You will tackle develop‐ ment challenges with a functional mindset, improving your programming regardless of the used language or paradigm. What about Android? It’s hard to talk about Java without bringing up Android as well. Even though you can write Android applications in Java, the underlying API and runtime aren’t the same. So what does this mean for adopting a functional approach to Java for Android apps? To better understand that, we first need to look at what makes Java for Android different from “normal” Java. Android doesn’t run Java bytecode directly on a minimalistic JVM optimized for smaller devices, like Java Platform Micro Edition. Instead, the bytecode gets recom‐ piled. The Dex-compiler creates Dalvik bytecode, which is then run on a specialized runtime: the Android Runtime (ART) and previously on the Dalvik virtual machine.7 Recompiling Java bytecode to Dalvik bytecode allows the devices to run highly optimized code, getting the most out of their hardware constraints. For you as a developer, however, that means that even though your code looks and feels like Java on the surface—most of the public API is identical—there isn’t a feature parity between the JDK and Android SDK you can rely on. For example, the cornerstones of this book—lambda expressions and Streams—were among the missing features in Android for a long time. The Android Gradle plugin started supporting some of the missing functional fea‐ tures (lambda expressions, method references, default and static interface methods) with version 3.0.0 by using so-called desugaring: the compiler uses bytecode transfor‐ mations to replicate a feature behind the scenes without supporting the new syntax or providing an implementation in the runtime itself. The next major version, 4.0.0, added even more functional features: Streams, Option‐ als, and the java.util.function package. That allows you to benefit from the func‐ tional paradigms and tools discussed in this book, even as an Android developer. Even though most of the JDK’s functional features are available on Android, too, they are not verbatim copies8 and might have different performance characteristics and edge cases. The available features are listed in the official documentation on the Java 8+ support. xvi | Preface
📄 Page
19
8 Jake Wharton, a well-known Android developer, provides a detailed insight on how Android desugars modern Java code. 9 See the official Kotlin documentation for an overview of supported platforms. 10 Each lambda compiles to an anonymous class extending kotlin.jvm.internal.FunctionImpl, as explained in the function type specs. A Functional Approach to Android In 2019, Kotlin took the mantle of preferred language for Android developers from Java. It’s a multiplatform language that mainly targets the JVM but also compiles to JavaScript and multiple native platforms.9 It aims to be a “modern and more concise” Java, fixing many of Java’s debatable shortcomings and cruft accumulated over the years due to backward compatibility, without forgoing all the frameworks and libraries available to Java. And it’s 100% interoperable: you can easily mix Java and Kotlin in the same project. One obvious advantage of Kotlin over Java is that many functional concepts and idioms are integral to the language itself. Still, as a different language, Kotlin has its own idioms and best practices that differ from Java’s. The generated bytecode might differ, too, like that used to generate lambdas.10 The most significant advantage of Kotlin is its attempt to create a more concise and predictable language compared to Java. And just like you can be more functional in Java without going fully functional, you can use Kotlin-only features without going full Kotlin in your Android projects, too. By mixing Java and Kotlin, you can pick the best features from both languages. Keep in mind that this book’s primary focus is the Java language and the JDK. Still, most of the ideas behind what you will learn are transferrable to Android, even if you use Kotlin. However, there won’t be any special considerations for Android or Kotlin throughout the book. Navigating This Book This book consists of two different parts: • Part I, Functional Basics, introduces the history and core concepts of functional programming, how Java implements these concepts, and what types are already available to us as developers. • Part II, A Functional Approach, is a topic-based deep dive through the more generalized programming concepts and how to augment them with functional principles and the newly available tools. Certain features, like Records and Streams, are highlighted with extended examples and use cases. Preface | xvii
📄 Page
20
Reading the chapters in their respective order will let you get the most out of them because they usually build on each other. But feel free to skim for the bits that might interest you and jump around. Any necessary connections are cross-referenced to fill in any blanks if needed. Conventions Used in This Book The following typographical conventions are used in this book: Italic Indicates new terms, URLs, email addresses, filenames, and file extensions. Constant width Used for program listings, as well as within paragraphs, to refer to program elements such as variable or function names, databases, data types, environment variables, statements, and keywords. Constant width bold Shows commands or other text that should be typed literally by the user. Constant width italic Shows text that should be replaced with user-supplied values or by values deter‐ mined by context. This element signifies a tip or suggestion. This element signifies a general note. This element indicates a warning or caution. xviii | Preface