sts ry What’s New in Java 12? s A DeveloperFocused Guide to the Latest Features and JVM Changes ing Paths Benjamin Muschko & Deals ghts gs Support Sign Out Playlists What’s New in Java 12? History by Benjamin Muschko Topics Copyright © 2019 O’Reilly Media, Inc. All rights reserved. Learning Paths Printed in the United States of America. Offers & Deals Published by O’Reilly Media, Inc., 1005 Gravenstein Highway North, Sebastopol, CA 95472. HigOhli’gRhtesilly books may be purchased for educational, business, or sales promotional use. Online editions are also available for most titles (http://oreilly.com). For more information, contact our Settciongrpsorate/institutional sales department: 8009989938 or [email protected]. Support Acquisitions Editor: Tyler Ortman Sign Out Development Editor: Corbin Collins Production Editor: Katherine Tozer Copyeditor: Octal Publishing, Inc. Proofreader: Charles Roumeliotis Interior Designer: David Futato Cover Designer: Karen Montgomery Illustrator: Rebecca Demarest April 2019: First Edition Revision History for the First Edition 20190430: First Release The O’Reilly logo is a registered trademark of O’Reilly Media, Inc. What’s New in Java 12?, the cover image, and related trade dress are trademarks of O’Reilly Media, Inc. The views expressed in this work are those of the author, and do not represent the publisher’s views. While the publisher and the author have used good faith efforts to ensure that the information and instructions contained in this work are accurate, the publisher and the author disclaim all responsibility for errors or omissions, including without limitation responsibility for damages resulting from the use of or reliance on this work. Use of the information and instructions contained in this work is at your own risk. If any code samples or other technology this work contains or describes is subject to open source licenses or the intellectual property rights of others, it is your responsibility to ensure that your use thereof complies with such licenses and/or rights. 9781492058236 [LSI] HistWory hat’s New in Java 12? Topics Introduction Learning Paths Java started out strong in 2019. On March 19, the Java team shipped the latest major version release, Java 12. If you follow the latest developments in the Java world, you might know that Offers & Deals you can expect two major Java releases per year. This is great news for everyone committed to the idea of shipping early and often. Ultimately, you’ll be able to get your hands on new features Highlights and bug fixes on a regular basis. Settings In comparison to the last LongTerm Release (LTS) Java 11, version 12 contains fewer features but also fewer breaking changes. If you haven’t caught up with Java 11 yet, I’d recommend Support reading the report “What’s New in Java 11?”. Sign Out This edition of the report condenses everything you need to know about Java 12 into a few pages to help you determine whether it’s worthwhile to upgrade now or if you should hold off. We begin by introducing the shiny new features. Later, we touch on removals and deprecations and then conclude with a discussion of performance and security enhancements. Let’s get started! New Features Let’s explore the new functionality through an example. To test it out, we’ll write some JUnit tests that apply the new APIs and verify the correct behavior for different use cases. We begin by having a look at the latest addition to the Stream API. Teeing Collectors Since its introduction in Java 8, the Stream API provides methods for transforming elements in a Stream instance with the help of Collectors. The latest version of Java adds a new method to the API called Collectors.teeing(Collector, Collector, BiFunction). This method uses two different collectors to transform the elements of a Stream and then combine the result, as shown in Figure 11. Figure 11. Merging the result of two Stream Collector operations It sounds abstract. When would you want to use this? I hear you. This method is best explained by example. Imagine that you work in the accounting department of a big corporation. Your task is to determine the minimum and maximum salary being paid to any of your employees. That’s usually fairly easy to achieve with the help of an SQL query, but what if you had to express this “query” as Java code? The teeing collector can help you solve this problem. Example 11 defines a Stream instance containing elements that represent salaries paid to employees. The collect method called on the Stream instance is provided with a Collector implementation created by the teeing method. As parameters, the teeing method call determines the minimum salary and the maximum salary as well as a Function that brings together the result as a representation of a SalaryRange, a POJO containing both values as Optional. Example 11. Merging the results of two independent Stream collectors import java.util.Optional; import java.util.stream.Stream; import static java.util.stream.Collectors.*; @Test void canCollectStreamFromTwoCollectorsAndMergeResult() { SalaryRange salaryRange = Stream .of(56700, 67600, 45200, 120000, 77600, 85000) .collect(teeing( minBy(Integer::compareTo), maxBy(Integer::compareTo), SalaryRange::fromOptional)); assertEquals("Salaries range between 45200 and 120000.", salaryRange.toString()); } private static class SalaryRange { private final Integer min; private final Integer max; private SalaryRange(Integer min, Integer max) { this.min = min; this.max = max; } public static SalaryRange fromOptional(Optional<Integer> min, Optional<Integer> max) { if (min.isEmpty() || max.isEmpty()) { throw new IllegalStateException("Minimum and " + "maximum salaries cannot be null"); } return new SalaryRange(min.get(), max.get()); } @Override public String toString() { return "Salaries range between " + min + " and " + max + "."; } } Use of the teeing operation as input for a collect method call. Collectors participating in the operation. The function that merges two results into a single one. In the next section, you will learn about the new methods that have been introduced to the String API. String API Enhancements One of the most anticipated features planned for Java 12, JDK Enhancement Proposal (JEP) 326: Raw String Literals, has been dropped because it wasn’t considered ready for prime time. The proposal captures a feature for declaring a multiline String without the need to add new line characters. An SQL query with a SELECT, FROM, and WHERE statements per line is a typical use case for a multiline String. JEP 326 also came with a couple of supporting features. One of those features actually shipped with Java 12, the method String.indent(int). INDENTING A STRING The indent method helps with (surprise!) changing the indentation of a String. You can either pass a positive value or a negative value depending on whether you want to add more white spaces or remove existing white spaces. There are various use cases for wanting to use the new indentation method. It’s very common for structured markup formats like XML, HTML, or YAML to indent nested elements. Figure 1 2 shows a multiline YAML file that is only valid by adhering to proper indentation. Figure 12. Indenting a String in Java 12 What does this look like in code? Example 12 constructs and indents a YAML file represented as a multiline String. You can see that we are building the String with the help of a StringBuilder. Many lines added to the builder have been indented to construct the expected YAML data structure. If you look closely at the assertion, you might notice that the indent method automatically adds a newline character if it hasn’t been provided yet. That’s to be expected and is a feature of the new method. Example 12. Indenting a multiline String by two or four white spaces @Test void canIndentString() { StringBuilder yaml = new StringBuilder(); yaml.append("spec:\n"); yaml.append("containers:\n".indent(2)); yaml.append(" name: nginx\n".indent(2)); yaml.append("image: nginx:1.7.9".indent(4)); assertEquals("spec:\n" + " containers:\n" + " name: nginx\n" + " image: nginx:1.7.9\n", yaml.toString()); } Indenting a String by two white spaces. The String already contains a new line character. Indenting a String by four white spaces. The next line character will be added. TRANSFORMING A STRING Another enhancement to the String API is the String.transform(Function) method. The method takes a String and transforms it into a new String with the help of a Function. Suppose that you wanted to clean up a String representing a URL by running it through a transformation pipeline comprising multiple Functions. The first pass ensures that the String doesn’t contain any leading or trailing white spaces by calling Java’s own String.strip(String) method. In the next pass, you encode all query parameters that might exist for a URL by using a method you implemented yourself. Figure 13 shows the workflow. Figure 13. Stepbystep transformation of a String Now, there’s nothing stopping you from implementing the transformation by nesting multiple method calls, but I think we can agree that the logic is somewhat difficult to read and understand: URLCleaner.encodeQueryParams("http://google.com/".strip()); How does this look in code using the transform method? Example 13 provides a cleaner approach that can be easily enhanced by additional transformations without losing its conciseness and readability. Example 13. Transforming a String by chaining multiple functions import java.io.UnsupportedEncodingException; import java.net.URLEncoder; import java.util.ArrayList; import java.util.List; @Test void canTransformString() { List<String> urls = List.of( " http://google.com/", "http://my.search.com?query=java server&page=1"); List<String> transformedUrls = new ArrayList<>(); for (String url : urls) { String transformedUrl = url.transform(String::strip) .transform(URLCleaner::encodeQueryParams); transformedUrls.add(transformedUrl); } assertEquals(List.of( "http://google.com/", "http://my.search.com?query%3Djava+server%26page%3D1"), transformedUrls); } private static class URLCleaner { public static String encodeQueryParams(String rawURL) { try { if (rawURL.contains("?")) { String[] splitURL = rawURL.split("\\?"); return splitURL[0] + "?" + URLEncoder.encode(splitURL[1], "UTF8"); } return rawURL; } catch (UnsupportedEncodingException ex) { throw new RuntimeException("UTF8 not supported", ex); } } } Transforms a String by removing leading and trailing white space characters. Transforms the query portion of a URL String with the help of a custom method. STRING CONSTANTS