Java
是一門強大的面向對象的語言,除了8
種基本的數據類型,其餘一切皆爲對象。所以,在Java
中定義函數或方法都離不開對象,也就意味着很難直接將方法或函數像參數同樣傳遞,而Java8
中的Lambda
表達式解決了這個問題。java
Lambda
?簡單的來講,引入Lambda
就是爲了簡化代碼,容許把函數做爲一個方法的參數傳遞進方法中。git
示例:若是想把某個接口的實現類做爲參數傳遞給一個方法會怎麼作?github
Java8
之前public static void general() { // 用匿名內部類的方式來建立線程 new Thread(new Runnable() { @Override public void run() { System.out.println("公衆號:風塵博客!"); } }).run(); }
Lambda
寫法public static void lambda() { // 使用Lambda來建立線程 new Thread(() -> System.out.println("公衆號:風塵博客!")).run(); }
Lambda
表達式是什麼?Java
中,將方法做爲參數進行傳遞的方式被稱爲Lambda
表達式。編程
Lambda
表達式語法結構Lambda
實際上是一個箭頭函數,也可稱爲匿名函數:->
數組
箭頭操做符將Lambda
表達式分紅了兩部分:app
Lambda
表達式的參數列表(接口中抽象方法的參數列表)Lambda
表達式中所需執行的功能(Lambda
體,對抽象方法的實現)Lambda
體只需一條語句。public static void noParam() { Runnable r1 = () -> System.out.println("noParam Test!"); r1.run(); }
Lambda
須要一個參數,參數的小括號能夠省略。public static void oneParam() { // Consumer<String> con = (s) -> System.out.println(s); // 參數的小括號能夠省略。 Consumer<String> con = s -> System.out.println(s); con.accept("oneParam Test!"); }
Lambda
須要多個參數,而且有返回值。public static void params() { Comparator<Integer> com = (x, y) -> { System.out.println("函數式接口"); // 比較x/y的大小 return Integer.compare(x, y); }; System.out.println(com.compare(1, 2)); }
Lambda
體只有一條語句時,return
與大括號能夠省略。public static void one() { Comparator<Integer> com = (x, y) -> Integer.compare(x, y); System.out.println(com.compare(1, 2)); }
上面幾條示例好像有一個共性:參數列表的數據類型都沒寫,這是爲何呢?dom
Lambda
表達式中的參數類型都是由編譯器推斷得出的。ide
public static void typeInference() { //Integer 類型能夠省略 Comparator<Integer> com = (Integer x,Integer y) -> { System.out.println("函數式接口"); return Integer.compare(x, y); }; // 類型推斷 BinaryOperator<Long> addImplicit = (x, y) -> x + y; }
Lambda
表達式中無需指定類型,程序依然可 以編譯,這是由於 javac
根據程序的上下文,在後臺 推斷出了參數的類型。Lambda
表達式的類型依賴於上下文環境,是由編譯器推斷出來的。函數式編程
Lambda
表達式使得Java
擁有了函數式編程的能力,但在Java
中Lambda
表達式是對象,它必須依附於一類特別的對象類型——函數式接口(functional interface
)。函數
函數接口是隻有一個抽象方法的接口,用做 Lambda
表達式的類型。使用@FunctionalInterface
註解修飾的類,編譯器會檢測該類是否只有一個抽象方法或接口,不然,會報錯。能夠有多個默認方法,靜態方法。
JDK8
在 java.util.function
中定義了幾個標準的函數式接口,供咱們使用。
Java
內置四大核心函數式接口函數式接口 | 參數類型 | 返回類型 | 用途 |
---|---|---|---|
Consumer<T> | T | void | 對類型爲T的對象應用操做,包含方法:void accept(T t) |
Supplier<T> | 無 | T | 返回類型爲T的對象,包 含方法:T get(); |
Function<T,R> | T | R | 對類型爲T的對象應用操做,並返回結果。結果是R類型的對象。包含方法:R apply(T t); |
Predicate<T> | T | boolean | 肯定類型爲T的對象是否知足某約束,並返回 boolean 值。包含方法 boolean test(T t); |
void accept(T t);
consumerDemo(3, s -> System.out.println(s * 3)); public static void consumerDemo(Integer value, Consumer<Integer> consumer) { consumer.accept(value); }
T get();
// 生成10個之內的隨機書 List<Integer> numList = supplierDemo(10, () -> (int)(100 * Math.random())); System.out.println(numList); public static List<Integer> supplierDemo(int num, Supplier<Integer> supplier) { List<Integer> list = new ArrayList<>(); for (int i = 0; i < num; i++) { Integer n = supplier.get(); list.add(n); } return list; }
R apply(T t);
// 處理字符串 String str1 = functionDemo("Hello!風塵博客", s -> s.substring(6)); System.out.println(str1); String str2 = functionDemo("vanDusty", s -> s.toUpperCase()); System.out.println(str2); public static String functionDemo(String str, Function<String, String> function) { return function.apply(str); }
boolean test(T t);
// 將知足條件的字符串放入集合 List<String> list = Arrays.asList("hello", "van", "function", "predicate"); List<String> newList = predicateDemo(list, s -> s.length() > 5); System.out.println(newList); public static List<String> predicateDemo(List<String> list, Predicate<String> predicate) { List<String> newList = new ArrayList<>(); for (String s : list) { if (predicate.test(s)) { newList.add(s); } } return newList; }
咱們能夠在任意函數式接口上使用 @FunctionalInterface
註解, 這樣作能夠檢查它是不是一個函數式接口,同時 javadoc
也會包含一條聲明,說明這個接口是一個函數式接口。
// 字符串轉大寫 String newStr = selfFunctionalInterface((str) -> str.toUpperCase(), "abc"); System.out.println(newStr); public static String selfFunctionalInterface(SelfFunctionalInterface<String> selfFunctionalInterface, String str) { return selfFunctionalInterface.getValue(str); }
方法引用是指經過方法的名字來指向一個方法。
實例對象名::實例方法名
private static void instanceMethod() { UserDomain user = new UserDomain(1L, "Van"); Supplier<String> sup = () -> user.getUserName(); System.out.println(sup.get()); // 等同於 Supplier<String> supplier = user::getUserName; System.out.println(supplier.get()); }
類名::靜態方法名
private static void staticMethod() { Comparator<Integer> com = (x, y) -> Integer.compare(x, y); System.out.println(com.compare(3,9)); // 等同於 Comparator<Integer> com2 = Integer::compare; System.out.println(com2.compare(3,9)); }
類名::實例方法名
private static void instanceMethodObject() { UserDomain user = new UserDomain(1L, "Van"); Function<UserDomain, String> fun = (e) -> e.getUserName(); System.out.println(fun.apply(user)); // 等同於 Function<UserDomain, String> fun2 = UserDomain::getUserName; System.out.println(fun2.apply(user)); }
類名 :: new
private static void object() { // UserDomain 中必須有一個 UserDomain(String userName) 的構造器,下同 Function<String,UserDomain> fun = (n) -> new UserDomain(n); fun.apply("Van"); System.out.println("===等價於==="); Function<String,UserDomain> function = UserDomain::new; function.apply("Van"); // 帶兩個參數的構造器引用就要用BiFunction,多個參數的話,還能夠自定義一個這樣的函數式接口 BiConsumer<Long, String> biConsumer = UserDomain :: new; biConsumer.accept(1L,"Van"); }
private static void array() { //傳統Lambda實現 Function<Integer,int[]> function = (i) -> new int[i]; int[] apply = function.apply(10); System.out.println(apply.length); //數組類型引用實現 function = int[] ::new; apply = function.apply(100); System.out.println(apply.length); }
Lambda
表達式是Java
對於函數式編程的溫和轉變,面向對象編程和函數式編程不是互相對立的,結合使用可以更加有效地幫助咱們管理程序的複雜性。