(This page has no text content)
(This page has no text content)
Praise for Functional Programming in Java Venkat has done a superb job of bringing core functional language concepts to the Java ecosystem. Once you have peered into his looking glass of functional language design, it will be hard to go back to old-school imperative programming. ➤ Stephen Chin, Java technology ambassador and JavaOne content chair The introduction of lambdas to Java 8 has made me excited to use Java again, and Venkat’s combination of technical details and best practices make it easy to apply functional thinking to this new feature. ➤ Kimberly D. Barnes, senior software engineer Java 8 lambda expressions are an incredibly important new language feature. Every Java developer should read this excellent book and learn how to use them effectively. ➤ Chris Richardson, software architect and Java champion Many can explain lambdas; Venkat makes them useful. ➤ Kirk Pepperdine, Java performance tuning expert I highly recommend this book for Java programmers who want to get up to speed with functional programming in Java 8. It is a very concise book but still provides a comprehensive overview of Java 8. ➤ Nilanjan Raychaudhuri, author and developer at Typesafe
Functional Programming in Java Harnessing the Power of Java 8 Lambda Expressions Venkat Subramaniam The Pragmatic Bookshelf Dallas, Texas • Raleigh, North Carolina
Many of the designations used by manufacturers and sellers to distinguish their products are claimed as trademarks. Where those designations appear in this book, and The Pragmatic Programmers, LLC was aware of a trademark claim, the designations have been printed in initial capital letters or in all capitals. The Pragmatic Starter Kit, The Pragmatic Programmer, Pragmatic Programming, Pragmatic Bookshelf, PragProg and the linking g device are trade- marks of The Pragmatic Programmers, LLC. Every precaution was taken in the preparation of this book. However, the publisher assumes no responsibility for errors or omissions, or for damages that may result from the use of information (including program listings) contained herein. Our Pragmatic courses, workshops, and other products can help you and your team create better software and have more fun. For more information, as well as the latest Pragmatic titles, please visit us at http://pragprog.com. Sir Charles Antony Richard Hoare’s quote is used by permission of the ACM. Abelson and Sussman’s quote is used under Creative Commons license. The team that produced this book includes: Jacquelyn Carter (editor) Potomac Indexing, LLC (indexer) Candace Cunningham (copyeditor) David J Kelly (typesetter) Janet Furlow (producer) Ellie Callahan (support) For international rights, please contact rights@pragprog.com. Copyright © 2014 The Pragmatic Programmers, LLC. All rights reserved. No part of this publication may be reproduced, stored in a retrieval system, or transmitted, in any form, or by any means, electronic, mechanical, photocopying, recording, or otherwise, without the prior consent of the publisher. Printed in the United States of America. ISBN-13: 978-1-937785-46-8 Encoded using the finest acid-free high-entropy binary digits. Book version: P1.0—February 2014
To the loving memory of my grandmothers, Kuppammal and Jayalakshmi. I cherish my wonder years under your care.
Contents Foreword . . . . . . . . . . . . . . xi Acknowledgments . . . . . . . . . . . xiii Preface . . . . . . . . . . . . . . xv 1. Hello, Lambda Expressions! . . . . . . . . . 1 Change the Way You Think 1 The Big Gains of Functional-Style Code 5 Why Code in the Functional Style? 6 Evolution, Not Revolution 12 A Little Sugar to Sweeten 15 Recap 17 2. Using Collections . . . . . . . . . . . 19 Iterating through a List 19 Transforming a List 23 Finding Elements 26 Reusing Lambda Expressions 27 Using Lexical Scoping and Closures 29 Picking an Element 33 Reducing a Collection to a Single Value 35 Joining Elements 38 Recap 40 3. Strings, Comparators, and Filters . . . . . . . 41 Iterating a String 41 Implementing the Comparator Interface 44 Multiple and Fluent Comparisons 51 Using the collect Method and the Collectors Class 52 Listing All Files in a Directory 56 Listing Select Files in a Directory 57
Listing Immediate Subdirectories Using flatMap 59 Watching a File Change 60 Recap 62 4. Designing with Lambda Expressions . . . . . . . 63 Separating Concerns Using Lambda Expressions 63 Delegating Using Lambda Expressions 68 Decorating Using Lambda Expressions 72 A Peek into the default Methods 77 Creating Fluent Interfaces Using Lambda Expressions 80 Dealing with Exceptions 83 Recap 86 5. Working with Resources . . . . . . . . . . 87 Cleaning Up Resources 87 Using Lambda Expressions to Clean Up Resources 91 Managing Locks 95 Creating Concise Exception Tests 97 Recap 101 6. Being Lazy . . . . . . . . . . . . . 103 Delayed Initialization 103 Lazy Evaluations 108 Leveraging the Laziness of Streams 111 Creating Infinite, Lazy Collections 115 Recap 119 7. Optimizing Recursions . . . . . . . . . . 121 Using Tail-Call Optimization 121 Speeding Up with Memoization 129 Recap 134 8. Composing with Lambda Expressions . . . . . . 135 Using Function Composition 135 Using MapReduce 138 Taking a Leap to Parallelize 143 Recap 145 9. Bringing It All Together . . . . . . . . . 147 Essential Practices to Succeed with the Functional Style 147 Performance Concerns 151 Adopting the Functional Style 153 Contents • viii
A1. Starter Set of Functional Interfaces . . . . . . . 155 A2. Syntax Overview . . . . . . . . . . . 157 A3. Web Resources . . . . . . . . . . . . 163 Bibliography . . . . . . . . . . . . 165 Index . . . . . . . . . . . . . . 167 Contents • ix
Foreword Venkat Subramaniam would never be described as a “waterfall” sort of guy. So, when he mentioned that he was starting on a Java 8 book—long before the design of Java 8 was settled—I was not at all surprised. It was clear this was going to be an “agile” book project. Despite having to more than occasionally rework the text as the language and library features evolved, Venkat had a secret advantage—he knew where we were going. The Java 8 design was heavily influenced by the core principles of functional programming: functions as values, immutability, and statelessness. We didn’t do this because functional programming is trendy or cool; we did it because programs that are expressed as stateless transformations on immutable data, rather than as modifications of mutable data structures, tend to be easier to read and main- tain, to be less error prone, and to parallelize more gracefully. The new features introduced in Java 8 were designed together to facilitate devel- opment of more expressive and parallel-friendly libraries. Lambda expressions reduce the syntactic overhead of encoding behavior as data; default methods allow existing libraries to evolve over time, enabling core JDK classes such as Collections to take advantage of lambda expressions; the java.util.stream package leverages these language features to offer rich aggregate operations on collections, arrays, and other data sources in a declarative, expressive, and parallel-friendly manner. All of this adds up to more powerful and performant code, as well as a more pleasant programming experience. This book not only provides lots of examples of how to use these new features, but offers readers a peek into the underlying principles behind their design, and why they result in better code. Let Venkat be your guide to this new and improved Java—you’re in for a treat. Brian Goetz Java Language Architect, Oracle Corporation September 2013 report erratum • discuss
Acknowledgments Writing a book is like taking a road trip—we know where we’re heading, but some details emerge only after the start. The journey may involve detours— opportunities to explore unexpected places—and it’s more fun with good company. I’m thankful for the great company of people on this voyage: smart reviewers, an amazing editor, a wonderful set of people at The Pragmatic Bookshelf, and a very supportive family. I first thank the Java language team members at Oracle for their hard work to bring the functional style of programming to one of the most popular mainstream languages. You’ve taken the language in the right direction—not through shortcuts and quick fixes, but by way of sound reasoning and prudent design decisions. Kudos, team. I express my sincere gratitude to the smart developers who volunteered their personal time to review this book. Thank you, Kimberly Barnes, Fred Daoud, Raju Gandhi, Marty Hall, Praveen Kumar, Rebecca Parsons, Kirk Pepperdine, Chris Richardson, Ian Roughley, Nate Schutta, Ken Sipe, and Dan Sline. Your comments were critical yet very constructive and motivational; they helped make this book better. I’m honored and humbled by Bruce Tate reviewing this book. He’s been a great mentor for me over the years. He reviewed this book multiple times, at different stages, and took the time to motivate me as to why certain changes were essential. Thank you, Bruce. I’d also like to express my gratitude to Brian Goetz for reviewing the book multiple times, for the encouragement starting early on, and for kindly agreeing to write the foreword. Any errors in the book are solely mine. The biggest benefit of publishing a beta copy of a book is the opportunity to improve it based on valuable feedback from early readers. I’m indebted to the following readers for taking their valuable time to provide feedback on the forum for this book or reporting errors on the errata page. Thank you, Greg Helton, Günter Jantzen, Narayanan Jayaratchagan, Wacek Kusnierczyk, Nabeel Ali Memon, Marc-Daniel Ortega, Arjo Ouwens, Philip Schwarz, report erratum • discuss
Ekaterina Sh, Dan Talpau, Benjamin Tan, Brian Tarbox, Marco Vermeulen, and Jason Weden. I benefited greatly from every interaction with Jackie Carter. She did not just edit; she motivated me and helped shape this book. Thank you, Jackie, for your hard work, fast responses, and truly making this a fun experience. Impatience is one of my many weaknesses, but the kind folks at The Pragmatic Bookshelf turned that into a strength when I set out to write this book. Susannah Pfalzer, Andy Hunt, Dave Thomas, and several others worked behind the scenes to help me succeed on a self-imposed fast-paced schedule. You make writing books so much easier and more fun, much more than you may realize. I have enjoyed the privilege of being part of various conferences, and the No Fluff Just Stuff (NFJS) conference series in particular. I have learned a lot from the interactions I’ve had with other speakers and developers. Special thanks to my friends on the conference circuit, especially Jay Zimmerman, director of NFJS. I thank my wife Kavitha and sons Karthik and Krupa for letting me hide in my office during the holidays to work on this book. It was a true pleasure to have their support and above all to see their curiousness. Special thanks to my parents for their blessings and encouragement. Acknowledgments • xiv report erratum • discuss
In any field, find the strangest thing and then explore it. ➤ John Archibald Wheeler Preface You’re in for a treat. One of the most prominent and widely used languages in the world has evolved. Until now Java gave us one set of tools—the object- oriented paradigm—and we did the best we could with it. Now there’s another, more elegant way to solve the common problems we encounter when develop- ing applications. We can now do quite effectively in Java what was previously possible only on the JVM using other languages—this means more power to Java programmers. I’m thankful to have had the privilege over the past few decades to program with multiple languages: C, C++, Java, C#, F#, Ruby, Groovy, Scala, Clojure, Erlang, JavaScript… When asked which one’s my favorite, my resounding answer has been that it’s not the language that excites me, but the way we program. The science and engineering in programming drew me in, but the art in pro- gramming keeps me. Coding has a lot in common with writing—there’s more than one way to express our ideas. Java helped us write code using objects. Now we have an additional way to implement our designs and ideas. This is a new way in Java, one that will make our code more expressive, easier to write, less error prone, and easier to parallelize than has been the case with Java until now. This way has been around for decades and widely used in languages like Lisp, Clojure, Erlang, Smalltalk, Scala, Groovy, and Ruby. It’s not only a new way in Java, but a better way. Since coding is like writing, we can learn a few things from that field. In On Writing Well [Zin01], William Zinsser recommends simplicity, clarity, and brevity. To create better applications, we can start by making the code simpler, clear, and concise. The new style of programming in Java lets us do exactly that, as we will explore throughout this book. report erratum • discuss
Who’s This Book For This book is for programmers well versed in object-oriented programming in Java and keen to learn and apply the new facilities of lambda expressions. You’ll need good experience programming in previous versions of Java, espe- cially Java 5, to make the best use of this book. Programmers mostly interested in JVM languages like Scala, Groovy, JRuby, and Clojure can benefit from the examples in this book and can relate back to the facilities offered in those languages. They can also use the examples to help fellow Java programmers on their teams. Programmers experienced with the functional style of programming in other languages and who are now involved in Java projects can use this book, as well. They can learn how what they know translates to the specifics of the lambda expressions’ usage in Java. Programmers who are familiar with lambda expressions in Java can use this book to help coach and train their team members who are getting up to speed in this area. What’s in This Book This book will help you get up to speed with Java 8 lambda expressions, to think in the elegant style, and to benefit from the additions to the Java Development Kit (JDK) library. We’ll take an example-driven approach to exploring the concepts. Rather than discuss the theory of functional program- ming, we’ll dive into specific day-to-day tasks to apply the elegant style. This approach will quickly get these concepts under our belts so we can put them to real use on projects right away. On the first read, take the time to go over the chapters sequentially as we build upon previously discussed concepts and examples. Each chapter closes with a quick summary to recap what was covered. Later, when working on applications, take a quick glance at any relevant example or section in the book. There’s also a syntax appendix for quick reference. Here’s how the rest of the book is organized: We discuss the functional style of programming, its benefits, and how it differs from the prevalent imperative style in Chapter 1, Hello, Lambda Expressions!, on page 1. We also look into how Java supports lambda expressions in this chapter. Preface • xvi report erratum • discuss
The JDK collections have received some special treatment in Java 8, with new interfaces, classes, and methods that support functional-style operations. We will explore these in Chapter 2, Using Collections, on page 19. In Chapter 3, Strings, Comparators, and Filters, on page 41, we exploit func- tional-style and lambda expressions to work with strings, implement the Comparator interface, and use filters for file selection. In addition to using the functional-style facilities in the JDK, we can benefit from applying the elegant style in the design of methods and classes we create. We’ll cover functional-style design techniques in Chapter 4, Designing with Lambda Expressions, on page 63. The lambda expressions facilitate a code structure that helps delineate oper- ations to manage object lifetimes and resource cleanup, as we’ll discuss in Chapter 5, Working with Resources, on page 87. We’ll see lambda expressions shine in Chapter 6, Being Lazy, on page 103; they provide us the ability to postpone instance creation and method evalua- tions as well as create infinite lazy collections, and thereby improve the code’s performance. In Chapter 7, Optimizing Recursions, on page 121, we will use lambda expressions to optimize recursions and achieve stellar performance using memoization techniques. We’ll put the techniques we cover in the book to some real use in Chapter 8, Composing with Lambda Expressions, on page 135, where we’ll transform objects, implement MapReduce, and safely parallelize a program with little effort. In Chapter 9, Bringing It All Together, on page 147, we’ll go over the key concepts and the practices needed to adopt those techniques. In Appendix 1, Starter Set of Functional Interfaces, on page 155, we’ll take a glance at some of the most popular functional interfaces. A quick overview of the Java 8 syntax for functional interfaces, lambda expressions, and method/constructor references is in Appendix 2, Syntax Overview, on page 157. The URLs mentioned throughout this book are gathered together for your convenience in Appendix 3, Web Resources, on page 163. report erratum • discuss What’s in This Book • xvii
Java Version Used in This Book To run the examples in this book you need Java 8 with support for lambda expressions. Using automated scripts, the examples in this book have been tried out with the following version of Java: java version "1.8.0" Java(TM) SE Runtime Environment (build 1.8.0-b128) Java HotSpot(TM) 64-Bit Server VM (build 25.0-b69, mixed mode) Take a few minutes to download the appropriate version of Java for your system. This will help you follow along with the examples in this book. How to Read the Code Examples When writing code in Java, we place classes in packages, and executable state- ments and expressions in methods. To reduce clutter, we’ll skip the package names and imports in the code listings. All code in this book belongs to a package: package fpij; Any executable code not listed within a method is part of an undisplayed main() method. When going through the code listings, if you have an urge to look at the full source code, remember it’s only a click away at the website for this book. Online Resources A number of web resources referenced throughout the book are collected in Appendix 3, Web Resources, on page 163. Here are a few that will help you get started with this book: The Oracle website for downloading the version of Java used in this book is https://jdk8.java.net/download.html. The JDK documentation is available at http://download.java.net/jdk8/docs/api. This book’s page at the Pragmatic Bookshelf website is http://www.pragprog.com/ titles/vsjava8. From there you can download all the example source code for the book. You can also provide feedback by submitting errata entries or posting your comments and questions in the forum. If you’re reading the book in PDF form, you can click on the link above a code listing to view or download the specific examples. Now for some fun with lambda expressions… Venkat Subramaniam February 2014 Preface • xviii report erratum • discuss
CHAPTER 1 There are two ways of constructing a software design: One way is to make it so simple that there are obviously no deficiencies, and the other way is to make it so complicated that there are no obvious deficiencies. The first method is far more difficult.1 ➤ Sir Charles Antony Richard Hoare Hello, Lambda Expressions! Our Java coding style is ready for a remarkable makeover. The everyday tasks we perform just got simpler, easier, and more expressive. The new way of programming in Java has been around for decades in other languages. With these facilities in Java we can write concise, elegant, and expressive code with fewer errors. We can use this to easily enforce policies and implement common design patterns with fewer lines of code. In this book we’ll explore the functional style of programming using direct examples of everyday tasks we do as programmers. Before we take the leap to this elegant style, and this new way to design and program, let’s discuss why it’s better. Change the Way You Think Imperative style—that’s what Java has provided us since its inception. In this style, we tell Java every step of what we want it to do and then we watch it faithfully exercise those steps. That’s worked fine, but it’s a bit low level. The code tends to get verbose, and we often wish the language were a tad more intelligent; we could then tell it—declaratively—what we want rather than delve into how to do it. Thankfully, Java can now help us do that. Let’s look at a few examples to see the benefits and the differences in style. The Habitual Way Let’s start on familiar ground to see the two paradigms in action. Here’s an imperative way to find if Chicago is in a collection of given cities—remember, the 1. Hoare, Charles Antony Richard, "The Emperor’s Old Clothes," Communications of the ACM 24, no. 2 (February 1981): 5–83, doi:10.1145/358549.358561. report erratum • discuss
listings in this book only have snippets of code (see How to Read the Code Examples, on page xviii). introduction/fpij/Cities.java boolean found = false; for(String city : cities) { if(city.equals("Chicago")) { found = true; break; } } System.out.println("Found chicago?:" + found); This imperative version is noisy and low level; it has several moving parts. We first initialize a smelly boolean flag named found and then walk through each element in the collection. If we found the city we’re looking for, then we set the flag and break out of the loop. Finally we print out the result of our finding. A Better Way As observant Java programmers, the minute we set our eyes on this code we’d quickly turn it into something more concise and easier to read, like this: introduction/fpij/Cities.java System.out.println("Found chicago?:" + cities.contains("Chicago")); That’s one example of declarative style—the contains() method helped us get directly to our business. Tangible Improvements That change improved our code in quite a few ways: • No messing around with mutable variables • Iteration steps wrapped under the hood • Less clutter • Better clarity; retains our focus • Less impedance; code closely trails the business intent • Less error prone • Easier to understand and maintain Beyond Simple Cases That was simple—the declarative function to check if an element is present in a collection has been around in Java for a very long time. Now imagine not having to write imperative code for more advanced operations, like parsing Chapter 1. Hello, Lambda Expressions! • 2 report erratum • discuss
files, working with databases, making calls to web services, programming concurrency, and so on. Java now makes it possible to write concise, elegant, less error-prone code, not just for simple cases, but throughout our applications. The Old Way Let’s look at another example. We’ll define a collection of prices and try out a few ways to total discounted price values. final List<BigDecimal> prices = Arrays.asList( new BigDecimal("10"), new BigDecimal("30"), new BigDecimal("17"), new BigDecimal("20"), new BigDecimal("15"), new BigDecimal("18"), new BigDecimal("45"), new BigDecimal("12")); Suppose we’re asked to total the prices greater than $20, discounted by 10%. Let’s do that in the habitual Java way first. introduction/fpij/DiscountImperative.java BigDecimal totalOfDiscountedPrices = BigDecimal.ZERO; for(BigDecimal price : prices) { if(price.compareTo(BigDecimal.valueOf(20)) > 0) totalOfDiscountedPrices = totalOfDiscountedPrices.add(price.multiply(BigDecimal.valueOf(0.9))); } System.out.println("Total of discounted prices: " + totalOfDiscountedPrices); That’s familiar code; we start with a mutable variable to hold the total of the discounted prices. We then loop through the prices, pick each price greater than $20, compute each item’s discounted value, and add those to the total. Finally we print the total value of the discounted prices. And here’s the output from the code. Total of discounted prices: 67.5 It worked, but writing it feels dirty. It’s no fault of ours; we had to use what was available. But the code is fairly low level—it suffers from “primitive obsession” and defies the single-responsibility principle. Those of us working from home have to keep this code away from the eyes of kids aspiring to be programmers, for they may be dismayed and sigh, “That’s what you do for a living?” A Better Way, Again Now we can do better—a lot better. Our code can resemble the requirement specification. This will help reduce the gap between the business needs and report erratum • discuss Change the Way You Think • 3
the code that implements it, further reducing the chances of the requirements being misinterpreted. Rather than tell Java to create a mutable variable and then to repeatedly assign to it, let’s talk with it at a higher level of abstraction, as in the next code. introduction/fpij/DiscountFunctional.java final BigDecimal totalOfDiscountedPrices = prices.stream() .filter(price -> price.compareTo(BigDecimal.valueOf(20)) > 0) .map(price -> price.multiply(BigDecimal.valueOf(0.9))) .reduce(BigDecimal.ZERO, BigDecimal::add); System.out.println("Total of discounted prices: " + totalOfDiscountedPrices); Let’s read that aloud—filter prices greater than $20, map the prices to dis- counted values, and then add them up. The code flows along with logic in the same way we’d describe the requirements. As a convention in Java, we wrap long lines of code and line up the dots before the method names, as in the previous example. The code is concise, but we’re using quite a number of new things from Java 8. First, we invoked a stream() method on the prices list. This opens the door to a special iterator with a wealth of convenience functions, which we’ll discuss later. Instead of explicitly iterating through the prices list, we’re using a few special methods, such as filter() and map(). Unlike the methods we’re used to in Java and the Java Development Kit (JDK), these methods take an anonymous function—a lambda expression—as a parameter, within the parentheses (). (We’ll soon explore this further.) We invoke the reduce() method to compute the total on the result of the map() method. The looping is concealed much like it was under the contains() method. The map() method (and the filter() method), however, is more sophisticated. For each price in the prices list, it invokes the provided lambda expression and puts the responses from these calls into a new collection. The reduce() method is invoked on this collection to get the final result. Here’s the output from this version of code: Total of discounted prices: 67.5 The Improvements This is quite an improvement from the habitual way: Chapter 1. Hello, Lambda Expressions! • 4 report erratum • discuss
Comments 0
Loading comments...
Reply to Comment
Edit Comment