Lambda表達式 :也可稱為閉包,Lambda允許把函數作為一個方法的參數(函數作為參數傳遞到方法中),免去了使用匿名方法的麻煩,並且給予Java簡單但是強大的函數化的編程能力 ...
目錄
前言:
這兩天徹底的複習了一遍Java8的各種新特性,趁著熱乎勁,把知識點整理成博客的形式保存一下。
一、Lambda介紹
Lambda表達式 : 也可稱為閉包,Lambda允許把函數作為一個方法的參數(函數作為參數傳遞到方法中),免去了使用匿名方法的麻煩,並且給予Java簡單但是強大的函數化的編程能力
語法格式:
(parameters) -> expression
或者
(parameters) ->{ statements; }格式說明:
():介面中抽象方法的參數列表,沒有參數就空著,有參數則寫出參數,多個參數用逗號分隔
->:傳遞的意思,將參數傳遞給方法體{}
{}:重寫介面的抽象方法的方法體
二、Lambda用法實例
1.簡單實例(基礎用法的規則)
1.1 無參數時,可以省略括弧內容
()->10 //返回結果值:10
1.2 傳入String類型的參數
(String s)->System.out.print(s) //列印s的內容
1.3 傳入的參數類型可以省略,如下
//
a->a*10 //返回結果值:a*10(傳入一個參數時,可以省略括弧)
(a,b)->a*b //返回結果值:a*b(傳入兩個或以上參數時,不能省略括弧,)
2.函數式介面
2.1 Lambda實現自定義介面Calculator
首先定義一個函數式介面 Calculator
,包含唯一一個抽象方法 Calcu()
public interface Calculator {
public abstract int Calcu(int x, int y);
}
其次定義一個 invokeCalcu()
,接收參數類型為介面
public static void invokeCalcu(int x, int y, Calculator calculator) {
int sum = calculator.Calcu(x, y);
System.out.println("sum = " + sum);
}
最後調用 invokeCalcu()
,一共有三種方法實現:
1)使用匿名內部類傳入介面,並實現抽象方法
invokeCalcu(10, 20, new Calculator() {
@Override
public int Calcu(int x, int y) {
return x + y;
}
});
2)使用Lambda表達式,簡化操作
invokeCalcu(120, 130, (a, b) -> a + b);
2.2 Lambda表達式實現多線程介面實例 Runnable
//使用匿名內部類的方式實現多線程的創建
new Thread(new Runnable() {
@Override
public void run() {
System.out.println("currentThreadName = " + Thread.currentThread().getName());
}
}).start();
//使用lambda表達式實現多線程
new Thread(() -> System.out.println("currentThread().getName() = " + Thread.currentThread().getName())).start();
2.3 Lambda表達式實現比較器介面實例 Comparator
定義一個Person類,保存姓名和年齡
public class Person {
private String name;
private int age;
public Person() { }
public Person(String name, int age) {
this.name = name;
this.age = age;
}
@Override
public String toString() {
return "Person{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
public String getName() { return name; }
public void setName(String name) { this.name = name; }
public int getAge() { return age; }
public void setAge(int age) { this.age = age; }
}
然後分別使用匿名內部類和Lambda表達式對Person類的年齡進行排序
Person[] arr = {
new Person("佟麗婭", 26),
new Person("範冰冰", 22),
new Person("柳岩", 21)
};
//匿名內部類重寫comparator介面里的compare()
System.out.println("=======匿名內部類升序排序:=================");
Arrays.sort(arr, new Comparator<Person>() {
@Override
public int compare(Person o1, Person o2) {
return o1.getAge() - o2.getAge();
}
});
for (Person person : arr) {
System.out.println(person);
}
//使用Lambda表達式,簡化匿名內部類
System.out.println("=======Lambda表達式降序排序:===============");
Arrays.sort(arr, (o1, o2) -> o2.getAge() - o1.getAge());
/**
* jdk1.8 也可以這樣寫 方法引用
* Arrays.sort(arr,Comparator.comparingInt(Person::getAge).reversed());
**/
for (Person person : arr) {
System.out.println(person);
}
//Stream API 也是Java8的新特新寫法
System.out.println("=======Stream-sort升序排序:===============");
Arrays.stream(arr).sorted(Comparator.comparing(Person::getAge)).forEach(System.out::println);
介面中 有且僅有唯一一個抽象方法 ,稱之為函數式介面(這種類型的介面也稱為SAM介面,即Single Abstract Method interfaces)
使用前提:
- 使用Lambda必須具有介面,且要求介面中有且僅有一個抽象方法
- 無論是 JDK內置的
Runnable
、Comparator
介面還是自定義的介面,只有當介面中的抽象方法存在且唯一時,才可以使用Lambda- 使用Lambda必須具有上下文推斷
方法的參數或局部變數類型必須為Lambda對應的介面類型,才能使用Lambda作為該介面的實例
有且僅有一個抽象方法的介面,稱為
“函數式編程”
三、Lambda變數作用域
3.1在 Lambda 表達式當中不允許聲明一個與局部變數同名的參數或者局部變數
String[] str = {"1","12","123"};
Comparator<String> comparator = (str, second) -> Integer.compare(str.length(), second.length()); //str處的編譯會出錯
3.2 Lambda 表達式只能引用標記了 final 的外層局部變數
也就是說不能在 lambda 內部修改定義在域外的局部變數,否則會編譯錯誤。
可以直接在 lambda 表達式中訪問外層的局部變數:
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; //由於在Lambda表達式中引用了num,所以num是隱形的final修飾,但是這裡修改了num的值,final就不存在了