Java SE 8 For the Really Impatient, Note 13
4,940 words in 31 minutes
Chapter 8 Miscellaneous Goodies
Working with Files
Java 8 brings a small number of convenience methods that use stream for reading lines from files and for visiting directory entries. Also, there is an official way of performing Base64 encoding.
Streams of Lines
Files.lines: read the lines of a file lazily. It yields a stream of strings, one per line of input:
As soon as the first line containing password is found, no further lines are read from the underlying file.
Files.lines defaults to UTF-8, unlike FileReader which opens files in local character encoding. You can specify other encodings by supplying a Charset argument.
The Stream interface extends AutoCloseable. The Files.lines method produces a stream whose close method closes the file. The easiest way to make sure the file in indeed closed is to use a Java 7 try-with-resources block:
When a stream spawns another, the close methods are chained.
When filteredLines is closed, it closes the underlying stream, which closes the underlying file.
Attach an onClose handler to be notified when the stream is closed.
If an IOException occurs as the stream fetches the lines, that exception is wrapped into an UncheckedIOException which is thrown out of the stream operation.
If you want to read lines from a source other than a file, use the BufferedReader.lines method instead:
With this method, closing the resulting stream does not close the reader. So you must place the BufferedReader object, and not the stream object, into the header of the try statement.
Streams of Directory Entries
Files.list: returns a Stream<Path> that reads the entries of a directory. The directory is read lazily, making it possible to efficiently process directories with huge numbers of entries.
The list method does not enter subdirectories.
Files.walk: processes all descendants of a directory.
You can limit the depth of the tree by calling Files.walk(pathToRoot, depth). Both walk methods have a varargs parameter of type FileVistOption..., but there is currently only one option you can supply: FOLLOW_LINKS to follow symbolic links.
If you filter the paths returned by walk and your filter criterion involves the file attributes stored with a directory, such as size, creation time, or type (file, directory, symbolic link), then use the find method instead. Call it with a predicate function that accepts a path and a BasicFileAttributes object. The only advantage is efficiency, since the directory is being read anyway, the attributes are readily available.
Base64 Encoding
The Base64 encoding encodes a sequence of bytes into a (longer) sequence of printable ASCII characters. Java 8 provides a standard encoder and decoder.
Normally, an encoded string has no line breaks, but the MIME standard used for email requires a “\r\n” every 76 characters.
For encoding, request a Base64.Encoder with one of the static methods getEncoder, getUrlEncoder, or getMimeEncoder of the Base64 class.
That class has methods to encode an array of bytes or a NIO ByteBuffer.
Alternatively, you can “wrap” an output stream, so that all data sent to it is automatically encoded.
To decode, reverse the operations:
Annotations
Annotations are tags inserted into the source code that some tools can process.
Java 8 has two enhancements to annotaion processing: repeated annotations and type use annotations. Moreover, reflection has been enhanced to report method parameter names. This has the potential to simplify annotations on method parameters.
Repeated Annotations
When annotations were first created, they were envisioned to mark methods and fields for processing.
In this context, it made no sense to apply same annotation twice. You can’t inject a field in two ways. Different annotations on the same element are fine and quite common:
Soon, more and more uses for annotations emerged, leading to situations where one would have liked to repeat the same annotation. E.g.,
Since that wasn’t possible, the annotations were packed into a container annotation, like this:
This is no longer necessary in Java 8. If your framework provider has enabled repeated annotations, you can just use them.
For a framework implementor, the AnnotatedElement interface has a method that gets the annotation of type T, if present.
What should the method do if multiple annotations of the same type are present? To solve this problem, the inventor of a repeatable annotation must
- Annotate the annotation as
@Repeatable - Provide a container annotation
E.g., for a simple unit testing framework, we might define a repeatable @TestCase annotation, to be used like this:
Here is how annotation can be defined:
Whenever the user supplies two or more @TestCase annotations, they are automatically wrapped into a @TestCases annotation.
When annotation processing code calls element.getAnnotation(TestCase.class) on the element representing the factorial method, null is returned. This is becasue the element is actually annotated with the container annotation TestCases.
When implementing an annotation processor for your repeatable annotation, you will find it simpler to use the getAnnotationsByType method. The call element.getAnnotationsByType(TestCase.class) “looks through” any TestCases container and gives you an array of TestCase annotations.
Type Use Annotations
Prior to Java 8, an annotation was applied to a declaration. A declaration is a part of code the introduces a new name.
In Java 8, you can annotate any type use. This can be useful in combination with tools that check for common programming errors. Suppose you annotated variables that you never want to be null as @NonNull. A tool can check that the following is correct:
The tool should detect any statement that might cause names to be null.
The null checker in the Checker Framework assumes that any nonlocal variables are implicitly @NonNull, but that local variables might be null unless the code shows otherwise. If a method may return a null, it needs to be annotated as @Nullable.
How can one express that the list elements should be non-null?
It is this kind of annotation that was not possible before Java 8 but has now become legal.
Type use annotations can appear in the following places:
- With generic type arguments:
List<@NonNull String>,Comparator.<@NonNull String>reverseOrder() - In any position of an array:
@NonNull String[][] words(words[i][j] is not null),String @NonNull [][] words(words is not null),String[] @NonNull [] words(words[i] is not null) - With superclasses and implemented interfaces:
class Image implements @Rectangular Shape - With constructor invocations:
new @Path String("/usr/bin") - With casts and
instanceOfchecks:(@Path String) input,if (input instanceOf @Path String). (The annotations are only for use by external tools. They have no effect on the behavior of a cast or aninstanceOfcheck.) - With exception specifications:
public Person read() throws @Localized IOException - With wildcards and type bounds:
List<@ReadOnly ? extends Person>,List<? extends @ReadOnly> Person - With method and constructor references:
@Immutable Person::getName
There are a few type positions that cannot be annotated:
It is also impossible to annotate an annotation.
More for extended type checking can be found at Checker Framework tutorial.
Method Parameter Reflection
The names of parameters are now available through reflection. Consider a typical JAX-RS method:
In almost all cases, the parameter names are the same as the annotation arguments, or they can be made to be the same. If the annotation processor could read the parameter names, then one could simply write
This is possible in Java 8, with the new class java.lang.relect.Parameter.
Unfortuantely, for the necessary information to appear in the classfile, the source must be compiled as javac -parameters SourceFile.java.
Miscellaneous Minor Changes
Null Checks
The Objects class has static predicate methods isNull and nonNull that can be useful for streams.
Lazy Messages
The log, logp, severe, warning, info, config, fine, finer, and finest methods of java.util.Logger class now support lazily constructed messages.
The message string is formatted even when the logging level is such that it would never be used. Instead, use
Now the lambda expression is only evaluated at the FINEST logging level, when the cost of the lambda invocation is presumably the least of one’s problems.
The requireNonNull of the Objects class also has a version that computes the message string lazily.
In the common case that directions is not null, this.directions is simply set to directions. If directions is null, the lambda is invoked, and a NullPointerException is thrown whose message is the returned string.
Regular Expressions
Java 7 introduced named capturing groups.
In Java 8, you can use the names in the start, end, and group methods of Matcher:
The Pattern class has a splitAsStream method that splits a CharSequence along a regular expression.
The method asPredicate can be used to filter strings that match a regular expression:
Locales
A locale specifies everything you need to know to present information to a user with local preferences concerning language, date formats, and so on.
A locale is composed of up to five components:
- A language, specified by two or three lowercase letters
- A script, specified by four letters with an intial uppercase
- A country, specified by two uppercase letters or three digits
- Optionally, a variant
- Optionally, an extension. Extensions describe local preferences for calendars, numbers, and so on
Since Java 7 you can simply call Locale.forLanguageTag("en-US"). Java 8 adds methods for finding locales that match user needs.
A language range is a string that denotes the locale characteristics that a user desires, with * for wildcards. One can optionally specify a weight between 0 and 1 when constructing a Locale.LanguageRange.
Given a list of weighted language ranges and a collection of locales, the filter method produces a list of matching locales, in descending order of match quality.
lookup: finds the best locale.
JDBC
In Java 8, JDBC has been updated to version 4.2.
The Date, Time, and Timestamp classes in the java.sql package have methods to convert from and to their java.time analogs LocalDate, LocalTime, and LocalDateTime.
The Statement class has a method executeLargeUpdate for executing an update whose row count exceeds Integer.MAX_VALUE.
JDBC 4.1 specified a generic method getObject(column, type) for Statement and ResultSet, where type is a Class instance. E.g., URL url = result.getObject("link", URL.class) retrieves a DATALINK as a URL. Now the corresponding setObject method is provided as well.

