Java 8 is a milestone release of Java that provides many features. This article focuses on The Lambda expressions and functional interfaces of Java 8.

Lambda expressions

Lambda expressions were introduced in Java to simplify code and make it more concise and elegant. As a result, code that uses Lambda expressions is generally shorter. How compact can Lambda expressions be written? Let’s look at a few examples below.

public void fun1(a) {
 Thread t = new Thread(() -> System.out.println("Hello"));
 t.start();
}

public void fun2(a) {  Integer[] arr = {1.4.3.8.6.9.2.7.0.5};  Arrays.sort(arr, (x, y) -> x - y);  System.out.println(Arrays.toString(arr)); } Copy the code

The above code is the use of two classic Lambda expressions, taking thread creation and sorting code, respectively. Without Lambda expressions, many people use anonymous inner classes to complete many more lines of redundant code, but the () -> {} form above is much cleaner.

Here are some common forms of Lambda expressions.

// No argument, no return value
() -> System.out.println("hello")

// No arguments, return value is 10
() - >10
 // There is one argument x -> x  // There are two arguments, the compiler automatically determines the type of argument (x, y) -> x + y  // There are two ints (int x, int y) -> x + y  // There are multiple statements with curly braces (x, y) -> {intsum = x + y; System.out.println(sum); }Copy the code

Based on the examples above, we can summarize the features of Lambda:

  • You can omit the parameter type and the compiler will recognize it automatically.

  • The parentheses can be added with or without a single argument, but more than one argument requires the parentheses.

  • If the body code has only one statement, braces are optional. If there are more than one statement, braces must be added.

Functional interface

Having said Lambda expressions, let’s talk about the implementation basis of Lambda expressions — functional interfaces. A functional interface must first and foremost be an interface that has one and only abstract method, but can have multiple non-abstract methods.

If you look at the Runnable interface definition, @functionalinterface refers to a FunctionalInterface. @functionalinterface is not required, but is just a convenience for the compiler to recognize. The Runnable interface has only one run() method, so the Runnable interface is a very typical functional interface.

@FunctionalInterface
public interface Runnable {
    public abstract void run(a);
}
Copy the code

When using the function’s Comparator interface, the abstract method does not have an abstract modifier, and neither of the methods has a method body. It is important to note that interfaces, except for the default method, are all abstract methods that are public abstract by default. Because Boolean equals(Object obj) is a method of the Object class, which the interface inherits by default, it does not count the interface’s abstract methods.

@FunctionalInterface
public interface Comparator<T> {
 int compare(T o1, T o2);
 boolean equals(Object obj);
.} Copy the code

Let’s take a look at a few common functional interfaces that Java 8 provides.

: Supplier interface

@FunctionalInterface
public interface Supplier<T> {
    T get(a);
}
Copy the code

Start with the simplest Supplier interface, which has a single get() method that has no arguments but returns a value. Here is a demo of the test. The code prints the test supplier string.

public void testSupplier(a) {
 Supplier<String> supplier = () -> "test supplier";
 System.out.println(supplier.get());
}
Copy the code

Consumer interface

@FunctionalInterface
public interface Consumer<T> {

    void accept(T t);

 default Consumer<T> andThen(Consumer<? super T> after) {  Objects.requireNonNull(after);  return (T t) -> { accept(t); after.accept(t); };  } } Copy the code

The idea of the Consumer interface is completely different from that of the Supplier interface. We can understand that Supplier is a data provider, while Consumer is a data Consumer, who needs us to provide data for consumption. In addition, the Consumer has an addThen method that controls the order in which the Consumer executes.

public void testConsumer(a) {
 Consumer<String> name = n -> System.out.println(
   "get name: " + n.split(",") [0]);
 Consumer<String> phone = p -> System.out.println(
   "get phone number: " + p.split(",") [1]);
 name.andThen(phone).accept("zhangsan,13912345678"); } Copy the code

The code prints the following string.

get name: zhangsan
get phone number: 13912345678
Copy the code

Predicate interface

@FunctionalInterface
public interface Predicate<T> {

    boolean test(T t);

 default Predicate<T> and(Predicate<? super T> other) {  Objects.requireNonNull(other);  return (t) -> test(t) && other.test(t);  }   default Predicate<T> negate(a) {  return(t) -> ! test(t); }   default Predicate<T> or(Predicate<? super T> other) {  Objects.requireNonNull(other);  return (t) -> test(t) || other.test(t);  }   static <T> Predicate<T> isEqual(Object targetRef) {  return (null == targetRef)  ? Objects::isNull  : object -> targetRef.equals(object);  } } Copy the code

Predicate is an assertion interface that takes an input parameter and returns a Boolean value. We’re trying to implement a demo that has two Predicate objects that return true as long as one of these conditions is satisfied, and the code will print true at the end.

public void testPredicate(a) {
 Predicate<String> s1 = s -> "predicate".equals(s);
 Predicate<String> s2 = s -> "consumer".equals(s);
 System.out.println(s1.or(s2).test("predicate"));
}
Copy the code

Function interface

@FunctionalInterface
public interface Function<T.R> {

    R apply(T t);

 default <V> Function<V, R> compose(Function<? super V, ? extends T> before) {  Objects.requireNonNull(before);  return (V v) -> apply(before.apply(v));  }   default <V> Function<T, V> andThen(Function<? super R, ? extends V> after) {  Objects.requireNonNull(after);  return (T t) -> after.apply(apply(t));  }   static <T> Function<T, T> identity(a) {  return t -> t;  } } Copy the code

The last is a functional interface that takes parameters and returns values. F2 concatenates a string and a number, and prints the result is 10.

public void testFunction(a) {
 Function<Integer, Integer> f1 = t -> 2 * t;
 Function<Integer, String> f2 = t -> "the result is " + t;
 System.out.println(f2.compose(f1).apply(5));
}
Copy the code

Like my article, please pay attention to my public account: “SKY Technical Training Guide”