BT

Facilitating the Spread of Knowledge and Innovation in Professional Software Development

Write for InfoQ

Topics

Choose your language

InfoQ Homepage News Everything About Java 8

Everything About Java 8

Leia em Português

This item in japanese

Lire ce contenu en français

TechEmpower, a custom application development company based in El Segundo, CA, has posted a blog entry titled "Everything about Java 8". The blog post is a comprehensive summary of the developer-facing changes coming in Java 8. Here's a quick overview of the blog post. Please visit the TechEmpower blog post for the complete details.

Interface improvements

Interfaces can now define static methods. For example, java.util.Comparator now has a static naturalOrder method.

    public static <T extends Comparable<? super T>> Comparator<T> naturalOrder() {
        return (Comparator<T>) Comparators.NaturalOrderComparator.INSTANCE;
    }

Interfaces can now provide default methods. This allows programmers to add new methods without breaking existing code that implements the interface. For example, java.lang.Iterable now has a default forEach method.

    public default void forEach(Consumer<? super T> action) {
        Objects.requireNonNull(action);
        for (T t : this) {
            action.accept(t);
        }
    }

Note that an interface cannot provide a default implementation for any of the methods of the Object class.

Functional interfaces

A functional interface is an interface that defines exactly one abstract method. The FunctionalInterface annotation has been introduced to indicate that an interface is intended to be a functional interface. For example, java.lang.Runnable is a functional interface.

    @FunctionalInterface
    public interface Runnable {
        public abstract void run();
    }

Note that the Java compiler will treat any interface meeting the definition as a functional interface regardless of whether or not the FunctionalInterface annotation is present.

Lambdas

The important property of functional interfaces is that they can be instantiated using lambdas. Lambda expressions enable you to treat functionality as method argument, or code as data. Here are a few examples of lambdas. The inputs are on the left and the code is on the right. The input types can be inferred and are optional.

    (int x, int y) -> { return x + y; }
    (x, y) -> x + y
    x -> x * x
    () -> x
    x -> { System.out.println(x); }

Here's an example of instantiating the Runnable functional interface.

    Runnable r = () -> { System.out.println("Running!"); }

Method references

Method references are compact lambda expressions for methods that already have a name. Here are examples of method references, with the equivalent lambda expression on the right.

    String::valueOf     x -> String.valueOf(x)
    Object::toString    x -> x.toString()
    x::toString         () -> x.toString()
    ArrayList::new      () -> new ArrayList<>()

Capturing versus non-capturing lambdas

Lambdas are said to be "capturing" if they access a non-static variable or object that was defined outside of the lambda body. For example, this lambda is accessing the variable x:

    int x = 5;
    return y -> x + y;

A lambda expression can only access local variables and parameters of the enclosing block that are final or effectively final.

java.util.function

A large number of new functional interfaces have been added to the java.util.function package. Here are a few examples:

  • Function<T, R> - take T as input, return R as output
  • Predicate<T> - take T as input, return a boolean as output
  • Consumer<T> - take T as input, don't return anything
  • Supplier<T> - take no input, return T
  • BinaryOperator<T> - take two T's as input, return one T as output

java.util.stream

The new java.util.stream package provides classes to support functional-style operations on streams of values. One common way to obtain a stream will be from a collection:

    Stream<T> stream = collection.stream();

Here's an example from the package Javadocs.

    int sumOfWeights = blocks.stream().filter(b -> b.getColor() == RED)
                                      .mapToInt(b -> b.getWeight())
                                      .sum();

Here we use a blocks Collection as a source for a stream, and then perform a filter-map-reduce on the stream to obtain the sum of the weights of the red blocks.

Stream can be infinite and stateful. They can be sequential or parallel. When working with streams, you first obtain a stream from some source, perform one or more intermediate operations, and then perform one final terminal operation. Intermediate operations include filter, map, flatMap, peel, distinct, sorted, limit, and substream. Terminal operations include forEach, toArray, reduce, collect, min, max, count, anyMatch, allMatch, noneMatch, findFirst, and findAny. One very useful utility class is java.util.stream.Collectors. It implements various reduction operations, such as converting streams into collections, and aggregating elements.

Generic type inference improvements

This improves the ability of the Java compiler to infer generic types and reduce explicit type-arguments in generic method calls. In Java 7, the code looks something like this:

    foo(Utility.<Type>bar());
    Utility.<Type>foo().bar();

In Java 8, improved inference in arguments and in chained calls allows you to write something like this:

    foo(Utility.bar());
    Utility.foo().bar();

java.time

The new date/time API is contained in the java.time package. All the classes are immutable and thread-safe. The date and time types include Instant, LocalDate, LocalTime, LocalDateTime and ZonedDateTime. Beyond dates and times, there are also the Duration and Period types. Additional value types include Month, DayOfWeek, Year, Month, YearMonth, MonthDay, OffsetTime and OffsetDateTime. Most of these new date/time classes are supported by JDBC.

Collections API additions

The ability of interfaces to have default methods allowed Java 8 to add a large number of new methods to the Collections API. Default implementations were provided on all the interfaces, and more efficient implementations were added to the concrete classes where applicable. Here's a list of the new methods:

  • Iterable.forEach(Consumer)
  • Iterator.forEachRemaining(Consumer)
  • Collection.removeIf(Predicate)
  • Collection.spliterator()
  • Collection.stream()
  • Collection.parallelStream()
  • List.sort(Comparator)
  • List.replaceAll(UnaryOperator)
  • Map.forEach(BiConsumer)
  • Map.replaceAll(BiFunction)
  • Map.putIfAbsent(K, V)
  • Map.remove(Object, Object)
  • Map.replace(K, V, V)
  • Map.replace(K, V)
  • Map.computeIfAbsent(K, Function)
  • Map.computeIfPresent(K, BiFunction)
  • Map.compute(K, BiFunction)
  • Map.merge(K, V, BiFunction)
  • Map.getOrDefault(Object, V)

Concurrency API additions

There were some additions to the concurrency API, some of which we will briefly discuss here. ForkJoinPool.commonPool() is the structure that handles all parallel stream operations. The common pool is used by any ForkJoinTask that is not explicitly submitted to a specified pool. ConcurrentHashMap has been completley rewritten. StampedLock is a new lock implementation that can be used as an alternative to ReentrantReadWriteLock. CompletableFuture is an implementation of the Future interface that provides methods for performing and chaining asynchronous tasks.

IO/NIO API additions

There are new IO/NIO methods, which are used to obtain java.util.stream.Stream from files and input streams.

  • BufferedReader.lines()
  • Files.list(Path)
  • Files.walk(Path, int, FileVisitOption...)
  • Files.walk(Path, FileVisitOption...)
  • Files.find(Path, int, BiPredicate, FileVisitOption...)
  • Files.lines(Path, Charset)
  • DirectoryStream.stream()

There is the new UncheckedIOException, an IOException that extends RuntimetimeException. There is also CloseableStream, a stream that can, and should be closed.

Reflection and annotation changes

With type annotations, annotations can be written in more places, such as generic type arguments like List<@Nullable String>. This enhances error detection by static analysis tools, which will strengthen and refine Java's built-in type system.

Nashorn JavaScript Engine

Nashorn is the new, lightweight, high-performance implementation of JavaScript integrated into the JDK. Nashorn is the successor to Rhino, with improved performance and memory usage. It will support the javax.script API, but will not include support for DOM/CSS nor include a browser plugin API.

Other miscellaneous additions to java.lang, java.util, and elsewhere

There are lot of other additions to other packages we have not yet mentioned. Here are a few of the notable ones. ThreadLocal.withInitial(Supplier) allows for a more compact thread-local variable declaration. The long overdue StringJoiner and String.join(...) are now part of Java 8. Comparator provides some new methods for doing chained and field-based comparisons. The default String pool map size is bigger at around 25-50K.

Please visit the Everything about Java 8 blog post for the complete details. The blog post was last updated on May 29, 2013.

Rate this Article

Adoption
Style

BT