文章目錄
一、什麼是Lambda表達式
Lambda表達式,也可稱爲閉包。類似於JavaScript中的閉包,它是推動Java8發佈的最重要的新特性。
二、爲什麼使用Lambda表達式
我們可以把Lambda表達式理解爲一段可以傳遞的代碼(將代碼像數據一樣進行傳遞)。Lambda允許把函數作爲一個方法的參數,使用Lambda表達式可以寫出更簡潔、更靈活的代碼,而其作爲一種更緊湊的代碼風格,使Java的語言表達能力得到了提升。
一個簡單示例:
使用傳統方法實現Runnable的run()方法,如下圖所示:
使用Lambda表達式實現,如下圖所示:
可以看到代碼明顯簡潔了許多。
三、Lambda表達式語法
Lambda表達式在Java語言中引入了一個操作符**“->”**,該操作符被稱爲Lambda操作符或箭頭操作符。它將Lambda分爲兩個部分:
- 左側:指定了Lambda表達式需要的所有參數
- 右側:制定了Lambda體,即Lambda表達式要執行的功能。
像這樣:
(parameters) -> expression
或
(parameters) ->{ statements; }
以下是lambda表達式的重要特徵:
- 可選類型聲明:不需要聲明參數類型,編譯器可以統一識別參數值。
- 可選的參數圓括號:一個參數無需定義圓括號,但多個參數需要定義圓括號。
- 可選的大括號:如果主體包含了一個語句,就不需要使用大括號。
- 可選的返回關鍵字:如果主體只有一個表達式返回值則編譯器會自動返回值,大括號需要指定明表達式返回了一個數值。
下面對每個語法格式的特徵進行舉例說明:
(1)語法格式一:無參,無返回值,Lambda體只需一條語句。如下:
@Test
public void test01(){
Runnable runnable=()-> System.out.println("Runnable 運行");
runnable.run();//結果:Runnable 運行
}
(2)語法格式二:Lambda需要一個參數,無返回值。如下:
@Test
public void test02(){
Consumer<String> consumer=(x)-> System.out.println(x);
consumer.accept("Hello Consumer");//結果:Hello Consumer
}
(3)語法格式三:Lambda只需要一個參數時,參數的小括號可以省略,如下:
public void test02(){
Consumer<String> consumer=x-> System.out.println(x);
consumer.accept("Hello Consumer");//結果:Hello Consumer
}
(4)語法格式四:Lambda需要兩個參數,並且Lambda體中有多條語句。
@Test
public void test04(){
Comparator<Integer> com=(x, y)->{
System.out.println("函數式接口");
return Integer.compare(x,y);
};
System.out.println(com.compare(2,4));//結果:-1
}
(5)語法格式五:有兩個以上參數,有返回值,若Lambda體中只有一條語句,return和大括號都可以省略不寫
@Test
public void test05(){
Comparator<Integer> com=(x, y)-> Integer.compare(x,y);
System.out.println(com.compare(4,2));//結果:1
}
(6)Lambda表達式的參數列表的數據類型可以省略不寫,因爲JVM可以通過上下文推斷出數據類型,即“類型推斷”
@Test
public void test06(){
Comparator<Integer> com=(Integer x, Integer y)-> Integer.compare(x,y);
System.out.println(com.compare(4,2));//結果:1
}
類型推斷:在執行javac編譯程序時,JVM根據程序的上下文推斷出了參數的類型。Lambda表達式依賴於上下文環境。
語法背誦口訣:左右遇一括號省,左側推斷類型省,能省則省。
四、函數式接口
1.什麼是函數式接口
只包含一個抽象方法的接口,就稱爲函數式接口。我們可以通過Lambda表達式來創建該接口的實現對象。
我們可以在任意函數式接口上使用@FunctionalInterface註解,這樣做可以用於檢測它是否是一個函數式接口,同時javadoc也會包含一條聲明,說明這個接口是一個函數式接口。
2.自定義函數式接口
按照函數式接口的定義,自定義一個函數式接口,如下:
@FunctionalInterface
public interface MyFuncInterf<T> {
public T getValue(String origin);
}
定義一個方法將函數式接口作爲方法參數。
public String toLowerString(MyFuncInterf<String> mf,String origin){
return mf.getValue(origin);
}
將Lambda表達式實現的接口作爲參數傳遞。
public void test07(){
String value=toLowerString((str)->{
return str.toLowerCase();
},"ABC");
System.out.println(value);//結果ABC
}
3.Java內置函數式接口
四大核心函數式接口的介紹,如圖所示:
使用示例:
1.Consumer:消費型接口 void accept(T t)
public void makeMoney(Integer money, Consumer<Integer> consumer){
consumer.accept(money);
}
@Test
public void test01(){
makeMoney(100,t-> System.out.println("今天賺了"+t));//結果:今天賺了100
}
2.Supplier:供給型接口 T get()
/**
* 產生指定的整數集合放到集合中
* Iterable接口的forEach方法的定義:方法中使用到了Consumer消費型接口,
* default void forEach(Consumer<? super T> action) {
* Objects.requireNonNull(action);
* for (T t : this) {
* action.accept(t);
* }
* }
*/
@Test
public void test02(){
List list = addNumInList(10, () -> (int) (Math.random() * 100));
list.forEach(t-> System.out.println(t));
}
public List addNumInList(int size, Supplier<Integer> supplier){
List<Integer> list=new ArrayList();
for (int i = 0; i < size; i++) {
list.add(supplier.get());
}
return list;
}
3.Function<T,R>:函數型接口 R apply(T t)
/**
*
* 使用函數式接口處理字符串。
*/
public String handleStr(String s,Function<String,String> f){
return f.apply(s);
}
@Test
public void test03(){
System.out.println(handleStr("abc",(String s)->s.toUpperCase()));
}
//結果:ABC
4.Predicate:斷言型接口 boolean test(T t)
/**
* 自定義條件過濾字符串集合
*/
@Test
public void test04(){
List<String> strings = Arrays.asList("啊啊啊", "2333", "666", "?????????");
List<String> stringList = filterStr(strings, (s) -> s.length() > 3);
for (String s : stringList) {
System.out.println(s);
}
}
public List<String> filterStr(List<String> list, Predicate<String> predicate){
ArrayList result = new ArrayList();
for (int i = 0; i < list.size(); i++) {
if (predicate.test(list.get(i))){
result.add(list.get(i));
}
}
return result;
}
其他接口的定義,如圖所示:
五、方法引用
當要傳遞給Lambda體的操作,已經有實現的方法了,就可以使用方法引用!(實現抽象方法的參數列表,必須與方法引用的參數列表)。方法引用可以理解爲方法引用是Lambda表達式的另外一種表現形式。
方法引用的語法:使用操作符“::”將對象或類和方法名分隔開。
方法引用的使用情況共分爲以下三種:
- 對象::實例方法名
- 類::靜態方法名
- 類::實例方法名
使用示例:
1.對象::實例方法名
/**
*PrintStream中的println方法定義
* public void println(String x) {
* synchronized (this) {
* print(x);
* newLine();
* }
* }
*/
//對象::實例方法名
@Test
public void test1(){
PrintStream out = System.out;
Consumer<String> consumer=out::println;
consumer.accept("hello");
}
- 類::靜態方法名
/**
* Integer類中的靜態方法compare的定義:
* public static int compare(int x, int y) {
* return (x < y) ? -1 : ((x == y) ? 0 : 1);
* }
*/
@Test
public void test2(){
Comparator<Integer> comparable=(x,y)->Integer.compare(x,y);
//使用方法引用實現相同效果
Comparator<Integer> integerComparable=Integer::compare;
System.out.println(integerComparable.compare(4,2));//結果:1
System.out.println(comparable.compare(4,2));//結果:1
}
3.類::實例方法名
@Test
public void test3(){
BiPredicate<String,String> bp=(x,y)->x.equals(y);
//使用方法引用實現相同效果
BiPredicate<String,String> bp2=String::equals;
System.out.println(bp.test("1","2"));//結果:false
System.out.println(bp.test("1","2"));//結果:false
}
六、構造器引用與數組引用
1.構造器引用
格式:類名::new
與函數式接口相結合,自動與函數式接口中方法兼容,可以把構造器引用賦值給定義的方法。需要注意構造器參數列表要與接口中抽象方法的參數列表一致。
使用示例:
創建一個實體類Employee:
public class Employee {
private Integer id;
private String name;
private Integer age;
@Override
public String toString() {
return "Employee{" +
"id=" + id +
", name='" + name + '\'' +
", age=" + age +
'}';
}
public Employee(){
}
public Employee(Integer id) {
this.id = id;
}
public Employee(Integer id, Integer age) {
this.id = id;
this.age = age;
}
public Employee(int id, String name, int age) {
this.id = id;
this.name = name;
this.age = age;
}
}
使用構造器引用與函數式接口相結合
@Test
public void test01(){
//引用無參構造器
Supplier<Employee> supplier=Employee::new;
System.out.println(supplier.get());
//引用有參構造器
Function<Integer,Employee> function=Employee::new;
System.out.println(function.apply(21));
BiFunction<Integer,Integer,Employee> biFunction=Employee::new;
System.out.println(biFunction.apply(8,24));
}
輸出結果:
Employee{id=null, name='null', age=null}
Employee{id=21, name='null', age=null}
Employee{id=8, name='null', age=24}
2.數組引用
數組引用的格式:type[]:new
使用示例:
@Test
public void test02(){
Function<Integer,String[]> function=String[]::new;
String[] apply = function.apply(10);
System.out.println(apply.length);//結果:10
}
筆記總結自:尚硅谷的視頻教程-【Java8新特性】
參考:
1.Java 8 Lambda 表達式