Java 14 will soon be released and it’s time to leave our good ol’ Java 8 and begin to use the new language features Java brings with its versions.

In this post, I will only cover some of the newest Java features.

Java 9

Release notes

Private methods in interfaces

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
interface PetStore {
  // You can now define private methods in interfaces. It's quite useful if you
  // want to factorize some code from default methods
  private long getNumberAvailablePets(List<Pet> pets, Predicate<Pet> predicate) {
    return pets.stream()
        .filter(Pet::isAvailable)
        .filter(predicate)
        .count();
  }

  default long getNumberAvailableCats(List<Pet> pets) {
    return getNumberAvailablePets(pets, pet -> Pet.Type.CAT == pet.getType());
  }

  default long getNumberAvailableDogs(List<Pet> pets) {
    return getNumberAvailablePets(pets, pet -> Pet.Type.DOG == pet.getType());
  }
}

New Optional APIs

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
class NewOptionalAPIs {

  private static final List<Cat> CATS = Arrays.asList(
      new Cat("Tadar sauce"),
      new Cat("Nyan cat"),
      new Cat("Grumpy cat")
  );

  private static Optional<Cat> findByName(String name) {
    return CATS.stream().filter(c -> name.equals(c.getName())).findAny();
  }

  public static void main(String[] args) {
    /*
     * Optional.ifPresentOrElse
     */
    Optional<Cat> cat = findByName("Grumpy cat");
    // Before
    if (cat.isPresent()) {
      System.out.println(cat.get().getName());
    } else {
      System.out.println("No cat found");
    }
    // After
    cat.ifPresentOrElse(c -> System.out.println(c.getName()), () -> System.out.println("No cat found"));
    findByName("foobar").ifPresentOrElse(c -> System.out.println(c.getName()), () -> System.out.println("No cat found"));

    /*
     * Optional.isEmpty
     */
     // Before
    if (cat.isEmpty()) {
      System.out.println("No cat found");
    } else {
      System.out.println(cat.get().getName());
    }

    // After
    // Optional.or
    Optional<Cat> c = findByName("Garfield")
        .or(() -> findByName("Tata"))
        .or(() -> findByName("Toto"));
    System.out.println(c.orElse(new Cat("titi")));

    // Optional.stream
    String name = "Tom";
    Cat[] cats = Optional.of(new Cat(name)).stream().toArray(Cat[]::new);
    System.out.println(cats);
  }
}

New Future APIs

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
// Annotation @Deprecated now has the attributes "forRemoval" and "since", useful to document Java classes
@Deprecated(forRemoval = true, since = "0.1.0")
public class NewCompletableFutureAPIs {

  public static void main(String[] args) throws InterruptedException, ExecutionException {
    CompletableFuture<String> future = new CompletableFuture<>();
    // Throw a TimeoutException if the task is not finished 
    future.orTimeout(2, TimeUnit.SECONDS);
    // Completes with the given value upon timeout
//  future.completeOnTimeout("foobar", 2, TimeUnit.SECONDS);

    Runnable runnable = () -> {
      try {
        Thread.sleep(3000);
      } catch (InterruptedException e) {
        e.printStackTrace();
      }
      future.complete("finished");
    };
    runnable.run();
    System.out.println(future.get());
  }
}

New Collection APIs

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
public static void main(String[] args) {
  /*
   * Set.of / List.of
   */
  // Before
  Set<String> catNames = new HashSet<>();
  catNames.add("Tadar sauce");
  catNames.add("Grumpy cat");
  catNames.add("Nyan cat");
  System.out.println(Collections.unmodifiableSet(catNames));

  // After
  System.out.println(Set.of("Tadar sauce", "Grumpy cat", "Nyan cat"));

  /*
   * Map.of
   */
  // Before
  Map<String, Integer> catLikes = new HashMap<>();
  catLikes.put("Tadar sauce", 10);
  catLikes.put("Grumpy cat", 1);
  catLikes.put("Nyan cat", 1000);
  System.out.println(Collections.unmodifiableMap(catLikes));

  // After
  System.out.println(Map.of("Tadar sauce", 10, "Grumpy cat", 1, "Nyan cat", 1000));
}

JShell

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
$ ${JAVA_HOME}/bin/jshell
|  Welcome to JShell -- Version 14
|  For an introduction type: /help intro

jshell> String greeting = "Hello, world";
greeting ==> "Hello, world"

jshell> System.out.println(greeting);
Hello, world

jshell> /vars
|    String greeting = "Hello, world"

jshell> 2+2*4
$3 ==> 10

Jigsaw

Java 9 also brings the Project Jigsaw whose goal is to have a standard module system for the Java Platform.

This is a large topic that I will not cover here.

Java 10

Release notes

Inference type

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
public class InferenceType {
  // Does not compile
//  private final var foobar = new String();

  public static void main(String[] args) {
    var catName = "Grumpy cat"; // String
    var nbCat = Integer.valueOf(1); // int
    var isCute = Boolean.TRUE; // Boolean

    System.out.println("There are " + nbCat + " " + catName);

    for (var i = 0; i < 10; i++) {
      System.out.println(i);
    }

    var catNames = new ArrayList<String>(); // Type resolved
    // Does not compile
//    catNames = new LinkedList<String>();

    // Does not compile
//    var foo;
//    var ints = {0, 1};
//    var appendSpace = (String a) -> a + " ";
  }

  // Does not compile
//  private var doSomething() {
//    return "foobar";
//  }

  // Does not compile
//  private String foobar(var s) {
//    return "";
//  }
}

New Optional APIs

1
2
3
public static void main(String[] args) {
  Optional.ofNullable(null).orElseThrow();
}

New Collection APIs

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
public static void main(String[] args) {
  // List.copyOf / Set.copyOf
  List.copyOf(List.of("foo", "bar"))
      .stream()
      .collect(
          // Collectors.toUnmodifiableList() / Collectors.toUnmodifiableSet
          Collectors.toUnmodifiableList()
      );
  // Map.copyOf
  Map.copyOf(Map.of("foo", "bar"));
}

Java 11

Release notes

Inference type on lambda

1
2
3
4
5
6
7
8
public class NewInferenceType {
  static IntFunction<Integer> doubleIt1 = x -> x * 2;
  // Compiles on java 8
  static IntFunction<Integer> doubleIt2 = (@Deprecated int x) -> x * 2;
  // Compiles only from java 11
  // Useful if we want to add annotations to the parameter
  static IntFunction<Integer> doubleIt3 = (@Deprecated var x) -> x * 2;
}

New String APIs

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
// String.isBlank
System.out.println(" ".isBlank());
System.out.println("".isBlank());
System.out.println("foo".isBlank());

// String.lines
"foo\nbar".lines().forEach(System.out::println);

// String.strip ~= String.trim with "Unicode aware"
System.out.println("foo" + " azert ".strip() + "bar");
System.out.println("foo" + " azert ".stripLeading() + "bar");
System.out.println("foo" + " azert ".stripTrailing() + "bar");

// String.repeat
System.out.println("foobar".repeat(3));

New Files APIs

1
2
3
4
// Files
var path = Files.writeString(Files.createTempFile("foobar", ".txt"), "foobar");
System.out.println(path);
System.out.println(Files.readString(path));

New HttpClient APIs

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
// HttpClient
var httpClient = HttpClient.newBuilder()
    .version(HttpClient.Version.HTTP_2)
    .build();
var request = HttpRequest.newBuilder()
    .uri(URI.create("http://localhost:8080/actuator/health"))
    .GET()
    .build();
HttpResponse<String> response = httpClient.send(request, HttpResponse.BodyHandlers.ofString());
System.out.println(response.statusCode());
System.out.println(response.body());
System.out.println();

var future = httpClient.sendAsync(request, HttpResponse.BodyHandlers.ofString())
    .thenAccept(r -> {
      System.out.println(r.statusCode());
      System.out.println(r.body());
      r.body();
    });

future.get();

Java 12

Release notes

New Switch expressions

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
// Before
switch (day) {
  case MONDAY:
  case FRIDAY:
  case SUNDAY:
    System.out.println(6);
    break;
  case TUESDAY:
    System.out.println(7);
    break;
  case THURSDAY:
  case SATURDAY:
    System.out.println(8);
    break;
  case WEDNESDAY:
    System.out.println(9);
    break;
}
// After
switch (day) {
  case MONDAY, FRIDAY, SUNDAY -> System.out.println(6);
  case TUESDAY                -> System.out.println(7);
  case THURSDAY, SATURDAY     -> System.out.println(8);
  case WEDNESDAY              -> System.out.println(9);
}

// Can be used as an expression
var d = switch (day) {
  case MONDAY, FRIDAY, SUNDAY -> 6;
  case TUESDAY                -> 7;
  case THURSDAY, SATURDAY     -> 8;
  case WEDNESDAY              -> 9;
  default                     -> 10;
};

Java 13

Release notes

Switch expressions enhancements

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
// yield keyword used when assigning / returning value
// and when we need to have multiple instructions in a block
var d = switch (day) {
  case MONDAY, FRIDAY, SUNDAY: yield 6;
  case TUESDAY               : yield 7;
  case THURSDAY, SATURDAY    : yield 8;
  case WEDNESDAY             : yield 9;
  default                    :
    // yield useful here compared to the Java 12 arrow "->"
    System.out.println("In default case");
    yield 10;
};

To have this feature, you need to add a flag to enable it:

1
2
3
4
# when compiling
javac --release 13 --enable-preview YourJavaClass.java
# when running
java --enable-preview YourJavaClass.java

Text blocks

Finally…

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
// Before
var html = "<html>\n" +
           "  <body>\n" +
           "    <h1>Hello, world!</h1>\n" +
           "  </body>\n" +
           "</html>\n";
// After
var html = """
<html>
  <body>
    <h1>Hello, world!</h1>
  </body>
</html>
""";

Java 14

Release notes

Pattern matching for instanceof

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
// Before
if (obj instanceof String) {
  String s = (String) obj;
  if (!s.isBlank()) {
    System.out.println(s.repeat(3));
  }
}
// After
if (obj instanceof String s && !s.isBlank()) {
  System.out.println(s.repeat(3));
}

Records

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
// Lightweight class intended to carry data only (same as "data" class in Kotlin)
public record Cat(
  // Compact constructor to avoid writing verbose code (e.g. getters / setters)
  String name,
  String type,
  int age
){
  // No explicit parameter in constructor
  public Cat{
    // Can be used to validate inputs
    if (age < 0) {
      throw new IllegalArgumentException("Don't try to challenge time!");
    }
  }

  // Can create methods, however, records only have immutable data
  public void meow() {
    System.out.println("Meow! :3");
  }
}