Modern Java Recipes now in Early Release

My latest book, Modern Java Recipes, is now available in Early Release form! If you have a Safari account (the online book repository from O’Reilly), you can add it to your bookshelf at any time. Otherwise, you can get it directly from http://shop.oreilly.com or even at Amazon.com.

modern_java_recipes_early

There are only three chapters in the Early Release,  covering 17 recipes, but that’s a bit misleading. The book itself is probably about 85% finished, covering about 50 recipes of an anticipated 60 or so. The released chapters are the sections in the best shape at the moment, but expect several more to be added rapidly in the coming weeks. Frankly, I hope to finish the entire book within the next month, though that may be asking a lot.

(Nothing like committing to an ambitious deadline in public. Sigh. I’ve noticed that when I’m being productive (and today has been a pretty decent day), my estimates tend to assume that I’ll be able to continue at the same level of productivity indefinitely. Yeah, good luck with that.)

Let me give the book the best praise I can, which is to say it’s not bad, and parts of it are actually pretty decent. Most of it is being reviewed by tech experts at the moment (you know who you are), so the quality will undoubtedly improve as I incorporate their suggestions.

The goal of the book is very practical. I want Java developers to understand the new additions to Java that came in version 8 (and are coming in version 9) and how to apply them to their code. With that in mind, recipe titles include topics like, “Default Method Conflict” for when a class implements two interfaces that each have the same default method implementation, “Logging with a Supplier” that shows how the updated Logger class overloads the logging methods to use a Supplier in order to defer execution, and “Using a Generic Exception Wrapper”, which shows how to wrap lambda expressions that throw checked exceptions into a method that will catch them and rethrow them as unchecked.

Some of the examples are very basic, but many are more advanced and come from several sources, including authors I respect. I tried to give credit wherever that happened, but if you see something that isn’t sourced and you think it should be, please let me know. Now is when I’ll have an opportunity to make those sorts of updates.

Some recipes are the result of questions I’ve asked on Stack Overflow (mandatory joke seen on Twitter: the maintainers of Stack Overflow have the hardest job in I.T., because when it goes down, they have to fix it without accessing Stack Overflow). I have to mention that my good friend Tim Yates was so helpful there that I finally contacted him directly so I could ask questions on a regular basis. He kindly agreed to be one of the tech reviewers, and has improved every section he touched.

(At least so far. Who knows what his review will reveal? But no pressure…)

I’ll have a lot more to say about the book in the coming weeks. I hope to post about it here on a regular basis, discussing various recipes and other lessons I’ve learned while writing the book, as well as sharing any jokes that my editor makes me remove (yes, that’s a teaser for later).

In the meantime, here’s a quick sample, which (1) shows the style I’m trying to follow, and (2) gives me an excuse (and any excuse is a good one) to use sample names from Firefly:

Filter Data With Predicates

Problem

You want to implement the java.util.function.Predicate interface.

Solution

Implement the boolean test(T t) method in the Predicate interface using a lambda expression or a method reference.

Discussion

Predicates are used primarily to filter streams. Given a stream of items, the filter method in java.util.stream.Stream takes a Predicate and returns a new stream that includes only the items that match the given predicate.

The single abstract method in Predicate is boolean test(T t), which takes a single generic argument and returns true or false. The complete set of methods in Predicate, including state and defaults, is given by:

default Predicate<T> and(Predicate<? super T> other)
static <T> Predicate<T> isEquals(Object targetRef)
default Predicate<T> negate()
default Predicate<T> or(Predicate<? super T> other)
boolean test(T t)

Say you have a collection of names and you want to find all the instances that have a particular length. The next example shows how to use stream processing to do so.

public String getNamesOfLength5(String... names) {
    return Arrays.stream(names)
                 .filter(s -> s.length() == 5)
                 .collect(Collectors.joining(", "));

Alternatively, perhaps you want only the names that start with a particular letter:

public String getNamesStartingWithS(String... names) {
    return Arrays.stream(names)
        .filter(s -> s.startsWith("S"))
        .collect(Collectors.joining(", "));
}

Both of these examples have hard-wired values for the filter. It’s more likely that the condition will be specified by the client:

public String getNamesSatisfyingCondition(Predicate<String> condition, String... names) {
    return Arrays.stream(names)
        .filter(condition)
        .collect(Collectors.joining(", "));
}

This is quite flexible, but it may be a bit much to expect the client to write every predicate themselves. One option is to add constants to the class representing the most common cases.

public class ImplementPredicate {
    public static final Predicate<String> LENGTH_FIVE = s -> s.length() == 5;
    public static final Predicate<String> STARTS_WITH_S = s -> s.startsWith("S");

    // ... rest as before ...
}

The other advantage to supplying a predicate as an argument is that you can also use the default methods and, or, and negate to create a composite predicate from a series of individual elements.

This test case demonstrates all of these techniques.

import org.junit.Before;
import org.junit.Test;

import java.util.stream.Stream;

import static functionpackage.ImplementPredicate.*;
import static org.junit.Assert.assertEquals;

public class ImplementPredicateTest {
    private ImplementPredicate demo = new ImplementPredicate();
    private String[] names;

    @Before
    public void setUp() {
        names = Stream.of("Mal", "Wash", "Kaylee", "Inara", "Zoë",
                          "Jayne", "Simon", "River", "Shepherd Book")
                      .sorted()
                      .toArray(String[]::new);
}

    @Test
    public void getNamesOfLength5() throws Exception {
        assertEquals("Inara, Jayne, River, Simon",
            demo.getNamesOfLength5(names));
    }

    @Test
    public void getNamesStartingWithS() throws Exception {
        assertEquals("Shepherd Book, Simon", demo.getNamesStartingWithS(names));
    }

    @Test
    public void getNamesSatisfyingCondition() throws Exception {
        assertEquals("Inara, Jayne, River, Simon",
            demo.getNamesSatisfyingCondition(s -> s.length() == 5, names));
        assertEquals("Shepherd Book, Simon",
            demo.getNamesSatisfyingCondition(s -> s.startsWith("S"), names));
        assertEquals("Inara, Jayne, River, Simon",
            demo.getNamesSatisfyingCondition(LENGTH_FIVE, names));
        assertEquals("Shepherd Book, Simon",
            demo.getNamesSatisfyingCondition(STARTS_WITH_S, names));
    }

    @Test
    public void composedPredicate() throws Exception {
        assertEquals("Simon",
            demo.getNamesSatisfyingCondition(LENGTH_FIVE.and(STARTS_WITH_S), names));
        assertEquals("Inara, Jayne, River, Shepherd Book, Simon",
            demo.getNamesSatisfyingCondition(LENGTH_FIVE.or(STARTS_WITH_S), names));
        assertEquals("Kaylee, Mal, Shepherd Book, Wash, Zoë",
            demo.getNamesSatisfyingCondition(LENGTH_FIVE.negate(), names));
    }
}

Other methods in the standard library that use predicates include:

  • Optional.filter(Predicate predicate) — if a value is present, and the value matches the given predicate, return an optional describing the value, otherwise return an empty Optional
  • Collection.removeIf(Predicate filter) — removes all elements of this collection that satisfy the predicate
  • Stream.allMatch(Predicate predicate) — return true if all elements of the stream satisfy the given predicate. The methods anyMatch and noneMatch work similarly
  • Collectors.partitioningBy(Predicate predicate) — returns a Collector which splits a stream into two categories: those that satisfy the predicate and those that do not

Predicates are useful whenever a stream should only return certain elements. This recipe hopefully gives you an idea where and when that might be useful.

See Also

Closure composition is also discussed in [the section on closure composition].

 

 

%d bloggers like this: