JavaRush /Java Blog /Random EN /Java 8 Features – The Ultimate Guide (Part 1)
0xFF
Level 9
Донецк

Java 8 Features – The Ultimate Guide (Part 1)

Published in the Random EN group
The first part of the translation of the article Java 8 Features – The ULTIMATE Guide . The second part is here (link may change). Java 8 Features – The Ultimate Guide (Part 1) - 1 Editor's Note : This article was published while Java 8 was available to the public and all indications are that this is indeed a major version. Here we have provided Java Code Geeks guides in abundance such as Playing with Java 8 – Lambdas and Concurrency , Java 8 Date and Time API Guide: LocalDateTime and Abstract Class vs. Interface in the Java 8 Era . We also link to 15 must-read Java 8 tutorials from other sources . Of course we look at some of the downsides, such as the Dark Side of Java 8 . So, it's time to collect all the main features of Java 8 in one place for your convenience. Enjoy!

1. Introduction

Without a doubt, the release of Java 8 is the greatest event since Java 5 (released quite a long time ago, in 2004). It brought many new features to Java both in the language, compiler, libraries, tools and JVM (Java Virtual Machine). In this tutorial, we're going to take a look at these changes and demonstrate different use cases with real-world examples. The guide consists of several parts, each of which addresses a specific aspect of the platform:
  • Language
  • Compiler
  • Libraries
  • Tools
  • Runtime Environment (JVM)

2. New features in Java 8

In any case, Java 8 is a major release. We can say that it took so long because of the implementation of the features that every Java developer was looking for. In this section we are going to cover most of them.

2.1. Lambdas and Functional Interfaces

Lambdas (also known as private or anonymous methods) are the biggest and most anticipated language change in the entire Java 8 release. They allow us to specify functionality as a method argument (by declaring a function around it), or to specify code as data: concepts that every functional developer is familiar with. programming _ Many languages ​​on the JVM platform (Groovy, Scala , ...) had lambdas from day one, but Java developers had no choice but to represent lambdas through anonymous classes. Discussing the design of lambdas took a lot of time and effort from the public. But eventually compromises were found, which led to the emergence of new concise designs. In its simplest form, a lambda can be represented as a comma-separated list of parameters, a –> symbol , and a body. For example:
Arrays.asList( "a", "b", "d" ).forEach( e -> System.out.println( e ) )
Note that the type of the argument e is determined by the compiler. Additionally, you can explicitly specify the type of a parameter by wrapping the parameter in parentheses. For example:
Arrays.asList( "a", "b", "d" ).forEach( ( String e ) -> System.out.println( e ) );
In case the lambda body is more complex, it can be wrapped in curly braces, similar to a regular function definition in Java. For example:
Arrays.asList( "a", "b", "d" ).forEach( e -< {
    System.out.print( e );
    System.out.print( e );
} );
A lambda can refer to class members and local variables (implicitly making the call effective regardless of whether finalthe field is accessed or not). For example, these 2 snippets are equivalent:
String separator = ",";
Arrays.asList( "a", "b", "d" ).forEach(
    ( String e ) -> System.out.print( e + separator ) );
AND:
final String separator = ",";
Arrays.asList( "a", "b", "d" ).forEach(
    ( String e ) -> System.out.print( e + separator ) );
Lambdas can return a value. The return type will be determined by the compiler. A declaration returnis not required if the body of the lambda consists of one line. The two code snippets below are equivalent:
Arrays.asList( "a", "b", "d" ).sort( ( e1, e2 ) -> e1.compareTo( e2 ) );
AND:
Arrays.asList( "a", "b", "d" ).sort( ( e1, e2 ) -> {
    int result = e1.compareTo( e2 );
    return result;
} );
The language developers thought for a long time about how to make existing functions lambda-friendly. As a result, the concept of a functional interface emerged. A functional interface is an interface with only one method. As a result, it can be implicitly converted to a lambda expression. java.lang.Runnableand java.util.concurrent.Callabletwo great examples of functional interfaces. In practice, functional interfaces are very fragile: if someone adds even one other method to the interface definition, it will no longer be functional and the compilation process will not complete. To avoid this fragility and explicitly define the intent of an interface as functional, a special annotation was added in Java 8 @FunctionalInterface(all existing interfaces in the Java library received the @FunctionalInterface annotation). Let's look at this simple definition of a functional interface:
@FunctionalInterface
public interface Functional {
    void method();
}
There is one thing to keep in mind: default methods and static methods do not violate the principle of a functional interface and can be declared:
@FunctionalInterface
public interface FunctionalDefaultMethods {
    void method();

    default void defaultMethod() {
    }
}
Lambdas are the most popular feature of Java 8. They have all the potential to attract more developers to this wonderful platform and provide smart support for features in pure Java. For more detailed information, please refer to the official documentation .

2.2. Default Interfaces and Static Methods

Java 8 expanded the definition of interfaces with two new concepts: default method and static method. Default methods make interfaces somewhat similar to traits, but serve a slightly different purpose. They allow you to add new methods to existing interfaces without breaking backward compatibility for previously written versions of these interfaces. The difference between default methods and abstract methods is that abstract methods must be implemented while default methods are not. Instead, each interface must provide a so-called default implementation, and all descendants will receive it by default (with the ability to override this default implementation if necessary). Let's look at the example below.
private interface Defaulable {
    // Интерфейсы теперь разрешают методы по умолчанию,
    // клиент может реализовывать  (переопределять)
    // or не реализовывать его
    default String notRequired() {
        return "Default implementation";
    }
}

private static class DefaultableImpl implements Defaulable {
}

private static class OverridableImpl implements Defaulable {
    @Override
    public String notRequired() {
        return "Overridden implementation";
    }
}
The interface Defaulabledeclares a default method notRequired()using a keyword defaultas part of the method definition. One of the classes, DefaultableImpl, implements this interface leaving the default method as is. Another class, OverridableImpl, overrides the default implementation and provides its own. Another interesting feature introduced in Java 8 is that interfaces can declare (and offer implementations of) static methods. Here's an example:
private interface DefaulableFactory {
    // Interfaces now allow static methods
    static Defaulable create( Supplier<Defaulable> supplier ) {
        return supplier.get();
    }
}
A small piece of code combines the default method and the static method from the example above:
public static void main( String[] args ) {
    Defaulable defaulable = DefaulableFactory.create( DefaultableImpl::new );
    System.out.println( defaulable.notRequired() );

    defaulable = DefaulableFactory.create( OverridableImpl::new );
    System.out.println( defaulable.notRequired() );
}
The console output of this program looks like this:
Default implementation
Overridden implementation
The default method implementation in the JVM is very efficient and method invocation is supported by bytecode instructions. Default methods allowed existing Java interfaces to evolve without breaking the compilation process. Good examples are the many methods added to the interface java.util.Collection: stream(), parallelStream(), forEach(), removeIf(), ... Although being powerful, default methods should be used with caution: before declaring a default method, you should think twice about whether this is really necessary as this can lead to compilation ambiguities and errors in complex hierarchies. For more detailed information, please refer to the documentation .

2.3. Reference Methods

Reference methods implement useful syntax to reference existing methods or constructors of Java classes or objects (instances). Together with lambda expressions, reference methods make language constructs compact and concise, making it template-based. Below is a class Caras an example of different method definitions, let's highlight the four supported types of reference methods:
public static class Car {
    public static Car create( final Supplier<Car> supplier ) {
        return supplier.get();
    }

    public static void collide( final Car car ) {
        System.out.println( "Collided " + car.toString() );
    }

    public void follow( final Car another ) {
        System.out.println( "Following the " + another.toString() );
    }

    public void repair() {
        System.out.println( "Repaired " + this.toString() );
    }
}
The first reference method is a reference to a constructor with syntax Class::newor an alternative for generics Class< T >::new. Note that the constructor has no arguments.
final Car car = Car.create( Car::new );
final List<Car> cars = Arrays.asList( car );
The second option is a reference to a static method with the syntax Class::static_method. Note that the method takes exactly one parameter of type Car.
cars.forEach( Car::collide );
The third type is a reference to a method of an instance of an arbitrary object of a certain type with the syntax Class::method. Note that no arguments are accepted by the method.
cars.forEach( Car::repair );
And the last, fourth type is a reference to a method of an instance of a specific class with the syntax instance::method. Please note that the method only accepts one parameter of type Car.
final Car police = Car.create( Car::new );
cars.forEach( police::follow );
Running all of these examples as Java programs produces the following console output (class reference Carmay vary):
Collided com.javacodegeeks.java8.method.references.MethodReferences$Car@7a81197d
Repaired com.javacodegeeks.java8.method.references.MethodReferences$Car@7a81197d
Following the com.javacodegeeks.java8.method.references.MethodReferences$Car@7a81197d
For more detailed information and details of reference methods, please refer to the official documentation .

2.4. Duplicate annotations

Since Java 5 introduced support for annotations , this feature has become very popular and very widely used. However, one of the limitations of using annotations was the fact that the same annotation cannot be declared more than once in the same place. Java 8 breaks this rule and introduces duplicate annotations. This allows the same annotations to be repeated multiple times at the location where they are declared. Duplicate annotations should annotate themselves using annotation @Repeatable. In fact, this is not so much a change in the language as it is a compiler trick, while the technique remains the same. Let's look at a simple example:
package com.javacodegeeks.java8.repeatable.annotations;

import java.lang.annotation.ElementType;
import java.lang.annotation.Repeatable;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

public class RepeatingAnnotations {
    @Target( ElementType.TYPE )
    @Retention( RetentionPolicy.RUNTIME )
    public @interface Filters {
        Filter[] value();
    }

    @Target( ElementType.TYPE )
    @Retention( RetentionPolicy.RUNTIME )
    @Repeatable( Filters.class )
    public @interface Filter {
        String value();
    };

    @Filter( "filter1" )
    @Filter( "filter2" )
    public interface Filterable {
    }

    public static void main(String[] args) {
        for( Filter filter: Filterable.class.getAnnotationsByType( Filter.class ) ) {
            System.out.println( filter.value() );
        }
    }
}
As we can see, the class Filteris annotated with @Repeatable( Filters. class). Filtersis simply the owner of annotations Filter, but the Java compiler tries to hide their presence from developers. Thus, the interface Filterablecontains annotations Filterthat are declared twice (without mentioning Filters). The Reflection API also provides a new method getAnnotationsByType()for returning duplicate annotations of some type (remember that Filterable. class.getAnnotation( Filters. class) will return a compiler-injected instance Filters). The output of the program will look like this:
filter1
filter2
For more detailed information, please refer to the official documentation .

2.5. Improved type inference

The Java 8 compiler has received many type inference improvements. In many cases, explicit type parameters can be defined by the compiler, thereby making the code cleaner. Let's look at one example:
package com.javacodegeeks.java8.type.inference;

public class Value<T> {
    public static<T> T defaultValue() {
        return null;
    }

    public T getOrDefault( T value, T defaultValue ) {
        return ( value != null ) ? value : defaultValue;
    }
}
And here is the use with type Value<String>:
package com.javacodegeeks.java8.type.inference;

public class TypeInference {
    public static void main(String[] args) {
        final Value<String> value = new Value<>();
        value.getOrDefault( "22", Value.defaultValue() );
    }
}
The type parameter Value.defaultValue()is automatically determined and does not need to be provided explicitly. In Java 7, the same example will not compile and must be rewritten as <NOBR>Value.<String>defaultValue()</NOBR>.

2.6. Expanded annotation support

Java 8 expands the context where annotations can be used. Nowadays, almost anything can have an annotation: local variables, generic types, superclasses and implemented interfaces, even method exceptions. A few examples are presented below:
package com.javacodegeeks.java8.annotations;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import java.util.ArrayList;
import java.util.Collection;

public class Annotations {
    @Retention( RetentionPolicy.RUNTIME )
    @Target( { ElementType.TYPE_USE, ElementType.TYPE_PARAMETER } )
    public @interface NonEmpty {
    }

    public static class Holder<@NonEmpty T> extends @NonEmpty Object {
        public void method() throws @NonEmpty Exception {
        }
    }

    @SuppressWarnings( "unused" )
    public static void main(String[] args) {
        final Holder<String> holder = new @NonEmpty Holder<String>();
        @NonEmpty Collection<@NonEmpty String> strings = new ArrayList<>();
    }
}
ElementType.TYPE_USEand ElementType.TYPE_PARAMETERtwo new element types to describe the relevant annotation context. Annotation Processing APIhas also undergone minor changes to recognize new annotation types in Java.

3. New features in the Java compiler

3.1. Parameter names

Throughout time, Java developers have invented a variety of ways to store method parameter names in Java bytecode to make them available at runtime (for example, the Paranamer library ). Finally, Java 8 creates this difficult function in the language (using the Reflection API and method Parameter.getName()) and bytecode (using the new compiler argument javac:) –parameters.
package com.javacodegeeks.java8.parameter.names;

import java.lang.reflect.Method;
import java.lang.reflect.Parameter;

public class ParameterNames {
    public static void main(String[] args) throws Exception {
        Method method = ParameterNames.class.getMethod( "main", String[].class );
        for( final Parameter parameter: method.getParameters() ) {
            System.out.println( "Parameter: " + parameter.getName() );
        }
    }
}
If you compile this class without using an argument –parametersand then run the program, you will see something like this:
Parameter: arg0
With the parameter –parameterspassed to the compiler, the program output will be different (the actual name of the parameter will be shown):
Parameter: args
For advanced Maven users, the –parameters argument can be added to the compilation using the section maven-compiler-plugin:
<plugin>
    <groupId>org.apache.maven.plugins</groupId>
    <artifactId>maven-compiler-plugin</artifactId>
    <version>3.1</version>
    <configuration>
    <compilerArgument>-parameters</compilerArgument>
    <source>1.8</source>
    <target>1.8</target>
    </configuration>
</plugin>
To check the availability of parameter names there is a convenient isNamePresent()method provided by the class Parameter.

4. New Java Tools

Java 8 comes with a new set of command line tools. In this section we will look at the most interesting of them.

4.1. Nashorn engine: jjs

jjs is a standalone Nashorn engine that is command line based. It takes a list of JavaScript source code files and runs them. For example, let's create a func.js file with the following content:
function f() {
     return 1;
};

print( f() + 1 );
To run this file let's pass it as an argument to jjs :
jjs func.js
The console output will be like this:
2
For more details see the documentation .

4.2. Class Dependency Analyzer: jdeps

jdeps is a really great command line tool. It shows package or class level dependencies for Java classes. It accepts a .class file, folder or JAR file as input. By default , jdeps outputs dependencies to standard output (console). As an example, let's look at the dependency report of the popular Spring Framework library . To keep the example short, let's look at the dependencies for the JAR file only org.springframework.core-3.0.5.RELEASE.jar.
jdeps org.springframework.core-3.0.5.RELEASE.jar
This command outputs quite a lot, so we will only analyze part of the output. Dependencies are grouped by packages. If there are no dependencies, not found will be displayed .
org.springframework.core-3.0.5.RELEASE.jar -> C:\Program Files\Java\jdk1.8.0\jre\lib\rt.jar
   org.springframework.core (org.springframework.core-3.0.5.RELEASE.jar)
      -> java.io
      -> java.lang
      -> java.lang.annotation
      -> java.lang.ref
      -> java.lang.reflect
      -> java.util
      -> java.util.concurrent
      -> org.apache.commons.logging                         not found
      -> org.springframework.asm                            not found
      -> org.springframework.asm.commons                    not found
   org.springframework.core.annotation (org.springframework.core-3.0.5.RELEASE.jar)
      -> java.lang
      -> java.lang.annotation
      -> java.lang.reflect
      -> java.util
For more detailed information, please refer to the official documentation .
Comments
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION