Java 11 HttpClient, Gson, Gradle, and Modularization

This post describes a simple system that uses the new HttpClient class that comes with Java 11 to access a RESTful web service. After demonstrating basic functionality, changes are made to parse the results using Gson, and even modularize the code and have it work with Gradle. The goal is to provide an example for anyone who wants to do similar tasks with their own systems.

Java 11 is a Long Term Support (LTS) release for the Java standard edition, meaning bug fixes and security patches will be available for at least the next three years (see the extensive Java Is Still Free post at for details about the future licensing arrangements). Java 11 contains very few new features, however — many more were added in Java 9 and 10. Most of the industry did not upgrade during those releases, however. Now that another LTS release is available, that will likely change.

One of the few real additions to Java 11 is the HttpClient API, as described in JEP 321. This takes a related incubator project from Java 9 and standardizes it for Java 11. This blog post will not be an exhaustive demonstration of its capabilities, but it will show both a synchronous and asynchronous example of how to use it, along with JUnit 5 test cases and a Gradle build file, and even modularize the code.

The web service is provided by Open Notify, which is an open source project that provides a few links to data from NASA. In this case, the example uses the “number of people in space” link at http://api.open-notify.org/astros.json. The current response to that link is:

{"message": "success", 
 "people": [
    {"craft": "ISS", "name": "Oleg Artemyev"}, 
    {"craft": "ISS", "name": "Andrew Feustel"}, 
    {"craft": "ISS", "name": "Richard Arnold"}, 
    {"craft": "ISS", "name": "Sergey Prokopyev"}, 
    {"craft": "ISS", "name": "Alexander Gerst"}, 
    {"craft": "ISS", "name": "Serena Aunon-Chancellor"}], 
 "number": 6}

The root object contains attributes message, number, and people, the last of which is a collection of objects, each of which contains a name and a craft. As the response shows, at the moment there are six people aboard the International Space Station.

Even though the plan is to map that response to Java classes, at first the example will simply download the JSON data and write it as a string to the console.

The HttpClient class is in the java.net.http package. You create one using a builder, as in:

HttpClient client = HttpClient.newBuilder()
                              .version(HttpClient.Version.HTTP_2)
                              .connectTimeout(Duration.ofSeconds(2))
                              .build();

Two of the many configuration options are shown: using HTTP/2 (which is actually the default) and setting a two-second connection timeout period. Other options, like setting a proxy, following redirects, or using an authenticator are shown in the (early access) JavaDocs.

Requests can be sent synchronously or asynchronously as desired, using the send or sendAsync methods. Either way, the first argument to those methods is an instance of HttpRequest, which is also created using a builder:

String astroUrl = "http://api.open-notify.org/astros.json";
HttpRequest.newBuilder()
           .uri(URI.create(astroUrl))
           .GET()
           .build();

Sending a synchronous request also requires a “body handler”, which comes from the HttpResponse.BodyHandlers inner class factory methods. For this case, it’s easiest just to use the ofString method:

HttpResponse response = client.send(
    buildRequest(),
    HttpResponse.BodyHandlers.ofString());
String output = response.body();

From the HttpResponse you can get the headers, the body (as shown), the status code, or several other properties.

An asynchronous request is sent using the sendAsync method, which returns a CompletableFuture. Extracting the result can be done with code like:

String output = client.sendAsync(buildRequest(),
                                 HttpResponse.BodyHandlers.ofString())
                      .thenApply(HttpResponse::body)
                      .get();

The thenApply method from CompletableFuture takes a java.util.function.Function argument that in this case maps the response into a String containing the body. The subsequent get method blocks until the request and conversion has completed.

To summarize, here is the initial version of the class called AstroClient:

package astro;

import java.io.IOException;
import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
import java.time.Duration;
import java.util.concurrent.ExecutionException;

public class AstroClient {
    private HttpClient client = HttpClient.newBuilder()
                                          .version(HttpClient.Version.HTTP_2)
                                          .connectTimeout(Duration.ofSeconds(2))
                                          .build();

    private HttpRequest buildRequest() {
        String astroUrl = "http://api.open-notify.org/astros.json";
        return HttpRequest.newBuilder()
                          .uri(URI.create(astroUrl))
                          .GET()
                          .build();
    }

    public String getSync() throws IOException, InterruptedException {
        HttpResponse response = client.send(
                buildRequest(),
                HttpResponse.BodyHandlers.ofString());
        return response.body();
    }


    public String getAsync() throws ExecutionException, InterruptedException {
        return client.sendAsync(buildRequest(),
                                HttpResponse.BodyHandlers.ofString())
                     .thenApply(HttpResponse::body)
                     .get();
    }

}

Ultimately, the tests will use some cool new methods from JUnit 5, but for the moment if you just invoke the getSync and getAsync methods and log the output using a java.util.logging.Logger you get something like:


Sep 22, 2018 12:45:54 PM astro.AstroClientTest getSync
INFO: {"message": "success", "people": [{"craft": "ISS", "name": "Oleg Artemyev"}, {"craft": "ISS", "name": "Andrew Feustel"}, {"craft": "ISS", "name": "Richard Arnold"}, {"craft": "ISS", "name": "Sergey Prokopyev"}, {"craft": "ISS", "name": "Alexander Gerst"}, {"craft": "ISS", "name": "Serena Aunon-Chancellor"}], "number": 6}

Sep 22, 2018 12:45:54 PM astro.AstroClientTest getAsync
INFO: {"message": "success", "people": [{"craft": "ISS", "name": "Oleg Artemyev"}, {"craft": "ISS", "name": "Andrew Feustel"}, {"craft": "ISS", "name": "Richard Arnold"}, {"craft": "ISS", "name": "Sergey Prokopyev"}, {"craft": "ISS", "name": "Alexander Gerst"}, {"craft": "ISS", "name": "Serena Aunon-Chancellor"}], "number": 6}

Obviously we can do better if we parse the response. To do that, add the Google Gson dependency and use it to convert the JSON response to a pair of classes. The classes that map to the JSON structure are Assignment:

package astro.json;

public class Assignment {
    private String name;
    private String craft;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getCraft() {
        return craft;
    }

    public void setCraft(String craft) {
        this.craft = craft;
    }
}

which includes fields for the name and craft, and AstroResponse:

package astro.json;

import java.util.List;

public class AstroResponse {
    private String message;
    private Integer number;
    private List people;

    public String getMessage() {
        return message;
    }

    public void setMessage(String message) {
        this.message = message;
    }

    public Integer getNumber() {
        return number;
    }

    public void setNumber(Integer number) {
        this.number = number;
    }

    public List getPeople() {
        return people;
    }

    public void setPeople(List people) {
        this.people = people;
    }
}

These POJOs could be simplified by using

  • Lombok annotations
  • Groovy POGOs, preferably with the @Canonical annotation
  • Kotlin data classes

among other ways. Any of those would cut the POJOs down to just their attributes, but these are small enough to be readable even without that.

The changes to the AstroClient class are to add a method that converts the String response to an instance of AstroResponse:

package astro;

import astro.json.AstroResponse;
import com.google.gson.Gson;

import java.io.IOException;
import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
import java.time.Duration;
import java.util.concurrent.ExecutionException;

public class AstroClient {
    private HttpClient client = ...

    private Gson gson = new Gson();

    private HttpRequest buildRequest() {
        // ... as before ...
    }

    public AstroResponse getSync() throws IOException, InterruptedException {
        HttpResponse response = client.send(
                buildRequest(),
                HttpResponse.BodyHandlers.ofString());
        return getResponse(response.body());
    }


    public AstroResponse getAsync() throws ExecutionException, InterruptedException {
        String json = client.sendAsync(buildRequest(),
                                HttpResponse.BodyHandlers.ofString())
                     .thenApply(HttpResponse::body)
                     .get();
        return getResponse(json);
    }


    private AstroResponse getResponse(String json) {
        return gson.fromJson(json, AstroResponse.class);
    }
}

Now that the methods return an AstroResponse, the tests can get more interesting. Here is the complete AstroClientTest class:

package astro;

import astro.json.Assignment;
import astro.json.AstroResponse;
import org.junit.jupiter.api.Test;

import java.time.Duration;
import java.util.List;
import java.util.logging.Logger;

import static org.junit.jupiter.api.Assertions.*;

class AstroClientTest {
    private Logger logger = Logger.getLogger(AstroClientTest.class.getName());

    private AstroClient client = new AstroClient();

    @Test
    void getSync() {
        AstroResponse response = assertTimeoutPreemptively(
                Duration.ofSeconds(2),
                () -> client.getSync());

        int num = response.getNumber();
        List assignments = response.getPeople();

        assertEquals("success", response.getMessage());
        assertEquals(num, assignments.size());
        assignments.forEach(assignment ->
           assertAll(() -> assertTrue(assignment.getName().length() > 0),
                     () -> assertTrue(assignment.getCraft().length() > 0)));

        logResponse(num, assignments);
    }

    @Test
    void getAsync() {
        AstroResponse response = assertTimeoutPreemptively(
                Duration.ofSeconds(2),
                () -> client.getAsync());

        int num = response.getNumber();
        List assignments = response.getPeople();

        assertEquals("success", response.getMessage());
        assertEquals(num, assignments.size());

        logResponse(num, assignments);
    }

    private void logResponse(int num, List assignments) {
        logger.info(String.format("There are %d people in space", num));
        assignments.forEach(person -> logger.info(
                () -> String.format("%s aboard %s",
                                    person.getName(),
                                    person.getCraft())));
    }
}

The first test uses the neat assertTimeoutPreemtively method. That causes JUnit to spawn a separate thread for the test, which it interrupts if the test does not complete within the specified Duration. The second argument is a ThrowingSupplier, which is like a regular java.util.function.Supplier except that its single abstract method, get, is declared to throw Throwable.

After checking that the message field is “success” and that the size of the Assignment collection (called people to match the JSON attribute) has the right size, each individual Assignment is checked to ensure its name and craft fields are not just empty strings. The assertAll method in JUnit 5 takes a variable argument list of Executable instances, which are like Runnable instances but can throw Exception. The nice thing about assertAll is that all of its Executable arguments will be evaluated, even if some of them fail. The asynchronous test does the same thing.

The private logResponse method just uses the logger to write out how many people are in space and their names and crafts:


Sep 22, 2018 4:58:27 PM astro.AstroClientTest logResponse
INFO: There are 6 people in space
Sep 22, 2018 4:58:27 PM astro.AstroClientTest lambda$logResponse$6
INFO: Oleg Artemyev aboard ISS
Sep 22, 2018 4:58:27 PM astro.AstroClientTest lambda$logResponse$6
INFO: Andrew Feustel aboard ISS
Sep 22, 2018 4:58:27 PM astro.AstroClientTest lambda$logResponse$6
INFO: Richard Arnold aboard ISS
Sep 22, 2018 4:58:27 PM astro.AstroClientTest lambda$logResponse$6
INFO: Sergey Prokopyev aboard ISS
Sep 22, 2018 4:58:27 PM astro.AstroClientTest lambda$logResponse$6
INFO: Alexander Gerst aboard ISS
Sep 22, 2018 4:58:27 PM astro.AstroClientTest lambda$logResponse$6
INFO: Serena Aunon-Chancellor aboard ISS
Sep 22, 2018 4:58:28 PM astro.AstroClientTest logResponse

INFO: There are 6 people in space
Sep 22, 2018 4:58:28 PM astro.AstroClientTest lambda$logResponse$6
INFO: Oleg Artemyev aboard ISS
Sep 22, 2018 4:58:28 PM astro.AstroClientTest lambda$logResponse$6
INFO: Andrew Feustel aboard ISS
Sep 22, 2018 4:58:28 PM astro.AstroClientTest lambda$logResponse$6
INFO: Richard Arnold aboard ISS
Sep 22, 2018 4:58:28 PM astro.AstroClientTest lambda$logResponse$6
INFO: Sergey Prokopyev aboard ISS
Sep 22, 2018 4:58:28 PM astro.AstroClientTest lambda$logResponse$6
INFO: Alexander Gerst aboard ISS
Sep 22, 2018 4:58:28 PM astro.AstroClientTest lambda$logResponse$6
INFO: Serena Aunon-Chancellor aboard ISS

To cap it all off, here is the Gradle build file (using the current version of Gradle, 4.10.2):

plugins {
    id 'java'
}

group 'kousenit.com'
version '1.0'

sourceCompatibility = 11

repositories {
    jcenter()
}

dependencies {
    implementation 'com.google.code.gson:gson:2.8.5'
    
    testImplementation 'org.junit.jupiter:junit-jupiter-api:5.3.1'
    testRuntimeOnly 'org.junit.jupiter:junit-jupiter-engine:5.3.1'
}

test {
    useJUnitPlatform()
}

There’s nothing terribly surprising in that file to experienced Gradle users, though as a reminder the useJUnitPlatform method was added to Gradle to make it easier to run JUnit 5 tests.

Honestly, this is good enough. There’s no particular reason to go on and modularize this code; it all works fine on Java 11. However, just to show the kinds of changes needed if you’re foolish enough to add a module-info.java file to the class path, here’s what happens:

First, you need to add the necessary requires statements to the module-info.java file, which resides in the src/main/java directory:

module http.client.demo.main {
    requires java.net.http;
    requires gson;
}

The module name can be anything, but the same “reverse dns” pattern favored for packages is recommended. The name here is the one auto-generated by IntelliJ IDEA if you ask for a new module-info descriptor.

The HttpClient API was in an incubator module in Java 9, which was called jdk.incubator.httpclient. Now that it is standardized, it’s now in the java.net.http module, so that’s the module needed.

The hard part was figuring out how to refer to Gson. Since that API has not yet been modularized, the associated jar files wind up in the “unnamed” module and you need to refer to them by name rather than some official module name. Enough googling (no pun intended) and eventually you’ll find that the right name to use is “gson”. Of course, that’s fragile and will no doubt change at some point in the future.

If you’ve played with modules in Java 9 or 10, you’ll notice something conspicuous by its absence. Prior to Java 11, the Java util logger (the one in the java.util.logging package) was part of a separate module in the JDK called java.logging. In Java 11 that was moved back into java.base, so no additional requires statement is necessary.

The other necessary change is inside the Gradle build file. You have to tell Gradle to use the module path rather than the class path for compilation. Based on the Gradle Guide called Building Java 9 Modules, the following additional block was necessary:

compileJava {
    doFirst {
        options.compilerArgs = [
                '--module-path', classpath.asPath,
        ]
        classpath = files()
    }
}

This code adds a doFirst block to the compileJava task, which is a pre-processing step. It adds the --module-path compiler argument and assigns it to the jars on the classpath. Then the block sets the classpath variable to an empty collection of files.

The Gradle Guide also suggests that the compileTestJava and test tasks also need to be modified, but those turned out not to be necessary.

(Those are the only needed changes, which, looking back on them, was a bit disappointing given how much time it took to figure them out. But that’s no doubt a good thing.:))

The complete code is hosted in GitHub at https://github.com/kousen/http-client-demo . The master branch has everything, including the modularization.

As a reminder, I have a book that discusses the new features added to Java 8 (and much of Java 9) called Modern Java Recipes. The GitHub repositories for the book are https://github.com/kousen/java_8_recipes and https://github.com/kousen/java_9_recipes, both of which now build correctly under Java 11.

Java 8 Constructor Refs (In All Their Glory)

[Note: my last post announced my new book, Modern Java Recipes, is now available from O’Reilly publishers in Early Release form. As a sample, I included a discussion of the Predicate interface, one of the new functional interfaces in the the java.util.function package. In this post, I highlight constructor references, which are discussed in another recipe in the book.]

Problem

You want to instantiate an object using a method reference as part of a stream pipeline.

Solution

Use the new keyword as part of a method reference.

Discussion

When people talk about the new syntax added to Java 8, they mention lambda expressions, method references, and streams. For example, say you had a list of people and you wanted to convert it to a list of names. One way to do so would be:

List<String> names = people.stream()
    .map(person -> person.getName()) // lambda expression
    .collect(Collectors.toList());

In other words, take a list of Person instances, turn it into a stream, map each one to a String by invoking the getName() method on it, and collect them back into a List.

That works, but most developers would use a method reference instead:

List<String> names = people.stream()
    .map(Person::getName)           // method reference
    .collect(Collectors.toList());

The method reference is slightly shorter, and makes clear that the only thing being done to each person is transforming it using the getName method. Lambda expressions can be far more complicated and versatile. Method references are simple.

What if you want to go the other way? What if you have a list of strings and you want to create a list of Person references from it? In that case you can use a method reference again, but this time using the keyword new. That’s a constructor reference, which I would like to illustrate here.

First, here is the Person class, which is just about the simplest Plain Old Java Object (POJO) imaginable. All it does is wrap a simple string attribute called name.

public class Person {
    private String name;

    public Person() {}   // default constructor

    public Person(String name) {
        this.name = name;
    }

    public String getName() { return name; }
    public void setName(String name) { this.name = name; }

    // ... equals, hashCode, toString methods ...

Given a list of string names, I can map them to Person instances using the one-argument constructor.

List<String> names =  
    Arrays.asList("Grace Hopper", "Barbara Liskov", "Ada Lovelace",
        "Karen Spärck Jones");

List<Person> people =
    names.stream()
         .map(name -> new Person(name))  // lambda expression
         .collect(Collectors.toList());

Now instead of using the lambda expression that invokes the one-argument constructor directly, I can use a constructor reference instead.

names.stream()
     .map(Person::new)     // Constructor reference
     .collect(Collectors.toList());

Like all lambda expression or method references, context is everything. The map method is invoked on a stream of strings, so the Person::new reference is invoked on each string in the stream. The compiler recognizes that the Person class has a constructor that takes a single string, so it calls it. The default constructor is ignored.

Copy Constructors

To make things more interesting, I’ll add two additional constructors: a “copy constructor” that takes a Person argument, and one that takes a variable argument list of strings.

public Person(Person p) {   // copy constructor
    this.name = p.name;
}

public Person(String... names) {  // varargs constructor
    this.name = Arrays.stream(names)
                      .collect(Collectors.joining(" "));
}

The copy constructor makes a new Person from an existing Person instance. Say I defined a person, then used that person in a stream without mapping it, and then converted back into a collection. Would I still have the same person?

Person before = new Person("Grace Hopper");

List<Person> people = Stream.of(before)
        .collect(Collectors.toList());
Person after = people.get(0);

assertTrue(before == after);  // exact same object

before.setName("Grace Murray Hopper");  // Change name using 'before'
assertEquals("Grace Murray Hopper", after.getName()); // Same in 'after'

The point is, if I have a reference to Admiral Hopper before the stream operations and I didn’t map her to another object, I still have the same reference afterwards.

Using a copy constructor I can break that connection.

people = Stream.of(before)
        .map(Person::new)          // use copy constructor
        .collect(Collectors.toList());
after = people.get(0);
assertFalse(before == after);      // different objects
assertEquals(before, after);       // but equivalent

before.setName("Rear Admiral Dr. Grace Murray Hopper"); // Change using 'before'
assertFalse(before.equals(after)); // No longer the same in 'after'

This time, when invoking the map method, the context is a stream of Person instances. Therefore the Person::new syntax invokes the constructor that takes a Person and returns a new, but equivalent, instance. I’ve broken the connection between the before reference and the after reference.

(Btw, I mean no disrespect by treating Admiral Hopper as an object. I have no doubt she could still kick my a**, and she passed away in 1992.)

Varargs Constructors

The varargs constructor is invoked by the client by passing zero or more string arguments separated by commas. Inside the constructor, the names variable is treated like String[], a string array. The static stream method on the Arrays class is used to convert that into a stream, which is then turned into a single string by calling the collect method, whose argument comes from the convenient joining(String delimiter) method in the Collectors class.

How does that get invoked? Java includes a split method on String that takes a delimiter and returns a String array:

String[] split(String delimiter)

Since the variable argument list is equivalent to an array, I can use that method to invoke the varargs constructor.

names.stream()                     // Stream<String>
    .map(name -> name.split(" "))  // Stream<String[]>
    .map(Person::new)              // Stream<Person> using String... ctor
    .collect(Collectors.toList());

This time I map the strings to string arrays before invoking the constructor. Note that this is one of those times where I can’t use a method reference, because there’s no way using a method reference to supply the delimiter argument. I have to use the lambda expression instead.

Since the context after the first map is now a stream of string arrays, the Person::new constructor reference now uses the varargs constructor. If I add a print statement to that constructor:

System.out.println("Varargs ctor, names=" + Arrays.asList(names));

I can then see it in action:

Varargs ctor, names=[Grace, Hopper]
Varargs ctor, names=[Barbara, Liskov]
Varargs ctor, names=[Ada, Lovelace]
Varargs ctor, names=[Karen, Spärck, Jones]

Arrays

With constructor references, not only can you create instances, you can even create arrays. Say instead of returning a List, I wanted to return an array of person, Person[]. The Stream class has a method called, naturally enough, toArray.

<A> A[] toArray(IntFunction<A[]> generator)

This method uses A to represent the generic type of the array returned containing the elements of the stream, which is created using the provided generator function. The cool part is that a constructor reference can be used for that, too.

names.stream()
     .map(Person::new)         // Person constructor ref
     .toArray(Person[]::new);  // Person[] constructor ref

The returned value is a Person[] with all the stream elements now included as Person references.

Constructor references are just method references by another name, using the word new to invoke a constructor. Which constructor is determined by the context, as usual. This technique gives a lot of flexibility when processing streams.

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].

 

 

Days Between Dates in Java 8

I would like to address a problem that is quite topical at the moment: calculating the number of days between two events. The new Date-Time API in Java 8 makes this process much simpler than it used to be.

The Date-Time API includes the interface java.time.temporal.TemporalUnit, which is implemented by the enum ChronoUnit in the same package. The between method on that interface takes two TemporalUnit instances and returns a long.

long between(Temporal temporal1Inclusive,
             Temporal temporal2Exclusive)

The start and end times must be of compatible types. The implementation converts the second argument to be an instance of the first type before calculating the amount. The result is negative if the second argument occurs before the first argument.

The return value is the number of “units” between the arguments. This becomes convenient when using the constants in the ChronoUnit enum.

For example, say you want to know how many days you need to wait until a particular date. Since you’re interested in days, use the ChronoUnit.DAYS constant from the enum.

import java.time.LocalDate;

import static java.time.temporal.ChronoUnit.DAYS;

public class DaysToElection {
    public static void main(String[] args) {
        LocalDate electionDay = LocalDate.of(2020, Month.NOVEMBER, 3);
        LocalDate today = LocalDate.now();

        System.out.printf("%d days to go...%n",
            DAYS.between(today, electionDay));
    }
}

Since the between method is invoked on the DAYS enum value, this will return the number of days. Other constants in ChronoUnit include HOURS, WEEKS, MONTHS, YEARS, DECADES, CENTURIES, and more (including, believe it or not, FOREVER; if you ever need that value, please send me a message — I’d love to know what the use case was).

At the time of this writing, the result is 1377 days. That’s too many to easily process, but it’s easy enough to use the additional constants to convert them to something more practical.

import java.time.LocalDate;
import java.time.Month;

import static java.time.temporal.ChronoUnit.DAYS;
import static java.time.temporal.ChronoUnit.MONTHS;
import static java.time.temporal.ChronoUnit.YEARS;

public class DaysToElection {
    private static String pluralize(long num) {
        return num == 1 ? "" : "s";
    }

    public static void main(String[] args) {
        LocalDate electionDay = LocalDate.of(2020, Month.NOVEMBER, 3);
        LocalDate today = LocalDate.now();

        System.out.printf("%d days to go...%n",
            DAYS.between(today, electionDay));

        long years  = YEARS.between(today, electionDay);
        long months = MONTHS.between(today.plusYears(years), electionDay);
        long days   = DAYS.between(
            today.plusYears(years).plusMonths(months), electionDay);

        System.out.printf("%d year%s, %d month%s, and %d day%s%n",
                years,  pluralize(years),
                months, pluralize(months),
                days,   pluralize(days));
    }
}

The output of this code is 3 years, 9 months, and 8 days, which is at least more intuitive. Feel free to adjust your time period to suit.

The java.time.temporal.TimeUnit interface is implemented by LocalDate, LocalTime, LocalDateTime, and many other classes. It has a method called until, whose behavior is entirely equivalent to between. It returns a long in the proper units.

There is an alternative, however, if the instance is of type LocalDate (or analogously LocalTime or LocalDateTime), whose methods are declared in the ChronoLocalDate (or ChronoLocalTime or ChronoLocalDateTime) interfaces. In that case there is an overload of until that returns a Period.

// In java.time.LocalDate
Period	until(ChronoLocalDate endDateExclusive)

In the example above, the returned Period prints at P3Y9M8D directly, which can be decomposed in a similar manner.

Period until = today.until(electionDay);

years  = until.getYears();
months = until.getMonths();
days   = until.getDays();
System.out.printf("%d year%s, %d month%s, and %d day%s%n",
        years,  pluralize(years),
        months, pluralize(months),
        days,   pluralize(days));

The Period class also has a static method called between that works the same way. The recommendation is to use whichever style makes the code more readable.

By the way, this is one of those cases where using Groovy doesn’t really simplify the code, but for the record, here’s the Groovy version:

import java.time.LocalDate
import java.time.Month
import java.time.Period

import static java.time.temporal.ChronoUnit.DAYS
import static java.time.temporal.ChronoUnit.MONTHS
import static java.time.temporal.ChronoUnit.YEARS

String pluralize(long num) { num == 1 ? '' : 's' }

LocalDate electionDay = LocalDate.of(2020, Month.NOVEMBER, 3)
LocalDate today = LocalDate.now()

// Using "between"
println "${DAYS.between(today, electionDay)} days to go..."

long years  = YEARS.between(today, electionDay)
long months = MONTHS.between(today.plusYears(years), electionDay)
long days   = DAYS.between(today.plusYears(years).plusMonths(months), electionDay)
println "$years year${pluralize(years)}, " +
    "$months month${pluralize(months)}, and " +
    "$days day${pluralize(days)}"

// Using "until"
Period until = today.until(electionDay)
assert today.until(electionDay).toString() == 'P3Y9M8D'

println "$years year${pluralize(until.years)}, " +
    "$months month${pluralize(until.months)}, and " +
    "$days day${pluralize(until.days)}"

So there you have it, and now I have a quick utility I can run whenever I need to. Which is every day right now, if not once an hour. Ugh.

Making Java 8 Groovier: A few annotated examples

In a couple of weeks, I’m giving two talks at talk at the 2016 JavaOne conference in San Francisco. One of them is called “Groovy and Java 8: Making Java Better“. I’m building examples in preparation for the conference, and as the Groovy community is good about correcting my errors in a friendly way, I thought I’d show some of them here ahead of time.

[Note: the session is labeled CON3277 and will take place Monday, Sept 19, from 12:30 – 1:30pm in the Hilton Plaza Room A, according to the session catalog.]

[One more aside: my co-host on the Groovy Podcast, Baruch Sadogursky, is participating in “The Ultimate Build Tools Face-off“, representing Gradle against Maven and Bazel. The winner of the face-off is determined by audience applause, so if you are within 500 miles of the event, be sure to make your voice heard. If I wasn’t giving my talk at the exact same time, I’d definitely be there.

Let me put that another way. As T’Pau said in the Star Trek original series episode Amok Time:

T’Pau: If both survive the lirpa, combat will continue with the ahn woon.
Kirk: Ah, what do you mean “if both survive?”
T’Pau: This combat is to the death. (emphasis unnecessarily added)

to_the_death_1

So Baruch, if you’re reading this: DON’T SCREW IT UP.

But no pressure.]

Anyway, my (first) talk is a demonstration of how Groovy goes beyond the functional capabilities added in Java 8, but can be used from Java to make life easier. Of course I’m going to talk about Java 8 lambdas vs Groovy closures, and how the method references syntax is different, but in this post I thought I’d highlight a couple of the cool AST transformations that are somewhat less common.

First, there’s memoize. Memoization is the process of building a cache of method calls, so if the same call occurs again, the system can return the cached value right away rather than re-computing it.

One way Groovy accomplishes this is by adding a memoize method to the Closure class. To build the cache, simply invoke the method. For example, consider a variable called add assigned to a closure that takes two arguments and sleeps for one second before returning their sum.

def add = { x, y -> sleep 1000; x + y }.memoize()

println add(3, 4)      // takes 1 sec
println add(3, 4)      // immediate
println add('a','b')   // takes 1 sec
println add('a','b')   // immediate

Invoking the memoize method replaces the closure with a new one that has keeps a cache of method calls. Therefore, the first call to add with any arguments executes normally, but the second and all subsequent calls with the same arguments pulls the value out of the cache. Pretty sweet.

Recursive calls are naturals for this, and the classic example is computing Fibonacci numbers. Here are two separate ways to memoize a fib function. In the first, a variable is assigned to a closure, so internally the closure uses the call method for recursion.

def fib = { n ->
    if (n < 2) 1
    else call(n - 1) + call(n - 2)
}.memoize()

Alternatively, the @Memoize Abstract Syntax Tree (AST) transformation can be applied to a method to accomplish the same thing.

@Memoized
long fib(long n) {
    if (n < 2) 1
    else fib(n - 1) + fib(n - 2)
}

Either way, the result of each call with a particular value of n is saved, so the recursive calls return almost immediately.

To demonstrate that I can use that from Java, I put the method in a Groovy class.

import groovy.transform.Memoized

class AnnotatedFunctions {
    @Memoized
    BigInteger fib(BigInteger n) {
        if (n < 2) 1
        else fib(n - 1) + fib(n - 2)
    }

    @Memoized
    BigInteger fact(BigInteger n) {
        if (n < 2) 1
        else n * fact(n - 1)
    }
}

In addition to the Fibonacci method fib, I also have a recursive factorial computation, fact. In Java, it’s easy enough to instantiate the Groovy class and invoke its methods directly. Here’s a snippet from the main method of my Java class.

AnnotatedFunctions mf = new AnnotatedFunctions();

IntStream.range(1, 100)                
    .forEach(i -> {
        long before = System.nanoTime();
        BigInteger val = mf.fib(new BigInteger(i + ""));
        long after = System.nanoTime();
        System.out.printf("%3d: %8s, fib(%2d) = %d%n", i, 
            (after - before) / 1e9, i, val);
    });

IntStream.range(1, 100)
    .forEach(i -> {
        long before = System.nanoTime();
        BigInteger val = mf.fact(new BigInteger(i + ""));
        long after = System.nanoTime();
        System.out.printf("%3d: %8s, fact(%2d) = %d%n", i, 
            (after - before) / 1e9, i, val);
    });

The output resembles:


  1:  0.10599, fib( 1) = 1
  2:  0.02197, fib( 2) = 2
  3:   2.1E-4, fib( 3) = 3
  4:  1.56E-4, fib( 4) = 5
  5:  1.71E-4, fib( 5) = 8
  6:   1.7E-4, fib( 6) = 13
  // ...
 98:  1.19E-4, fib(98) = 218922995834555169026
 99:  1.16E-4, fib(99) = 354224848179261915075

  1:  2.74E-4, fact( 1) = 1
  2: 0.002715, fact( 2) = 2
  3:  1.34E-4, fact( 3) = 6
  4:  1.31E-4, fact( 4) = 24
  5:   8.1E-5, fact( 5) = 120
  6:  1.41E-4, fact( 6) = 720
  // ...
 98:   5.5E-5, fact(98) = 9426890448883247745626185743057242473809693764078951663494238777294707070023223798882976159207729119823605850588608460429412647567360000000000000000000000
 99:   5.3E-5, fact(99) = 933262154439441526816992388562667004907159682643816214685929638952175999932299156089414639761565182862536979208272237582511852109168640000000000000000000000

Pretty impressive, and trivial to do in Groovy even if you just want to call it from Java.

Another AST transform that comes up if the algorithm is right is @TailRecursive. If you can express your algorithm in a way that the recursive call comes last, you can use that transform, which will also check that you did it properly.

I added the following method to my AnnotatedFunctions class.

import groovy.transform.TailRecursive
class AnnotatedFunctions {
    // ... other methods ...

    @TailRecursive
    BigInteger factorial(BigInteger n, BigInteger acc = 1G) {
        n < 2 ? acc : factorial(n - 1G, n * acc)
    }
}

I don’t normally write BigInteger, since Groovy automatically uses it if the system requires that many digits, but when integrating with Java it helps to be explicit. I’m also taking advantage of Groovy’s ability to optionally initialize a method variable by assigning the acc accumulator the value of 1.

Now I can call this from Java, too.

AnnotatedFunctions mf = new AnnotatedFunctions();
// ... other calls from before ...

System.out.println("70000! has " +
     mf.factorial(new BigInteger(70_000 + "")).toString().length() +
     " digits");

[This also takes advantage of the fact that starting in Java 7, you can embed underscores in numeric literals, like 70_000, for readability.]

The output is: 70000! has 308760 digits.

Another cool AST transform is @Immutable. Functional programming favors immutability, but it’s enormously difficult to make a Java class produce immutable objects. You have to remove all the setters, provide private final backing fields for properties, wrap collections in their unmodifiable equivalents, make the class final, and so on.

Or, you can just use the @Immutable annotation, which does all that and more for you. Here’s an immutable point class.

import groovy.transform.Immutable

@Immutable
class Point {
    double x
    double y
}

Here is a Spock test (aside — my other JavaOne talk is on Spock testing) that demonstrates its capabilities.

import spock.lang.Specification

class PointSpec extends Specification {
    def 'tuple constructor works'() {
        expect: new Point(3, 4)
    }

    def "can't change x"() {
        given:
        Point p = new Point(1, 2)

        when:
        p.x = 5

        then:
        thrown(ReadOnlyPropertyException)
    }

    def "can't change y"() {
        given:
        Point p = new Point(1, 2)

        when:
        p.y = 5

        then:
        thrown(ReadOnlyPropertyException)
    }
}

I couldn’t really leave that alone, so I added a few more methods.

import groovy.transform.Immutable

@Immutable
class Point {
    double x
    double y


    static Point createPoint(double x, double y) {
        new Point(x, y)
    }

    Point translate(double dx = 0, double dy = 0) {
        new Point(x + dx, y + dy)
    }

    Point rotate(double radians) {
        double r = Math.sqrt(x * x + y + y)
        new Point(r * Math.cos(radians), r * Math.sin(radians))
    }

    Point plus(Point p) {
        new Point(x + p.x, y + p.y)
    }

    Point minus(Point p) {
        new Point(x - p.x, y - p.y)
    }
}

The translate and rotate methods produce new points that are the result of moving or rotating the original point. I also added a plus and a minus method to take advantage of operator overloading. The corresponding tests are:

import spock.lang.Specification

class PointSpec extends Specification {
    // ... other tests ...

    def "can translate"() {
        given:
        Point start = new Point(1, 0)
        Point end = new Point(3, 3)

        when:
        Point p = start.translate(2, 3)

        then:
        assert (p.x - end.x).abs() < 1e-10
        assert (p.y - end.y).abs() < 1e-10

    }

    def "can rotate 90 deg"() {
        given:
        Point p = new Point(1, 0)

        when:
        p = p.rotate(Math.PI / 2)

        then:
        p.x.abs() < 1e-10
        p.y == 1
    }

    def "can rotate 180 deg"() {
        given:
        Point p = new Point(1, 0)

        when:
        p = p.rotate(Math.PI)

        then:
        p.x == -1
        p.y.abs() < 1e-10
    }

    def "overloaded plus"() {
        given:
        Point p1 = new Point(1, 2)
        Point p2 = new Point(3, 4)

        when:
        Point p = p1 + p2

        then:
        p.x == 4
        p.y == 6
    }

    def "overloaded minus"() {
        given:
        Point p1 = new Point(1, 2)
        Point p2 = new Point(3, 4)

        when:
        Point p = p1 - p2

        then:
        p.x == -2
        p.y == -2
    }
}

The only nuisance is that because I’m planning to integrate with Java, I’m using doubles rather than BigDecimals, and that means the precision of zero isn’t quite what I need. Everything works, though, including the tuple constructor.

That’s significant, because I ran into a problem when trying to call this from Java.

public class UsePoint {
    public static void main(String[] args) {
        // Point p = new Point(1, 0) // doesn't work (aww)

        Point p = Point.createPoint(1, 0);
        System.out.println(p);
        System.out.printf("(%s,%s)%n", p.getX(), p.getY());

        Point p1 = p.translate(2, 3);
        System.out.println(p1); // should be (3,3)

        Point p2 = p.rotate(Math.PI / 2);
        System.out.println(p2); // should be (0,1)
    }
}

Even though the AST transform generates a tuple constructor for Point, the Java code apparently is compiled too soon to see it. I was forced to add a createPoint method to Point in order to instantiate the class.

The rest works, though. I can invoke translate or rotate without a problem. The plus and minus methods don’t help Java much, since they’re there just for the Groovy operator overloading. Of course, there are no setters (no setX or setY methods) available, so I don’t have an issue with Java trying to call them.

I’m going to talk about streams, lambdas, and method references, too, but this post has enough in it for now. I’ll show that stuff in my next post. Besides, that’ll give me another chance to “encourage” Baruch in the Thunderdome.

Fun with Time Zones in Java 8

[Note: Revised based on suggestions in the comments.]

They say that one way to identify a software developer is to whisper the word “timezone” in their ear and see if they shudder.

That’s certainly true for me, though my reaction is based more on travel and trying to arrange conference calls across time zones than actual coding. Like most scary things, I’ve tried to avoid the whole date/time API in Java, partly because prior to Java 8 the API is a tire fire and partly because the whole issue is like the “Here be dragons” section of a map.

herebedragons

Recently, however, I’ve been teaching Java 8 upgrade classes, and making Java 8 presentations at conferences on the No Fluff, Just Stuff tour. As part of those talks, I give an overview of the new java.time package.

The new package, by the creators of JodaTime, finally (finally!) provides an alternative to java.util.Date and java.util.Calendar. New classes like java.time.LocalDate, java.time.LocalTime, java.time.LocalDateTime and java.time.ZonedDateTime are now all available and much more powerful. If you used JodaTime in the past (no pun intended, but they’re hard to avoid), you’re already familiar with them, as the same people who wrote JodaTime in the first place wrote the new package.

I’m certainly not going to review the whole thing here, but I did want to mention a couple of fun examples.

First, I’ve known for some time that there are time zones in the world that are off by half-hour offsets rather than whole hours. To pick one, Indian Standard Time is UTC+05:30. When I mentioned that in class, I also said that someone once told me that there was a time zone in the world offset by 45 minutes. At the time I thought they were pulling my leg, but now I have the machinery to find out.

Once problem, however, is that abbreviations like EST or IST are no longer valid. The Wikipedia article on Time Zones discusses the issue, which claims that “such designations can be ambiguous”, where ECT could stand for Eastern Carribean Time, Ecuador Time, or even European Central Time. Instead, the ISO 8601 standard uses either offset designators, like UTC-05:00, or “region-based IDs”, like “America/New_York”.

(Speaking of the ISO 8601 standard, since there’s an XKCD cartoon on everything, here’s the one on that: https://xkcd.com/1179/ .)

Bringing it back to Java, the API defines a class called java.time.ZoneId, which has a static method called ZoneId.of(...) that takes a designator. You use that to create a ZonedDateTime. If you use an offset as the argument, then the time in the ZonedDateTime does not change, but if you use the region, the time will automatically adjust for Daylight Savings Time rules in that region.

[As you can imagine, the whole Daylight Savings Time issue is another rabbit hole I choose not to dive into. Those rules are discussed in a class called java.util.time.zone.ZoneRules, which refers to classes like ZoneOffsetTransition, ZoneOffsetTransitionRule, and ZoneRulesProvider. You can see how the complexity just goes up and up, especially because DST rules change frequently in different locations. Yikes.]

If you know the region ID, you can create a ZoneId using the of method. I have the opposite problem, however. I want to figure out the region ID given the offset.

Fortunately, the Java Tutorial has a section on ZoneId and ZoneOffset that actually addresses this problem. For some strange reason, however, their sample code doesn’t use the Java 8 streams and lambda expressions, so I decided to rewrite it. Here’s my version:

import java.time.LocalDateTime;
import java.time.ZoneId;
import java.time.ZoneOffset;
import java.time.ZonedDateTime;
import java.time.format.DateTimeFormatter;
import java.time.format.FormatStyle;
import java.util.List;

import static java.util.Comparator.comparingInt;
import static java.util.stream.Collectors.toList;

public class FunnyOffsets {
    public static void main(String[] args) {

        Instant instant = Instant.now();
        ZonedDateTime current = instant.atZone(ZoneId.systemDefault());
        System.out.printf("Current time is %s%n%n", current);

        System.out.printf("%10s %20s %13s%n", "Offset", "ZoneId", "Time");
        ZoneId.getAvailableZoneIds().stream()
            .map(ZoneId::of)
            .filter(zoneId -&gt; {
                ZoneOffset offset = instant.atZone(zoneId).getOffset();
                return offset.getTotalSeconds() % (60 * 60) != 0;
            })
            .sorted(comparingInt(zoneId -&gt;
                instant.atZone(zoneId).getOffset().getTotalSeconds()))
            .forEach(zoneId -&gt; {
                ZonedDateTime zdt = current.withZoneSameInstant(zoneId);
                System.out.printf("%10s %25s %10s%n", zdt.getOffset(), zoneId,
                    zdt.format(DateTimeFormatter.ofLocalizedTime(FormatStyle.SHORT)));
            });
    }
}

That code requires some explanation. First, the ZoneId.getAvailableZoneIds() method returns a Set of Strings containing all the region IDs. After converting to a Stream, the map(ZoneId::of) expression transforms that into a stream of ZoneId instances.

Then I want to filter that stream to return only those ZoneIds that have an offset that isn’t evenly divisible by 3600 (= 60 sec/min * 60 min/hr). To get the offset, however, you need a ZonedDateTime, so I use the current Instant and use the atZone method with each ZoneId to get a ZonedDateTime, and then call its getOffset method. That, in turn, has a getTotalSeconds method, and I can do the modulus on that. At that point, I could have just printed them, but I decided to sort them by offset first.

The sorted method on Stream takes a java.util.Comparator. I could implement the Comparator as a lambda myself, but Java 8 also added several default and static methods to that interface. One of them is Comparator.comparingInt, which takes an ToIntFunction that transforms its argument into an int. Then sorted generates a Comparator that sorts the ints, which then sorts the collection based on the results.

Believe it or not, that whole map/filter/sorted paradigm gets much easier with practice. It was harder for me to write that explanation than to figure out the method calls.

To print the results, I wanted to show the offset in each time zone as well as its region name. The ZonedDateTime class has a method called withZoneSameInstant, which converts a given time to its equivalent in another time zone.

(That’s a very convenient method that I’ve needed my entire professional career, and justifies all the time (again, no pun intended) I’ve spent on this.)

Finally, printing them out was easier if I formatted the time, for which I used the DateTimeFormatter shown. The result right now is:

Current time is 2016-07-16T16:12:51.905-04:00[America/New_York]
    Offset               ZoneId          Time
    -09:30         Pacific/Marquesas   10:42 AM
    -04:30           America/Caracas    3:42 PM
    -02:30          America/St_Johns    5:42 PM
    -02:30       Canada/Newfoundland    5:42 PM
    +04:30                      Iran   12:42 AM
    +04:30               Asia/Tehran   12:42 AM
    +04:30                Asia/Kabul   12:42 AM
    +05:30              Asia/Kolkata    1:42 AM
    +05:30              Asia/Colombo    1:42 AM
    +05:30             Asia/Calcutta    1:42 AM
    +05:45            Asia/Kathmandu    1:57 AM
    +05:45             Asia/Katmandu    1:57 AM
    +06:30              Asia/Rangoon    2:42 AM
    +06:30              Indian/Cocos    2:42 AM
    +08:45           Australia/Eucla    4:57 AM
    +09:30           Australia/North    5:42 AM
    +09:30      Australia/Yancowinna    5:42 AM
    +09:30        Australia/Adelaide    5:42 AM
    +09:30     Australia/Broken_Hill    5:42 AM
    +09:30           Australia/South    5:42 AM
    +09:30          Australia/Darwin    5:42 AM
    +10:30       Australia/Lord_Howe    6:42 AM
    +10:30             Australia/LHI    6:42 AM
    +11:30           Pacific/Norfolk    7:42 AM
    +12:45                   NZ-CHAT    8:57 AM
    +12:45           Pacific/Chatham    8:57 AM

So not only are there regions with half-hour offsets, like “Canada/Newfoundland”, “Australia/Adelaide”, and “Pacific/Norfolk”, there are indeed time zones offset by 45 minutes, like “Asia/Katmandu”, “Australia/Eucla”, and “Pacific/Chatham”.

I haven’t been able to find the reasons for all the odd offsets, but they appear to be due to political compromises between two surrounding zones. Some are very recent adoptions, like the Mongolian one (“Asia/Kathmandu”), which wasn’t established until 1986.

On the guiding principle that anything I can do in Java I can do much more easily in Groovy, I decided to write a Groovy version. In this case, the Groovy JDK hasn’t done anything with the classes in java.time yet. Still, the normal Groovy simplifications lead me to this version:

import java.time.LocalDateTime
import java.time.ZoneId
import java.time.ZoneOffset
import java.time.ZonedDateTime
import java.time.format.DateTimeFormatter
import java.time.format.FormatStyle

LocalDateTime now = LocalDateTime.now();
List<ZonedDateTime> zdts =
    ZoneId.availableZoneIds
        .collect { now.atZone(ZoneId.of(it)) }
        .findAll { it.offset.totalSeconds % (60 * 60) != 0 }
        .sort { it.offset.totalSeconds }

ZonedDateTime current = now.atZone(ZoneId.systemDefault());
println "Current time is $current"
printf("%10s %20s %13s%n", "Offset", "ZoneId", "Time")
zdts.each {
    ZonedDateTime zdt = current.withZoneSameInstant(it.zone)
    System.out.printf("%10s %25s %10s%n", zdt.offset, it.zone,
        zdt.format(DateTimeFormatter.ofLocalizedTime(FormatStyle.SHORT)))
}

I could have used the same map/filter/sorted methods here that I used in Java, but I think this version is a bit more idiomatic. All the needed methods have been added directly to collections, so I don’t need to switch to streams first. That means I don’t need to switch back, either, so I need fewer steps. I also take advantage of the convention that property access (like offset or totalSeconds) is converted to the associated getter method (getOffset or getTotalSeconds) automatically. This time, just to show an alternative, I used the ZonedDateTime class instead of Instant and converted to a list before printing the values.

That was fun, but if you really want see how crazy time zones can get, check out this figure, from the Wikipedia article on time zones in Antarctica.

antarctica_time_zones

If that doesn’t make a developer shudder, nothing will.

I decided to print those out, too. Here’s my Java version:

import java.time.LocalDateTime;
import java.time.ZoneId;
import java.time.ZonedDateTime;
import java.util.Comparator;
import java.util.List;
import java.util.stream.Collectors;

public class AntarcticaTimeZones {
    public static void main(String[] args) {
        Instant now = Instant.now();
        ZoneId.getAvailableZoneIds().stream()
            .filter(id -> id.contains("Antarctica"))
            .map(id -> now.atZone(ZoneId.of(id)))
            .sorted(Comparator.comparingInt(zoneId -&amp;gt;
                    zoneId.getOffset().getTotalSeconds()))
            .collect(Collectors.toList());
            .forEach(zdt ->
                System.out.printf("%s: %s%n", zdt.getOffset(), zdt.getZone()));
    }
}

This time I filtered on region IDs with the word “Antarctica” and I didn’t bother with the static import for Comparator.comparingInt. The result this time is:

-04:00: Antarctica/Palmer
-03:00: Antarctica/Rothera
+03:00: Antarctica/Syowa
+05:00: Antarctica/Mawson
+06:00: Antarctica/Vostok
+07:00: Antarctica/Davis
+08:00: Antarctica/Casey
+10:00: Antarctica/DumontDUrville
+11:00: Antarctica/Macquarie
+12:00: Antarctica/McMurdo
+12:00: Antarctica/South_Pole

Yeah, good luck with that. The Groovy version is naturally shorter:

import java.time.ZoneId

ZoneId.availableZoneIds
    .findAll { it ==~ /.*Antarctica.*/ }
    .collect { now.atZone(ZoneId.of(it)) }
    .sort { it.offset.totalSeconds }

In case you’re wondering, orbiting spacecraft experience many sunrises and sunsets in a 24 hour period, so timezones are hopeless. The International Space Station (according to the Wikipedia article on time zones in space) just gives up and uses GMT. The same article says that the “common practice for lunar missions is to use the Earth-based time zone of the launch site or mission control”.

Timekeeping on Mars gets worse, because the length of the Martian day is approximately 24 hours and 39 minutes, which is why Matt Damon kept referring to a sol.

That reminds me of this quote from Men in Black:

Jay: Zed, don’t you guys ever get any sleep around here?
Zed: The twins keep us on Centaurian time, standard thirty-seven hour day. Give it a few months. You’ll get used to it… or you’ll have a psychotic episode.

I suspect that if I spend much more time (ugh, again — see how hard it is to avoid those puns?) on this, I may be vulnerable to the same problem, so I’ll take this as a good time (haha — that one was intentional) to end.

%d bloggers like this: