Java 8 Lambda表達式和理解
說明:部分資料來源於網絡
時間:20190704
Lambda 表達式,也可稱為閉包,它是推動 Java 8 發布的最重要新特性。Lambda 允許把函數作為一個方法的參數(函數作為參數傳遞進方法中)。使用 Lambda 表達式可以使代碼變的更加簡潔緊湊。
一、語法
1、lambda 表達式的語法格式如下:
(左邊)輸入參數->(右邊)lambda主體
(parameters) -> expression;
或
(parameters) ->{ statements; }
2、以下是lambda表達式的重要特征:
(1)輸入參數:
可選類型聲明:不需要聲明參數類型,編譯器可以統一識別參數值。
可選的參數圓括號:
a、一個參數無需定義圓括號,但多個參數需要定義圓括號。
b、如果申明了參數類型,則一定需要圓括號。
(2)lambda主體:
可選的大括號:如果主體包含了一個語句,就不需要使用大括號。
可選的返回關鍵字:如果主體只有一個表達式返回值則編譯器會自動返回值,大括號需要指定明表達式返回了一個數值。
3、按照上面的格式,lambda不同參數的表達式寫法
(1)沒有參數的表達式:
() -> System.out.println("this is no parameter Lambda expression");
(2)只有一個參數的表達式:
(x) -> System.out.println("this is only one parameter Lambda expression");
或者
(X x) -> System.out.println("this is only one parameter Lambda expression");
和
(x) -> { x = x*2; System.out.println("this is only one parameter Lambda expression"); System.out.println("the function is double input value"); return x; };
說明:一個參數的可以不用使用(),如果參數聲明了參數類型則必須要加();Lambda主體是語句塊的話需要使用{}。
(3)有兩個或者多個參數的表達式:
(x,y) -> System.out.println("these are several parameters Lambda expression");
或者
(X x,Y y) -> { System.out.println("the function is add two input values"); System.out.println("these are several parameters Lambda expression"); return x+y; };
說明:有多個參數的lambda表達式,參數使用,隔開。Lambda主體是語句塊的話需要使用{}。
二、Lambda 表達式實例
lambda示例1:
/* * Predicate<T>接口中boolean test(T t)方法只接收一個參數,返回值為boolean類型, * 故lambda表達式的輸入參數只有一個參數:x,lambda的主體返回值為boolean類型 */ Predicate<Integer> atLeast5 = x -> x >= 5; System.out.println("傳入參數是否大於5:" + atLeast5.test(6)); /* * BinaryOperator<T>接口中R apply(T t, U u)方法接收兩個泛型參數,返回值為也為泛型 * 故lambda表達式輸入Long參數有兩個:x,y,lambda的主體返回值為Long類型 */ BinaryOperator<Long> addLongs = (x, y) -> { Long z = x + y; return z; }; System.out.println("計算傳入兩個Long參數的和:" + addLongs.apply(5l, 6l)); /* * Runnable接口中run()方法沒有參數,有沒有返回值,故lambda表達式沒有參數,lambda主體也沒有返回值 */ Runnable run1 = () -> System.out.println("這個方法就是run里面的方法"); run1.run(); try { final Integer value = 9; /* * 1、Callable<V>接口中V call() throws Exception方法沒有參數,故lambda表達式也不能有輸入參數, * 返回類型為泛型,故lambda主體需要返回指定Integer類型 * 2、因為.call()方法拋出異常,所以需要拋出或者捕獲異常 * 3、lambda表達式使用外部參數,需要是final類型,及時外部參數沒有定義為final類型, * 也會隱式的指定為final,故值或者引用地址不能修改。 */ Callable<Integer> call = () -> value; System.out.println("無參的方法,所以lambda的參數列表中不能傳遞參數:" + call.call()); } catch (Exception e) { e.printStackTrace(); }
lambda示例二:
public class Java8Tester { public static void main(String args[]){
Java8Tester tester = new Java8Tester(); // 類型聲明 MathOperation addition = (int a, int b) -> a + b; // 不用類型聲明 MathOperation subtraction = (a, b) -> a - b; // 大括號中的返回語句 MathOperation multiplication = (int a, int b) -> { return a * b; }; // 沒有大括號及返回語句 MathOperation division = (int a, int b) -> a / b; System.out.println("10 + 5 = " + tester.operate(10, 5, addition)); System.out.println("10 - 5 = " + tester.operate(10, 5, subtraction)); System.out.println("10 x 5 = " + tester.operate(10, 5, multiplication)); System.out.println("10 / 5 = " + tester.operate(10, 5, division)); // 不用括號 GreetingService greetService1 = message -> System.out.println("Hello " + message); // 用括號 GreetingService greetService2 = (message) -> System.out.println("Hello " + message); greetService1.sayMessage("Runoob"); greetService2.sayMessage("Google"); }
// 定義接口和接口中為實現的方法 interface MathOperation {
int operation(int a, int b);
}
// 定義接口和接口中為實現的方法
interface GreetingService {
void sayMessage(String message);
}
// 定義方法,方法通過lambda表達式實現了接口中的方法 private int operate(int a, int b, MathOperation mathOperation){ return mathOperation.operation(a, b); } }
執行以上代碼,輸出結果為:
10 + 5 = 15
10 - 5 = 5
10 x 5 = 50
10 / 5 = 2
Hello Runoob
Hello Google
使用 Lambda 表達式需要注意以下兩點:
Lambda表達式主要用來定義行內執行的方法類型接口,例如,一個簡單方法接口。在上面例子中,我們使用各種類型的Lambda表達式來定義MathOperation接口的方法。然后我們定義了sayMessage的執行。
Lambda 表達式免去了使用匿名方法的麻煩,並且給予Java簡單但是強大的函數化的編程能力。
Lambda表達式常用的場景是:函數式接口。函數式接口是指的只有一個抽象方法的接口,我們常用的函數式接口有:
Runnable、Callable、PrivilegedAction、Comparator、FileFilter、PathMatcher、InvocationHandler、PropertyChangeListener、ActionListener、ChangeListener、Function、Predicate、BinaryOperator
三、lambda表達式中的變量作用域
lambda表達式只能引用標記了final的外層局部變量,這就是說不能在lambda內部修改定義在域外的局部變量,否則會編譯錯誤。
在 Java8Tester.java 文件輸入以下代碼:
public class Java8Tester { final static String salutation = "Hello! "; public static void main(String args[]){
GreetingService greetService1 = message -> System.out.println(salutation + message); greetService1.sayMessage("Runoob"); //====================相當於下面============================== GreetingService g = new GreetingService() { @Override public void sayMessage(String message) { System.out.println(salutation + message); } }; g.sayMessage("jack");
} interface GreetingService { void sayMessage(String message); } }
執行以上腳本,輸出結果為:
$ javac Java8Tester.java $ java Java8Tester Hello! Runoob Hello! jack
我們也可以直接在 lambda 表達式中訪問外層的局部變量:
public class Java8Tester { public static void main(String args[]) { final int num = 1; Converter<Integer, String> s = (param) -> System.out.println(String.valueOf(param + num)); s.convert(2); // 輸出結果為 3 } public interface Converter<T1, T2> { void convert(int i); } }
lambda表達式的局部變量可以不聲明為final,但是不可被后面的代碼修改(即隱性的具有final 的語義)
int num = 1; Converter<Integer, String> s = (param) -> System.out.println(String.valueOf(param + num)); s.convert(2); num = 5; //報錯信息:Local variable num defined in an enclosing scope must be final or effectively final
在 Lambda 表達式當中不允許聲明一個與局部變量同名的參數或者局部變量。
String first = ""; Comparator<String> comparator = (first, second) -> Integer.compare(first.length(), second.length()); //編譯會出錯