Java8新特性——函數式接口和Lambda表達式

 

      函數式接口是爲Java8中的lambda而設計的,lambda表達式的方法體就是函數接口的實現。

一、什麼是函數式接口?

       函數式接口是隻包含一個方法的抽象接口。比如:Java標準庫中的Java.lang.Runnable,java.util.Callable就是典型的函數式接口。

二、如何使用函數是接口?

       在Java8中通過@FunctionalInterface進行註解,將一個接口的標註爲函數式接口,該接口只包含一個抽象方法。 @Functionallnterface註解不是必須的,只要接口只包含一個抽象方法,虛擬機會自動判斷該接口爲函數式接口。 一般建議在接口上使用@Functionallnterface註解進行聲明,以免他人錯誤的往接口中添加新的方法。當使用該註解後,如果在該接口中定義了第二個抽象方法的話,編譯器會報錯。

函數式接口爲我們提供了四大內置核心函數式接口:

package Lambda;

import org.junit.Test;

import java.util.*;
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.function.Predicate;
import java.util.function.Supplier;

/*
*Java8 內置的四大核心函數式接口
*
*Consumer<T> : 消費型接口
*   void accept(T t);
*Supplier<T> : 供給型接口
*   T get();
*Funcation<T,R> : 函數型接口
*   R apply(T t);
*Predicate<T> : 斷言型接口
*   Boolean test(T t);
*
* */
public class FuncationCoreInterface {
//  Consumer<T> : 消費型接口
    @Test
    public void fun1(){
        happy(100000,(m) -> System.out.println("今天消費:" +m+"元。"));
    }
    public void happy(double money, Consumer<Double>consumer){
        consumer.accept(money);
    }
//  Supplier<T> : 供給型接口
    @Test
    public void fun2(){
        List<Integer> numList = getNumList(10,() -> (int)(Math.random()*100));
        for (Integer num:numList) {
            System.out.println(num);
        }
    }
//  需求:產生指定個數的整數,並放入集合中
    public List<Integer> getNumList(int num, Supplier<Integer> sup){
        List<Integer> list = new ArrayList<>();
        for (int i =0;i<num;i++){
            Integer n = sup.get();
            list.add(n);
        }
        return list;
    }
//  Funcation<T,R> : 函數型接口
    @Test
    public void fun3(){
        String s1 = strHandler("\t\t\t\t\t hdfs", (s) -> s.trim());
        System.out.println(s1);
        String s2 = strHandler("common", (s) -> s.substring(2, 4));
        System.out.println(s2);

    }
//  需求:用於處理字符串
    public String strHandler(String str, Function<String,String> fun){
        return fun.apply(str);
    }
//  Predicate<T> : 斷言型接口
    @Test
    public void fun4(){
        List<String> list = Arrays.asList("hello", "lanbda", "world", "java", "ok");
        List<String> list1 = filterStr(list, (s) -> s.length() <= 4);
        for (String str:list1) {
            System.out.println(str);
        }
    }
//  需求:將滿足條件的字符串,放入到集合中
    public List<String> filterStr(List<String> list, Predicate<String> pre){
        List<String> strList = new ArrayList<>();

        for (String str:list) {
            if (pre.test(str)){
                strList.add(str);
            }
        }
        return strList;
    }
}

三、什麼是Lambda表達式?

         Lambda表達式是一個匿名函數。可以將它看做匿名內部類,但從虛擬機的角度來看,並不是如此,因爲Lambda表達式在編譯的時候,並不會生成xxx$1.class的匿名類,而是通過動態綁定,在運行的時候再調用,因此避免了在編譯時生成匆匆而影響jvm的加載速度。

四、怎麼使用lambda表達式?

       使用Lambda表達式的時候,接口必須是函數式接口。

基本語法:(<函數式接口> <變量名> = (參數1,參數2...) -> { //方法體 }

       Java8中引入了一個新的操作符“->”該操作符稱爲箭頭操作符或者Lambda操作符,箭頭操作符將Lambda表達式拆分成兩個部分:

      左側:Lambda表達式的參數列表。

      右側:Lambda表達式所需執行的內容,即Lambda體。

      語法格式一:無參數,無返回值。()  -> System.out.println("Hello Lambda");

   

package Lambda;
/*接口定義,無參無返回值*/
@FunctionalInterface
public interface ICompute {
    public void print();
}
package Lambda;

public class testAnonymousInnerClass {
    public static void main(String[] args) {
        /*傳統內部類測試*/
        int i = 12;
        hello(new ICompute() {
            @Override
            public void print() {
                System.out.println(i);
                System.out.println("Fuck");
            }
        });
        /*lambda測試*/
        hello(()-> System.out.println("Hello lambda"));
    }
    /*定義使用該接口的方法*/
    private static void hello(ICompute iCompute) {
        iCompute.print();
    }
}

      語法格式二:有一個參數,並且無返回值。(x)  -> System.out.println(x);

package Lambda;
@FunctionalInterface
public interface HelloInterface {

    void sayHello(String message);
}
package Lambda;

public class Test {
    public static void main(String[] args) {
        sayHello("peter",x-> System.out.println("Hello "+x));
    }

    private static void sayHello(String message,HelloInterface helloInterface){
           helloInterface.sayHello(message);
    }
}

      語法格式三:若只有一個參數,小括號可以省略不寫。x  -> System.out.println(x);

 

      語法格式四:有兩個以上的參數,有返回值,並且Lambda體中有多條語句。                           

Comparator<Integer> com = (x, y) -> {

     System.out.println("函數式接口");

     return Integer.compare(x, y);

  };

        語法格式五:若Lambda體重只有一條語句,return和大括號都可以省略不寫。

                             

Comparator<Integer> com = (x, y) -> Integer.compare(x, y);

語法格式六 : Lambda 表達式的參數列表的數據類型可以省略不寫,因爲JVM編譯器通過上下文推斷出,數據類型,即“類型推 斷”

(Integer x, Integer y) -> Integer.compare(x, y);

注 : Lambda 表達式中的參數類型都是由編譯器推斷得出的。 Lambda 表達式中無需指定類型,程序依然可以編譯,這是因爲 javac 根據程序的上下文,在後臺推斷出了參數的類型。 Lambda 表達式的類型依賴於上下文環境,是由編譯器推斷出來的。這就是所謂的 “類型推斷”

上聯 : 左右遇一括號省                下聯 : 左側推斷類型省                橫批 : 能省則省

package Lambda;

import org.junit.Test;

import java.util.Comparator;
import java.util.function.Consumer;

public class LambdaParam {
    @Test
    public void test1(){
//        無參數,無返回值
        final int num = 0; //jdk 1.7前,必須是final
        Runnable runnable = new Runnable() {
            @Override
            public void run() {
                System.out.println("HelloWorld" + num);
            }
        };
        System.out.println("========我是分割線=========");
        Runnable r1 = () -> System.out.println("HelloLambda");
    }
//  一個參數,無返回值
    @Test
    public void test2(){
        Consumer<String> con = (x) -> System.out.println(x);
        con.accept("哈哈哈哈");
        Consumer<String> con1 = x -> System.out.println(x);
        con1.accept("嘖嘖嘖嘖");
     }
//  有兩個參數,有返回值,Lambda體中有多條語句
    @Test
    public void test3(){
        Comparator<Integer> com = (x,y) -> {
            System.out.println("函數式接口");
            return Integer.compare(x,y);
        };
//  如果只有一條語句,大括號和return都可以不寫
        Comparator<Integer> com1 = (x,y) -> Integer.compare(x,y);
    }
//  Lambda表達式的參數列表可以省略不寫,因爲jvm編譯器可以自動推斷出參數類型  
    @Test
    public void test4(){
        Comparator<Integer> com = (x,y) -> Integer.compare(x,y);
        Comparator<Integer> com1 = (Integer x,Integer y) -> Integer.compare(x,y);
    }
}

Lambda表達式的應用:

        需求:

              1、獲取公司中員工年齡大於35歲的信息。

              2、獲取當前公司中員工工資大於5000的員工信息。

//實體類

package Lambda.LambdaCase;

public class Employee {
    private String name;
    private Integer age;
    private Double salary;

    public Employee() {
    }

    public Employee(String name, Integer age, Double salary) {
        this.name = name;
        this.age = age;
        this.salary = salary;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public Integer getAge() {
        return age;
    }

    public void setAge(Integer age) {
        this.age = age;
    }

    public Double getSalary() {
        return salary;
    }

    public void setSalary(Double salary) {
        this.salary = salary;
    }

    @Override
    public String toString() {
        return "Employee{" +
                "name='" + name + '\'' +
                ", age=" + age +
                ", salary=" + salary +
                '}';
    }
}
//最原始的方法

package Lambda.LambdaCase;

import org.junit.Test;

import java.util.*;

public class TestLambda {
 //    需求:獲取公司中員工年齡大於35的員工信息
List<Employee> employees = Arrays.asList(
    new Employee("張三",18,9999.99),
    new Employee("李四",19,8888.88),
    new Employee("王五",100,1.11),
    new Employee("趙六",80,2.22),
    new Employee("田七",20,9898.98)
    );

    @Test
    public void test3(){
        List<Employee> list = filterEmployees1(employees);
        for (Employee emp:list) {
            System.out.println(emp);
        }
        System.out.println("============我是分割線===========");
        List<Employee> list1 = filterEmployees2(employees);
        for (Employee emp:list1) {
            System.out.println(emp);
        }
    }

    public List<Employee> filterEmployees1(List<Employee> list) {
        List<Employee> emps = new ArrayList<>();
        for (Employee emp:list) {
            if (emp.getAge() >= 35){
                emps.add(emp);
            }
        }
        return emps;
    }
//    需求:獲取當前公司中員工工資大於5000的員工信息,和上面的程序相差不多,產生大量的重複代碼
    public List<Employee> filterEmployees2(List<Employee> list) {
        List<Employee> emps = new ArrayList<>();
        for (Employee emp:list) {
            if (emp.getSalary()>=5000) {
                emps.add(emp);
            }
        }
        return emps;
    }
}

//優化方式一:對Employee集合按照Predicate的方式進行過濾(策略設計模式)
    @Test
    public void test4(){
        List<Employee> list = filterEmployee(employees, new FilterEmployeeByAge());
        for (Employee employee:list) {
            System.out.println(employee);
        }
        System.out.println("============我是分割線===========");
        List<Employee> list1 = filterEmployee(employees, new FilterEmployeeBySalary());
        for (Employee employee:list1) {
            System.out.println(employee);
        }
    }
    public List<Employee> filterEmployee(List<Employee>list,MyPredicate<Employee> mp){
        List<Employee> emps = new ArrayList<>();
        for (Employee emp:list) {
            if (mp.test(emp)){
                emps.add(emp);
            }
        }
        return emps;
    }
//需求1

package Lambda.LambdaCase;

public class FilterEmployeeByAge implements MyPredicate<Employee> {
    @Override
    public boolean test(Employee employee) {

        return employee.getAge()>=35;
    }
}
//需求二

package Lambda.LambdaCase;

public class FilterEmployeeBySalary implements MyPredicate<Employee> {
    @Override
    public boolean test(Employee employee) {

        return employee.getSalary()>=35;
    }
}
//定義接口

package Lambda.LambdaCase;

public interface MyPredicate<T> {
    public boolean test(T t);
}
//優化方式二:匿名內部類,省略創建的類MyPredicate
    @Test
    public void test5(){
        List<Employee> list = filterEmployee(employees, new MyPredicate<Employee>() {
            @Override
            public boolean test(Employee employee) {
                return employee.getSalary() <= 5000;
            }
        });
        for (Employee employee:list) {
            System.out.println(employee);
        }
    }
//優化方式三:Lanbda表達式
    @Test
    public void test6(){
        List<Employee> list = filterEmployee(employees, (e) -> e.getSalary() <= 5000);
        list.forEach(System.out::println);
    }
//優化方式四:Stream API
    @Test
    public void test7(){
        employees.stream()
                  .filter((e) -> e.getSalary() >= 5000)
                  .limit(2)
                  .forEach(System.out::println);
        System.out.println("============我是分割線===========");
        employees.stream()
                  .map(Employee::getName)
                  .forEach(System.out::println);
    }

 

 

 

 

 

 

 

 

 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章