一:簡介
JDK的升級的目的有以下幾個:增加新的功能、修復bug、性能優化、簡化代碼等幾個方面,Lambda表達式就是屬於簡化代碼,用於簡化匿名實現類,提供一種更加簡潔的寫法。Lambda表達式在Swift語言中稱之爲代碼塊,Lambda表達式可以認爲是一種特殊的接口,該接口必須只有一個抽象方法。
語法
(參數類型 參數名, 數參數類型 參數名2...) -> {
// code
};
- 1
- 2
- 3
小括號()中的內容就是方法中的參數列表包括參數類型、參數名,其中參數類型是可以省略的,當參數個數只有一個時也可以省略掉小括號;
花括號{}中的內容就是方法中的方法體,當方法體中只有一行代碼時可以省略{},當方法體中只有一行代碼並且需要返回值時也可以省略掉return;
由於Lambda表達式是匿名實現類的簡寫,是一種特殊的接口,當賦值給一個變量時也少不掉分號;
Lambda表達式的作用
- 簡化匿名實現類的書寫
- 作爲函數中的參數來傳遞
二:示例
示例1:兩個參數,一個返回值
IHello 一個很普通的接口,但接口中只能有一個抽象方法
public interface IHello {
String sayHello(String name, String msg);
}
- 1
- 2
- 3
Main
public class Main {
public static void main(String[] args) {
// 將一個Lambda表達式賦值給一個接口,說明Lambda表達式就是一種接口數據類型,只不過該接口只能有一個抽象方法
// 參數列表可以省略參數類型,可以寫成(name, msg),
// 在JDK1.8中有個叫做類型推斷的東西,可以自動推斷出參數的類型,
// 因爲IHello中只有一個抽象方法,知道方法了就知道參數列表了,從而就能推出參數類型來
IHello iHello = (String name, String msg) -> {
String hello = name + ": " + msg;
return hello;
};
// 調用接口的方法
String content = iHello.sayHello("mengday", "happy new year everyone!");
System.out.println(content);
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
示例2:一個參數,一個返回值
public interface IHello {
String sayHello(String name);
}
- 1
- 2
- 3
Main
public class Main {
public static void main(String[] args) {
// 參數列表可以省略參數類型,當只有一個參數時可以省略小括號 (String name) --> (name) --> name
// 當方法體中只有一行代碼並且有返回值時可以同時省略花括號和return
// { return name + ": " + "happy new year everyone!";} --> name + ": " + "happy new year everyone!";
IHello iHello = name -> name + ": " + "happy new year everyone!";
String content = iHello.sayHello("mengday");
System.out.println(content);
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
示例3:沒有參數,沒有返回值
public interface IHello {
void sayHello();
}
- 1
- 2
- 3
public class Main {
public static void main(String[] args) {
// 當表達式沒有參數時一對小括號是不能省略掉的
IHello iHello = () -> System.out.println("mengday: happy new year everyone!");
iHello.sayHello();
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
從這三個示例中我們發現我們只定義了接口,並沒有定義實現類,而是通過Lambda表達式來代替了實現類。
注意:Lambda接口只能有一個抽象方法,可以同時擁有多個靜態方法和默認方法。
示例4:Lambda表達式參數
public interface IHello {
void sayHello(String name);
}
- 1
- 2
- 3
public class Main {
public static void sayHello(IHello iHello, String name) {
iHello.sayHello(name);
}
public static void main(String[] args) {
IHello iHello = name -> {
String content = name + ": " + "happy new year everyone!";
System.out.println(content);
};
// 這裏可以把iHelo看成一個匿名實現類來傳遞參數
sayHello(iHello, "mengday");
// 如果去掉變量的接收,直接將Lambda表達式傳遞到參數中,此時Lambda表達式更像是一個函數
// 也就是說JDK1.8竟然可以將一個函數作爲參數傳遞到方法中,這是之前版本做不到的
// 將函數作爲方法的參數傳遞,一般用於回調函數,將回調函數傳遞到方法中
sayHello(name -> {
String content = name + ": " + "happy new year everyone!";
System.out.println(content);
}, "mengday");
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
示例5:集合排序示例
public static void main(String[] args) {
// 寫法一:使用匿名內部類
// 好好學習,天天向上
List<String> words = Arrays.asList("good", "good", "study", "day", "day", "up");
// public static <T> void sort(List<T> list, Comparator<? super T> c)
Collections.sort(words, new Comparator<String>() {
@Override
public int compare(String s1, String s2) {
// 降續排序
return s2.compareTo(s1);
}
});
System.out.println(words);
// 寫法二:使用Lambda表達式
// 咱倆誰跟誰
words = Arrays.asList("we", "two", "who", "and", "who");
Collections.sort(words, (String s1, String s2) -> {return s2.compareTo(s1);});
System.out.println(words);
// 寫法三:使用Lambda表達式(簡寫)
// 有事起奏,無事退朝
words = Arrays.asList("if", "you", "have", "something", "to", "say", "then", "say!",
"if", "you", "have", "nothing", "to", "say", "go", "home!");
Collections.sort(words, (s1, s2) -> s2.compareTo(s1));
System.out.println(words);
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
函數式接口@FunctionalInterface
從我們自定義的IHello示例來看,Lambda表達式其實是一種接口類型的數據類型,嚴格的說Lambda表達式的數據類型是:函數式接口,是一種特殊的接口,該接口使用@FunctionalInterface註解來標記(不是必須的,可以不用該註解標記,IHello接口就沒有使用該註解標記, ),並且接口中只能有一個抽象方法,可以有多個靜態方法或者默認方法, 每一個該類型的lambda表達式都會被匹配到這個抽象方法。
@FunctionalInterface
public interface Comparator<T> {
int compare(T o1, T o2);
// 其它static、default方法
}
- 1
- 2
- 3
- 4
- 5
- 6
@FunctionalInterface: 該註解沒啥太大含義,該註解是給編譯器做檢查使用的,如果使用了該註解,編譯器就會檢查該接口中的抽象方法是不是隻有一個,如果有多個就會報錯:在接口Xxx中找到多個非覆蓋抽象方法
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface FunctionalInterface {}
- 1
- 2
- 3
- 4
我們完善一下我們的IHello, 使用@FunctionalInterface註解
@FunctionalInterface
public interface IHello {
void sayHello(String name);
}
- 1
- 2
- 3
- 4
我們可以將lambda表達式當作任意只包含一個抽象方法的接口類型,也就是說我們的IHello接口無論叫什麼名字,接口中的方法無論叫什麼名字都無所謂(只是可讀性更好些),因此可以再進行抽象化一下,JDK1.8中提供了這樣的函數式接口,我們也不需要再定義IHello接口了,JDK1.8中提供了Supplier、Consumer、Function、BiFunction,這幾個是比較常用的
Supplier< T > 供應商:沒有參數,有返回值
@FunctionalInterface
public interface Supplier<T> {
T get();
}
- 1
- 2
- 3
- 4
Consumer< T > 消費者: 只有一個參數,沒有返回值
@FunctionalInterface
public interface Consumer<T> {
void accept(T t);
}
- 1
- 2
- 3
- 4
Function< T, R > 函數:一個參數,一個返回值
@FunctionalInterface
public interface Function<T, R> {
R apply(T t);
}
- 1
- 2
- 3
- 4
BiFunction< T, U, R > 二元函數:兩個參數,一個返回值
@FunctionalInterface
public interface BiFunction<T, U, R> {
R apply(T t, U u);
}
- 1
- 2
- 3
- 4
Comparator< T > 比較器:接收兩個參數,返回比較的結果
@FunctionalInterface
public interface Comparator<T> {
int compare(T o1, T o2);
}
- 1
- 2
- 3
- 4
使用以上四大函數式接口來取代自定義的接口IHello
public class Main {
private static String end = ".";
public static void main(String[] args) {
// 直接使用JDK1.8提供的接口,不需要再定義IHello接口, 直接使用JDK提供的接口來接收Lambda表達式
Supplier<String> supplier = () -> "mengday: happy new year everyone!";
String result = supplier.get();
System.out.println(result);
Consumer<String> consumer = (name) -> System.out.println(name + ": " + "happy new year everyone!");
consumer.accept("mengday");
Function<String, String> func = (name) -> name + ": " + "happy new year everyone!";
String hi = func.apply("mengday");
System.out.println(hi);
// 在代碼塊的內部可以訪問靜態全局變量
// 在代碼塊中可以訪問外邊局部變量
// 在代碼塊的內部可以修改全局靜態變量
// 在代碼塊內部是不能訪問接口中的其它方法的
String split = ": ";
BiFunction<String, String, String> biFunction = (String name, String msg) -> {
end = "!";
String hello = name + split + msg + end;
return hello;
};
String hello = biFunction.apply("mengday", "happy new year everyone");
System.out.println(hello);
// 根據字符串長度比較大小
Comparator<String> comparator = (s1, s2) -> s1.length() - s2.length();
int compare = comparator.compare("abc", "ab");
System.out.println(compare);
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
Predicate< T > 斷言 謂詞: 用於測試一個條件的真假
package java.util.function;
import java.util.Objects;
@FunctionalInterface
public interface Predicate<T> {
// 在給定的參數上評估這個謂詞
boolean test(T t);
// 返回一個組合的謂詞,表示該謂詞與另一個謂詞的短路邏輯AND
default Predicate<T> and(Predicate<? super T> other) {
Objects.requireNonNull(other);
return (t) -> test(t) && other.test(t);
}
// 返回表示此謂詞的邏輯否定的謂詞,相當於not
default Predicate<T> negate() {
return (t) -> !test(t);
}
// 返回一個組合的謂詞,表示該謂詞與另一個謂詞的短路邏輯或
default Predicate<T> or(Predicate<? super T> other) {
Objects.requireNonNull(other);
return (t) -> test(t) || other.test(t);
}
// 返回根據 Objects.equals(Object, Object)測試兩個參數是否相等的謂詞
static <T> Predicate<T> isEqual(Object targetRef) {
return (null == targetRef) ? Objects::isNull : object -> targetRef.equals(object);
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
Main
public static void main(String[] args) {
// 可以構造複雜的條件: 並且and、或者or、否negate
String email = "[email protected]";
Predicate<String> predicate = (str) -> str.length() > 20;
// 測試 emial.length > 0 的boolean
boolean result = predicate.test(email); // false
// 測試 !(emial.length > 0) 的boolean
result = predicate.negate().test(email); // true
Predicate<String> orPredicate = (str) -> str.contains("@");
// 測試 emial.length > 0 or emial.contains("@")
result = predicate.or(orPredicate).test(email); // true
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15