16, Other new features of Java 8

Overview of new Java 8 features

Lambda expression

1. Comparison of lambda expression before and after use:
Example 1:
@Test
public void test1(){

Runnable r1 = new Runnable() {
    @Override
    public void run() {
        System.out.println("I Love Beijing Tiananmen ");
    }
};

r1.run();

System.out.println("***********************");

Runnable r2 = () -> System.out.println("I love the Forbidden City in Beijing");

r2.run();

}

Example 2:
@Test
public void test2(){

Comparator<Integer> com1 = new Comparator<Integer>() {
    @Override
    public int compare(Integer o1, Integer o2) {
        return Integer.compare(o1,o2);
    }
};

int compare1 = com1.compare(12,21);
System.out.println(compare1);

System.out.println("***********************");
//How to write Lambda expressions
Comparator<Integer> com2 = (o1,o2) -> Integer.compare(o1,o2);

int compare2 = com2.compare(32,21);
System.out.println(compare2);


System.out.println("***********************");
//Method reference
Comparator<Integer> com3 = Integer :: compare;

int compare3 = com3.compare(32,21);
System.out.println(compare3);

}
2. Basic syntax of lambda expression:

  • 1. For example: (O1, O2) - > integer compare(o1,o2);
  • 2. Format:
  •  -> :lambda Operator or arrow operator
    
  •  ->Left: lambda Formal parameter list (in fact, it is the formal parameter list of the abstract method in the interface)
    
  •  ->right: lambda Body (in fact, it is the method body of the overridden abstract method
    

3. How to use: there are six cases

Summarize six situations:
->On the left: the parameter type of lambda parameter list can be omitted (type inference); If the lambda parameter list has only one parameter, its pair () can also be omitted
->Right: lambda body should be wrapped with a pair of {}; If the lambda body has only one execution statement (possibly a return statement), omit this pair of {} and return keywords

Functional interface

1. Instructions for functional interface

If only one abstract method is declared in an interface, the interface is called a functional interface.
We can use the @ functional interface annotation on an interface to check whether it is a functional interface.
The essence of Lambda expressions: as instances of functional interfaces

2. Four basic functional interfaces provided by Lambda expressions in Java 8:
Specific use:

3. Summary
3.1 when to use lambda expressions?
When you need to instantiate a functional interface, you can use lambda expressions.
3.2 when to use a given functional interface?
If we need to define a functional interface during development, first look at whether the functional interface provided in the existing jdk is provided
Functional interface that can meet the requirements. If yes, you can call it directly. You don't need to customize it yourself.

Method reference

Method reference
1. Understand:
Method reference can be regarded as a deep expression of Lambda expression. In other words, a method reference is a Lambda expression, that is, an instance of a functional interface, pointing to a method by its name.

2. Use situation:
When the operation to be passed to the Lambda body has implemented the method, you can use the method reference!

3. Format:
Class (or object): method name

4. There are three situations as follows:

  • Case 1 object:: non static method
  • Case 2: static method
  • Case 3: non static method

5. Requirements:

The parameter list and return value type of the abstract method in the interface are required to be the same as the parameter list and return value type of the method referenced by the method! (for cases 1 and 2)
When the first parameter of the functional interface method is the caller who needs to reference the method, and the second parameter is the parameter (or no parameter) that needs to reference the method: ClassName::methodName (for case 3)

6. Suggestions for use:
If providing an instance to a functional interface just meets the use situation of method reference, you can consider using method reference to provide an instance to a functional interface. If you are not familiar with method references, you can also use lambda expressions.

7. Use examples:
//Case 1: object: instance method
//Void accept in Consumer
//Void println in PrintStream
@Test
public void test1() {
Consumer con1 = str -> System.out.println(str);
con1.accept("Beijing");

System.out.println("*******************");
PrintStream ps = System.out;
Consumer<String> con2 = ps::println;
con2.accept("beijing");

}

//T get() in Supplier
//String getName() in Employee
@Test
public void test2() {
Employee emp = new Employee(1001,"Tom",23,5600);

Supplier<String> sup1 = () -> emp.getName();
System.out.println(sup1.get());

System.out.println("*******************");
Supplier<String> sup2 = emp::getName;
System.out.println(sup2.get());

}

//Case 2: Class: static method
//Int compare in Comparator (t T1, t T2)
//Int compare in Integer (t T1, t T2)
@Test
public void test3() {
Comparator com1 = (t1,t2) -> Integer.compare(t1,t2);
System.out.println(com1.compare(12,21));

System.out.println("*******************");

Comparator<Integer> com2 = Integer::compare;
System.out.println(com2.compare(12,3));

}

//R apply in Function (T)
//Long round(Double d) in Math
@Test
public void test4() {
Function<Double,Long> func = new Function<Double, Long>() {
@Override
public Long apply(Double d) {
return Math.round(d);
}
};

System.out.println("*******************");

Function<Double,Long> func1 = d -> Math.round(d);
System.out.println(func1.apply(12.3));

System.out.println("*******************");

Function<Double,Long> func2 = Math::round;
System.out.println(func2.apply(12.6));

}

//Case: Class:: instance method (difficulty)
//Int comapre in Comparator (t T1, t T2)
//Int T1 in String compareTo(t2)
@Test
public void test5() {
Comparator com1 = (s1,s2) -> s1.compareTo(s2);
System.out.println(com1.compare("abc","abd"));

System.out.println("*******************");

Comparator<String> com2 = String :: compareTo;
System.out.println(com2.compare("abd","abm"));

}

//Boolean test in BiPredicate (t T1, t T2);
//Boolean T1 in String equals(t2)
@Test
public void test6() {
BiPredicate<String,String> pre1 = (s1,s2) -> s1.equals(s2);
System.out.println(pre1.test("abc","abc"));

System.out.println("*******************");
BiPredicate<String,String> pre2 = String :: equals;
System.out.println(pre2.test("abc","abd"));

}

//R apply in Function (T)
//String getName() in Employee;
@Test
public void test7() {
Employee employee = new Employee(1001, "Jerry", 23, 6000);

Function<Employee,String> func1 = e -> e.getName();
System.out.println(func1.apply(employee));

System.out.println("*******************");

Function<Employee,String> func2 = Employee::getName;
System.out.println(func2.apply(employee));

}

Constructor reference and array reference

1. Constructor reference format:
Class name:: new

2. Constructor reference requirements:
Similar to method references, the formal parameter list of abstract methods of functional interfaces is consistent with that of constructors. The return value type of the abstract method is the type of the class to which the constructor belongs

3. Constructor reference example:
//T get() in Supplier
//Empty parameter constructor for Employee: Employee()
@Test
public void test1(){

   Supplier<Employee> sup = new Supplier<Employee>() {
       @Override
       public Employee get() {
           return new Employee();
       }
   };
   System.out.println("*******************");

   Supplier<Employee>  sup1 = () -> new Employee();
   System.out.println(sup1.get());

   System.out.println("*******************");

   Supplier<Employee>  sup2 = Employee :: new;
   System.out.println(sup2.get());

}

//R apply in Function (T)
@Test
public void test2(){
Function<Integer,Employee> func1 = id -> new Employee(id);
Employee employee = func1.apply(1001);
System.out.println(employee);

   System.out.println("*******************");

   Function<Integer,Employee> func2 = Employee :: new;
   Employee employee1 = func2.apply(1002);
   System.out.println(employee1);

}

//R apply (T, t, u, U) in BiFunction
@Test
public void test3(){
BiFunction<Integer,String,Employee> func1 = (id,name) -> new Employee(id,name);
System.out.println(func1.apply(1001,"Tom"));

   System.out.println("*******************");

   BiFunction<Integer,String,Employee> func2 = Employee :: new;
   System.out.println(func2.apply(1002,"Tom"));

}

4. Array reference format:
Array type []: new

5. Example of array reference:
//R apply in Function (T)
@Test
public void test4(){
Function<Integer,String[]> func1 = length -> new String[length];
String[] arr1 = func1.apply(5);
System.out.println(Arrays.toString(arr1));

System.out.println("*******************");

Function<Integer,String[]> func2 = String[] :: new;
String[] arr2 = func2.apply(10);
System.out.println(Arrays.toString(arr2));

}

Stream API

1. Understanding of stream API:
1.1 Stream focuses on data operation and dealing with CPU
Collections focus on the storage of data and deal with memory

1.2 java8 provides a set of APIs, which can be used to filter, sort, map, reduce and other operations on the data in memory. This is similar to sql operations on tables in a database.

2. Precautions:

  • ① Stream itself does not store elements.
  • ② Stream does not change the source object. Instead, they return a new stream that holds the result.
  • ③ Stream operations are deferred. This means that they wait until the results are needed.

3. Use process of stream:

  • ① Instantiation of Stream
  • ② A series of intermediate operations (filtering, mapping,...)
  • ③ Terminate operation

4. Precautions for using the process:

  • 4.1 an intermediate operation chain to process the data of the data source
  • 4.2 once the termination operation is executed, the intermediate operation chain is executed and the results are generated. After that, it will not be used again

5. Step 1: Stream instantiation
//Method 1 of creating Stream: through collection
@Test
public void test1(){
List employees = EmployeeData.getEmployees();

//Default stream (): returns a sequential stream
Stream stream = employees.stream();

//default Stream parallelStream(): returns a parallel stream
Stream parallelStream = employees.parallelStream();

}

//Method 2 of creating Stream: through array
@Test
public void test2(){
    int[] arr = new int[]{1,2,3,4,5,6};
    //Call static < T > stream < T > stream (t [] array) of Arrays class: return a stream
    IntStream stream = Arrays.stream(arr);

    Employee e1 = new Employee(1001,"Tom");
    Employee e2 = new Employee(1002,"Jerry");
    Employee[] arr1 = new Employee[]{e1,e2};
    Stream<Employee> stream1 = Arrays.stream(arr1);

}
//Method 3 of creating a Stream: through of()
@Test
public void test3(){

    Stream<Integer> stream = Stream.of(1, 2, 3, 4, 5, 6);

}

//Create Stream mode 4: create infinite Stream
@Test
public void test4(){

//Iteration
// public static Stream iterate(final T seed, final UnaryOperator f)
//Traverse the first 10 even numbers
Stream.iterate(0, t -> t + 2).limit(10).forEach(System.out::println);

//Generate
// public static Stream generate(Supplier s)
Stream.generate(Math::random).limit(10).forEach(System.out::println);

}

6. Step 2: intermediate operation

7. Step 3: terminate the operation

Collector s need to provide instances using Collectors.

Use of Optional class

java.util.Optional class
1. Understanding: born to solve the null pointer problem in java!
The optional class (java.util.Optional) is a container class that can store the value of type T, representing the existence of this value. Or just save null
, indicating that this value does not exist. Originally, null was used to indicate that a value does not exist. Now Optional can better express this concept. And can avoid
Null pointer exception free.

2. Common methods:
@Test
public void test1(){
//empty(): value = null inside the created Optional object
Optional op1 = Optional.empty();
if(!op1.isPresent()){//Optional whether the encapsulated data contains data
System.out.println("data is empty");

    }
    System.out.println(op1);
    System.out.println(op1.isPresent());
    //If the Optional encapsulated data value is empty, get() will report an error. Otherwise, value is returned when value is not null

// System.out.println(op1.get());

}

@Test
public void test2(){
    String str = "hello";

// str = null;
//Of (T): encapsulates data t to generate an Optional object. It is required that t is not empty, otherwise an error will be reported.
Optional op1 = Optional.of(str);
//get() is usually used with the of() method. Used to get the internal encapsulated data value
String str1 = op1.get();
System.out.println(str1);

}

@Test
public void test3(){
    String str = "beijing";
    str = null;
    //Ofnullable (T): encapsulates the value that the data t is assigned to the internal part of Optional. Don't ask t to be non empty
    Optional<String> op1 = Optional.ofNullable(str);
    //orElse(T t1): if the value inside Optional is not empty, this value will be returned. If
    //If value is null, t1.0 is returned
    String str2 = op1.orElse("shanghai");

    System.out.println(str2);//


}

3. Typical exercises:
It can ensure that there will be no null pointer exception during the execution of the following methods.
//Use getGirlName() of the Optional class:
public String getGirlName2(Boy boy){

Optional<Boy> boyOptional = Optional.ofNullable(boy);
//boy1 must not be empty at this time
Boy boy1 = boyOptional.orElse(new Boy(new Girl("Delireba")));

Girl girl = boy1.getGirl();

Optional<Girl> girlOptional = Optional.ofNullable(girl);
//girl1 must not be empty
Girl girl1 = girlOptional.orElse(new Girl("Gulinaza"));

return girl1.getName();

}

@Test
public void test5(){
Boy boy = null;
boy = new Boy();
boy = new Boy(new Girl("Mr. Cang");
String girlName = getGirlName2(boy);
System.out.println(girlName);

}

Added by ronniebrown on Wed, 05 Jan 2022 07:31:06 +0200