前言
文字已收錄至我的GitHub倉庫,歡迎Star:github.com/bin39232820…
種一棵樹最好的時間是十年前,其次是現在
我知道很多人不玩qq了,但是懷舊一下,歡迎加入六脈神劍Java菜鳥學習群,群聊號碼:549684836 鼓勵大家在技術的路上寫部落格
絮叨
今天 開始寫Java8新特性系列,怎麼說呢,主要有幾個新東西
- Lambda表示式
- 函式式介面
- 方法引用
- Stream流
- Optionl類
- default關鍵字
這個四個的主要作用 簡化程式碼編寫,提高效能等等,但是也會給維護帶來麻煩,因為不懂的人去看,真心累,但是寫起來是真的香,今天打算講標題上的。
考慮大家可能對 內部類 和 泛型不熟悉,有需要的可以看我這2篇文章
?你不知道的Java內部類
?你必須知道的Java泛型
文字力求簡單講清每個知識點,希望大家看完能有所收穫
Lambda表示式簡介
Lambda 表示式,也可稱為閉包,允許把函式作為一個引數,使程式碼更簡潔。
那麼什麼是函數語言程式設計呢?
首先,什麼是函數語言程式設計,引用廖雪峰先生的教程裡面的解釋就是說:函數語言程式設計就是一種抽象程度很高的程式設計正規化,純粹的函數語言程式設計語言編寫的函式沒有變數,因此,任意一個函式,只要輸入是確定的,輸出就是確定的,這種純函式我們稱之為沒有副作用。而允許使用變數的程式設計語言,由於函式內部的變數狀態不確定,同樣的輸入,可能得到不同的輸出,因此,這種函式是有副作用的。函數語言程式設計的一個特點就是,允許把函式本身作為引數傳入另一個函式,還允許返回一個函式!
基本語法
// 1. 不需要引數,返回值為 5
() -> 5
// 2. 接收一個引數(數字型別),返回其2倍的值
x -> 2 * x
// 3. 接受2個引數(數字),並返回他們的差值
(x, y) -> x – y
// 4. 接收2個int型整數,返回他們的和
(int x, int y) -> x + y
// 5. 接受一個 string 物件,並在控制檯列印,不返回任何值(看起來像是返回void)
(String s) -> System.out.print(s)
複製程式碼
這個幾個基本語法 和下面的4種函式式介面是對應的。謝謝讀者的提醒。本來還沒把這塊加上去,新手看起來有點懵逼。
基礎列子
建立執行緒
再來看看遍歷Map集合:
引入lambda表示式的一個最直觀的作用就是大大的簡化了程式碼的開發,像其他一些程式語言Scala,Python等都是支援函式式的寫法的。當然,不是所有的介面都可以通過這種方法來呼叫,只有函式式介面才行,jdk1.8裡面定義了好多個函式式介面,我們也可以自己定義一個來呼叫,下面說一下什麼是函式式介面。函式式介面
- 只包含一個抽象方法的介面,稱為函式式介面。
- 可以通過 Lambda 表示式來建立該介面的物件。(若 Lambda 表示式丟擲一個受檢異常,那麼該異常需要在目標介面的抽象方 法上進行宣告)。
- 可以在任意函式式介面上使用 @FunctionalInterface 註解,這樣做可以檢查它是否是一個函式式介面,同時 javadoc 也會包 含一條宣告,說明這個介面是一個函式式介面。
- 之所以Lambda必須和函式式介面配合是因為,介面如果多個函式,則Lambda表示式無法確定實現的是哪個
來個簡單的例子
這是一個自定義的函式式介面:作用是傳入一個引數,返回一個引數。
@FunctionalInterface
public interface MyNumber<T> {
T getValue(T t);
}
複製程式碼
這是用法
public static void main(String[] args) {
//Lambda的寫法
System.out.println(toUpperString(str -> str.toUpperCase(), "abc")); //ABC
//匿名內部類的寫法
System.out.println(toUpperString(new MyNumber<String>() {
@Override
public String getValue(String s) {
return s;
}
}, "abc"));
}
public static String toUpperString(MyNumber<String> mn, String str) {
return mn.getValue(str);
}
複製程式碼
作為引數傳遞 Lambda 表示式:為了將 Lambda 表示式作為引數傳遞,接 收Lambda 表示式的引數型別必須是與該 Lambda 表示式相容的函式式介面 的型別。
Java 內建四大核心函式式介面
函式式介面 | 引數型別 | 返回型別 | 用途 |
---|---|---|---|
Consumer 消費型介面 | T | void | 對型別為T的物件應用操 作,包含方法: void accept(T t) |
Supplier 供給型介面 | 無 | T | 返回型別為T的物件,包 含方法:T get(); |
Function<T, R> 函式型介面 | T | R | 對型別為T的物件應用操 作,並返回結果。結果 是R型別的物件。包含方 法:R apply(T t); |
Predicate 斷言型介面 | T | boolean | 確定型別為T的物件是否 滿足某約束,並返回 boolean 值。包含方法 boolean test(T t); |
使用場景
之前MyNumber這種介面配合Lambda使用,可以發現必須先宣告介面,很麻煩,而內建的幾個介面就是解決這種問題的;而這些內建的介面也存在大量的內部實現,或者程式設計者自己定義的類,只要符合對應的引數型別和返回值型別的,都可以使用。例如:定義MyClass只要符合引數T返回R,則都可以使用Function<T, R> 函式型介面對應形式,包含下面的構造器引用,方法引用等等形式
如下:
public static void main(String[] args) {
//Lambda的寫法
System.out.println(toUpperString1(str -> str.toUpperCase(), "abc")); //ABC
//匿名內部類的寫法
System.out.println(toUpperString1(new MyNumber<String>() {
@Override
public String getValue(String s) {
return s;
}
}, "abc"));
//使用內建的函式式介面的lambda寫法
System.out.println(toUpperString(str->str.toUpperCase(),"abc"));
}
//內建的函式介面
public static String toUpperString(Function<String,String> mn, String str) {
return mn.apply(str);
}
//定義的函式介面
public static String toUpperString1(MyNumber<String> mn, String str) {
return mn.getValue(str);
}
複製程式碼
方法引用與構造器引用
方法引用
當要傳遞給Lambda體的操作,已經有實現的方法了,可以使用方法引用! (實現抽象方法的引數列表,必須與方法引用方法的引數列表保持一致!) 方法引用:使用操作符 “::” 將方法名和物件或類的名字分隔開來。 如下三種主要使用情況:
- 物件::例項方法
- 類::靜態方法
- 類::例項方法
例子
//例如:此時Consumer引數型別和返回值和println方法一致
//物件::例項方法名
//為什麼這樣用,因為 ConSumer這個函式介面就是傳入引數,返回為void ,並且printl 方法就是這樣的
/**我們看下面的原始碼 和那個函式介面一樣
* public void println(String x) {
* synchronized (this) {
* print(x);
* newLine();
* }
* }
*/
PrintStream printStream=System.out;
Consumer<String> con= printStream::println;
con.accept("haha");
/類::靜態方法名
Comparator<Integer> com=Integer::compare;
Comparator<Integer> com1=(x,y)->Integer.compare(x,y);
複製程式碼
構造器引用
格式: ClassName::new
與函式式介面相結合,自動與函式式介面中方法相容。 可以把構造器引用賦值給定義的方法,與構造器引數 列表要與介面中抽象方法的引數列表一致
Function<Integer,MyClass> fun= n->new MyClass(1);
Function<Integer,MyClass> fun1=MyClass::new ;
MyClass apply = fun1.apply(1);
複製程式碼
如果存在多個構造器,會自動匹配對應引數或者無參的構造器,取決於apply傳遞的引數。
default關鍵字
在java裡面,我們通常都是認為介面裡面是隻能有抽象方法,不能有任何方法的實現的,那麼在jdk1.8裡面打破了這個規定,引入了新的關鍵字default,通過使用default修飾方法,可以讓我們在介面裡面定義具體的方法實現,如下
public interface NewCharacter {
public void test1();
public default void test2(){
System.out.println("我是新特性1");
}
}
複製程式碼
那這麼定義一個方法的作用是什麼呢?為什麼不在介面的實現類裡面再去實現方法呢?
其實這麼定義一個方法的主要意義是定義一個預設方法,也就是說這個介面的實現類實現了這個介面之後,不用管這個default修飾的方法,也可以直接呼叫,如下。
public class NewCharacterImpl implements NewCharacter{
@Override
public void test1() {
}
public static void main(String[] args) {
NewCharacter nca = new NewCharacterImpl();
nca.test2();
}
}
複製程式碼
所以說這個default方法是所有的實現類都不需要去實現的就可以直接呼叫,那麼比如說jdk的集合List裡面增加了一個sort方法,那麼如果定義為一個抽象方法,其所有的實現類如arrayList,LinkedList等都需要對其新增實現,那麼現在用default定義一個預設的方法之後,其實現類可以直接使用這個方法了,這樣不管是開發還是維護專案,都會大大簡化程式碼量。
結尾
好了,今天就講那麼多了。謝謝大家的支援
日常求贊
好了各位,以上就是這篇文章的全部內容了,能看到這裡的人呀,都是真粉。
創作不易,各位的支援和認可,就是我創作的最大動力,我們下篇文章見
六脈神劍 | 文 【原創】如果本篇部落格有任何錯誤,請批評指教,不勝感激 !