Java 8 in Action
Lambdas, streams, and functional-style programming
Raoul-Gabriel Urma, Mario Fusco, and Alan Mycroft
  • August 2014
  • ISBN 9781617291999
  • 424 pages

A great and concise guide to what’s new in Java8, with plenty of examples to get you going in a hurry.

Jason Lee, Oracle

Java 8 in Action is a clearly written guide to the new features of Java 8. The book covers lambdas, streams, and functional-style programming. With Java 8's functional features you can now write more concise code in less time, and also automatically benefit from multicore architectures. It's time to dig in!

About the book

Every new version of Java is important, but Java 8 is a game changer. Java 8 in Action is a clearly written guide to the new features of Java 8. It begins with a practical introduction to lambdas, using real-world Java code. Next, it covers the new Streams API and shows how you can use it to make collection-based code radically easier to understand and maintain. It also explains other major Java 8 features including default methods, Optional, CompletableFuture, and the new Date and Time API.

Table of Contents detailed table of contents



about this book

about the authors

about the cover illustration

Part 1 Fundamentals

1. Java 8: why should you care?

1.1. Why is Java still changing?

1.1.1. Java’s place in the programming language ecosystem

1.1.2. Stream processing

1.1.3. Passing code to methods with behavior parameterization

1.1.4. Parallelism and shared mutable data

1.1.5. Java needs to evolve

1.2. Functions in Java

1.2.1. Methods and lambdas as first-class citizens

1.2.2. Passing code: an example

1.2.3. From passing methods to lambdas

1.3. Streams

1.3.1. Multithreading is difficult

1.4. Default methods

1.5. Other good ideas from functional programming

1.6. Summary

2. Passing code with behavior parameterization

2.1. Coping with changing requirements

2.1.1. First attempt: filtering green apples

2.1.2. Second attempt: parameterizing the color

2.1.3. Third attempt: filtering with every attribute you can think of

2.2. Behavior parameterization

2.2.1. Fourth attempt: filtering by abstract criteria

2.3. Tackling verbosity

2.3.1. Anonymous classes

2.3.2. Fifth attempt: using an anonymous class

2.3.3. Sixth attempt: using a lambda expression

2.3.4. Seventh attempt: abstracting over List type

2.4. Real-world examples

2.4.1. Sorting with a Comparator

2.4.2. Executing a block of code with Runnable

2.4.3. GUI event handling

2.5. Summary

3. Lambda expressions

3.1. Lambdas in a nutshell

3.2. Where and how to use lambdas

3.2.1. Functional interface

3.2.2. Function descriptor

3.3. Putting lambdas into practice: the execute around pattern

3.3.1. Step 1: Remember behavior parameterization

3.3.2. Step 2: Use a functional interface to pass behaviors

3.3.3. Step 3: Execute a behavior!

3.3.4. Step 4: Pass lambdas

3.4. Using functional interfaces

3.4.1. Predicate

3.4.2. Consumer

3.4.3. Function

3.5. Type checking, type inference, and restrictions

3.5.1. Type checking

3.5.2. Same lambda, different functional interfaces

3.5.3. Type inference

3.5.4. Using local variables

3.6. Method references

3.6.1. In a nutshell

3.6.2. Constructor references

3.7. Putting lambdas and method references into practice!

3.7.1. Step 1: Pass code

3.7.2. Step 2: Use an anonymous class

3.7.3. Step 3: Use lambda expressions

3.7.4. Step 4: Use method references

3.8. Useful methods to compose lambda expressions

3.8.1. Composing Comparators

3.8.2. Composing Predicates

3.8.3. Composing Functions

3.9. Similar ideas from mathematics

3.9.1. Integration

3.9.2. Connecting to Java 8 lambdas

3.10. Summary

Part 2 Functional-style data processing

4. Introducing streams

4.1. What are streams?

4.2. Getting started with streams

4.3. Streams vs. collections

4.3.1. Traversable only once

4.3.2. External vs. internal iteration

4.4. Stream operations

4.4.1. Intermediate operations

4.4.2. Terminal operations

4.4.3. Working with streams

4.5. Summary

5. Working with streams

5.1. Filtering and slicing

5.1.1. Filtering with a predicate

5.1.2. Filtering unique elements

5.1.3. Truncating a stream

5.1.4. Skipping elements

5.2. Mapping

5.2.1. Applying a function to each element of a stream

5.2.2. Flattening streams

5.3. Finding and matching

5.3.1. Checking to see if a predicate matches at least one element

5.3.2. Checking to see if a predicate matches all elements

5.3.3. Finding an element

5.3.4. Finding the first element

5.4. Reducing

5.4.1. Summing the elements

5.4.2. Maximum and minimum

5.5. Putting it all into practice

5.5.1. The domain: Traders and Transactions

5.5.2. Solutions

5.6. Numeric streams

5.6.1. Primitive stream specializations

5.6.2. Numeric ranges

5.6.3. Putting numerical streams into practice: Pythagorean triples

5.7. Building streams

5.7.1. Streams from values

5.7.2. Streams from arrays

5.7.3. Streams from files

5.7.4. Streams from functions: creating infinite streams!

5.8. Summary

6. Collecting data with streams

6.1. Collectors in a nutshell

6.1.1. Collectors as advanced reductions

6.1.2. Predefined collectors

6.2. Reducing and summarizing

6.2.1. Finding maximum and minimum in a stream of values

6.2.2. Summarization

6.2.3. Joining Strings

6.2.4. Generalized summarization with reduction

6.3. Grouping

6.3.1. Multilevel grouping

6.3.2. Collecting data in subgroups

6.4. Partitioning

6.4.1. Advantages of partitioning

6.4.2. Partitioning numbers into prime and nonprime

6.5. The Collector interface

6.5.1. Making sense of the methods declared by Collector interface

6.5.2. Putting them all together

6.6. Developing your own collector for better performance

6.6.1. Divide only by prime numbers

6.6.2. Comparing collectors' performances

6.7. Summary

7. Parallel data processing and performance

7.1. Parallel streams

7.1.1. Turning a sequential stream into a parallel one

7.1.2. Measuring stream performance

7.1.3. Using parallel streams correctly

7.1.4. Using parallel streams effectively

7.2. The fork/join framework

7.2.1. Working with RecursiveTask

7.2.2. Best practices for using the fork/join framework

7.2.3. Work stealing

7.3. Spliterator

7.3.1. The splitting process

7.3.2. Implementing your own Spliterator

7.4. Summary

Part 3 Effective Java 8 programming

8. Refactoring, testing, and debugging

8.1. Refactoring for improved readability and flexibility

8.1.1. Improving code readability

8.1.2. From anonymous classes to lambda expressions

8.1.3. From lambda expressions to method references

8.1.4. From imperative data processing to Streams

8.1.5. Improving code flexibility

8.2. Refactoring object-oriented design patterns with lambdas

8.2.1. Strategy

8.2.2. Template method

8.2.3. Observer

8.2.4. Chain of responsibility

8.2.5. Factory

8.3. Testing lambdas

8.3.1. Testing the behavior of a visible lambda

8.3.2. Focusing on the behavior of the method using a lambda

8.3.3. Pulling complex lambdas into separate methods

8.3.4. Testing high-order functions

8.4. Debugging

8.4.1. Examining the stack trace

8.4.2. Logging information

8.5. Summary

9. Default methods

9.1. Evolving APIs

9.1.1. API version 1

9.1.2. API version 2

9.2. Default methods in a nutshell

9.3. Usage patterns for default methods

9.3.1. Optional methods

9.3.2. Multiple inheritance of behavior

9.4. Resolution rules

9.4.1. Three resolution rules to know

9.4.2. Most specific default-providing interface wins

9.4.3. Conflicts and explicit disambiguation

9.4.4. Diamond problem

9.5. Summary

10. Using Optional as a better alternative to null

10.1. How do you model the absence of a value?

10.1.1. Reducing NullPointerExceptions with defensive checking

10.1.2. Problems with null

10.1.3. What are the alternatives to null in other languages?

10.2. Introducing the Optional class

10.3. Patterns for adopting Optional

10.3.1. Creating Optional objects

10.3.2. Extracting and transforming values from optionals with map

10.3.3. Chaining Optional objects with flatMap

10.3.4. Default actions and unwrapping an optional

10.3.5. Combining two optionals

10.3.6. Rejecting certain values with filter

10.4. Practical examples of using Optional

10.4.1. Wrapping a potentially null value in an optional

10.4.2. Exceptions vs. Optional

10.4.3. Putting it all together

10.5. Summary

11. CompletableFuture: composable asynchronous programming

11.1. Futures

11.1.1. Futures limitations

11.1.2. Using CompletableFutures to build an asynchronous application

11.2. Implementing an asynchronous API

11.2.1. Converting a synchronous method into an asynchronous one

11.2.2. Dealing with errors

11.3. Make your code non-blocking

11.3.1. Parallelizing requests using a parallel Stream

11.3.2. Making asynchronous requests with CompletableFutures

11.3.3. Looking for the solution that scales better

11.3.4. Using a custom Executor

11.4. Pipelining asynchronous tasks

11.4.1. Implementing a discount service

11.4.2. Using the Discount service

11.4.3. Composing synchronous and asynchronous operations

11.4.4. Combining two CompletableFutures—dependent and independent

11.4.5. Reflecting on Future vs. CompletableFuture

11.5. Reacting to a CompletableFuture completion

11.5.1. Refactoring the best-price-finder application

11.5.2. Putting it to work

11.6. Summary

12. New Date and Time API

12.1. LocalDate, LocalTime, Instant, Duration, and Period

12.1.1. Working with LocalDate and LocalTime

12.1.2. Combining a date and a time

12.1.3. Instant: a date and time for machines

12.1.4. Defining a Duration or a Period

12.2. Manipulating, parsing, and formatting dates

12.2.1. Working with TemporalAdjusters

12.2.2. Printing and parsing date-time objects

12.3. Working with different time zones and calendars

12.3.1. Fixed offset from UTC/Greenwich

12.3.2. Using alternative calendar systems

12.4. Summary

Part 4 Beyond Java

13. Thinking functionally

13.1. Implementing and maintaining systems

13.1.1. Shared mutable data

13.1.2. Declarative programming

13.1.3. Why functional programming?

13.2. What’s functional programming?

13.2.1. Functional-style Java

13.2.2. Referential transparency

13.2.3. Object-oriented vs. functional-style programming

13.2.4. Functional style in practice

13.3. Recursion vs. iteration

13.4. Summary

14. Functional programming techniques

14.1. Functions everywhere

14.1.1. Higher-order functions

14.1.2. Currying

14.2. Persistent data structures

14.2.1. Destructive updates vs. functional

14.2.2. Another example with Trees

14.2.3. Using a functional approach

14.3. Lazy evaluation with streams

14.3.1. Self-defining stream

14.3.2. Your own lazy list

14.4. Pattern matching

14.4.1. Visitor design pattern

14.4.2. Pattern matching to the rescue

14.5. Miscellany

14.5.1. Caching or memoization

14.5.2. What does "return the same object" mean?

14.5.3. Combinators

14.6. Summary

15. Blending OOP and FP: comparing Java 8 and Scala

15.1. Introduction to Scala

15.1.1. Hello beer

15.1.2. Basic data structures: List, Set, Map, Tuple, Stream, Option

15.2. Functions

15.2.1. First-class functions in Scala

15.2.2. Anonymous functions and closures

15.2.3. Currying

15.3. Classes and traits

15.3.1. Less verbosity with Scala classes

15.3.2. Scala traits vs. Java 8 interfaces

15.4. Summary

16. Conclusions and where next for Java

16.1. Review of Java 8 features

16.1.1. Behavior parameterization (lambdas and method references)

16.1.2. Streams

16.1.3. CompletableFuture

16.1.4. Optional

16.1.5. Default methods

16.2. What’s ahead for Java?

16.2.1. Collections

16.2.2. Type system enhancements

16.2.3. Pattern matching

16.2.4. Richer forms of generics

16.2.5. Deeper support for immutability

16.2.6. Value types

16.3. The final word

Appendix A: Miscellaneous language updates

A.1. Annotations

A.1.1. Repeated annotations

A.1.2. Type annotations

A.2. Generalized target-type inference

Appendix B: Miscellaneous library updates

B.1. Collections

B.1.1. Additional methods

B.1.2. The Collections class

B.1.3. Comparator

B.2. Concurrency

B.2.1. Atomic

B.2.2. ConcurrentHashMap

B.3. Arrays

B.3.1. Using parallelSort

B.3.2. Using setAll and parallelSetAll

B.3.3. Using parallelPrefix

B.4. Number and Math

B.4.1. Number

B.4.2. Math

B.5. Files

B.6. Reflection

B.7. String

Appendix C: Performing multiple operations in parallel on a stream

C.1. Forking a stream

C.1.1. Implementing the Results interface with the ForkingStreamConsumer

C.1.2. Developing the ForkingStreamConsumer and the BlockingQueueSpliterator

C.1.3. Putting the StreamForker to work

C.2. Performance considerations

Appendix D: Lambdas and JVM bytecode

D.1. Anonymous classes

D.2. Bytecode generation

D.3. InvokeDynamic to the rescue

D.4. Code-generation strategies


What's inside

  • How to use Java 8's powerful new features
  • Writing effective multicore-ready applications
  • Refactoring, testing, and debugging
  • Adopting functional-style programming
  • Quizzes and quick-check questions

About the reader

This book is written for programmers familiar with Java and basic OO programming.

About the authors

Raoul-Gabriel Urma is a software engineer, speaker, trainer, and PhD candidate at the University of Cambridge. Mario Fusco is an engineer at Red Hat and creator of the lambdaj library. Alan Mycroft is a professor at Cambridge and cofounder of the Raspberry Pi Foundation.

placing your order...

Don't refresh or navigate away from the page.

FREE domestic shipping on three or more pBooks