簡介
Lambda表達式(也稱閉包),是Java8中最受期待和歡迎的新特性之一。在Java語法層面Lambda表達式容許函數做爲一個方法的參數(函數做爲參數傳遞到方法中),或者把代碼當作數據。Lambda表達式能夠簡化函數式接口的使用。函數式接口就是一個只具備一個抽象方法的普通接口,像這樣的接口就可使用Lambda表達式來簡化代碼的編寫。
使用Lambda表達式的前提
對應接口有且只有一個抽象方法!!!
基礎語法
Lambda 表達式的基礎語法:Java8中引入了一個新的操做符 「->」 該操做符稱爲箭頭操做符或 Lambda 操做符
箭頭操做符將 Lambda 表達式拆分紅兩部分:
左側:Lambda 表達式的參數列表
右側:Lambda 表達式中所需執行的功能, 即 Lambda 體
(args1, args2...) -> {};br/>Lambda表達式的重要特徵
可選類型聲明:不須要AxiTrader返傭www.kaifx.cn/broker/axitrader.html聲明參數類型,編譯器能夠統一識別參數值。
可選的參數圓括號:一個參數無需定義圓括號,但多個參數須要定義圓括號。
可選的大括號:若是主體包含了一個語句,就不須要使用大括號。
可選的返回關鍵字:若是主體只有一個表達式返回值則編譯器會自動返回值,大括號須要指定明表達式返回了一個數值。
使用Lambda表達式的優缺點
優勢
使用Lambda表達式能夠簡化接口匿名內部類的使用,能夠減小類文件的生成,多是將來編程的一種趨勢。
缺點
使用Lambda表達式會減弱代碼的可讀性,並且Lambda表達式的使用侷限性比較強,只能適用於接口只有一個抽象方法時使用,不宜調試。
函數式接口
只有函數式接口,才能夠轉換爲lambda表達式
有且只有一個抽象方法的接口被成爲函數式接口!
函數式接口能夠顯式的被@FunctionalInterface所表示,當被標識的接口不知足規定時,編譯器會提示報錯
案例1 無參無返回
public class Demo01 {
public static void main(String[] args) {
// 1.傳統方式 須要new接口的實現類來完成對接口的調用
ICar car1 = new IcarImpl();
car1.drive();
// 2.匿名內部類使用
ICar car2 = new ICar() {br/>@Override
public void drive() {
System.out.println("Drive BMW");
}
};
car2.drive();
// 3.無參無返回Lambda表達式
ICar car3 = () -> {System.out.println("Drive Audi");};
car3.drive();
// 4.無參無返回且只有一行實現時能夠去掉{}讓Lambda更簡潔
ICar car4 = () -> System.out.println("Drive Ferrari");
car4.drive();
// 去查看編譯後的class文件 你們能夠發現 使用傳統方式或匿名內部類都會生成額外的class文件,而Lambda不會
}
}
interface ICar {
void drive();
}
class IcarImpl implements ICar {br/>@Override
public void drive() {
System.out.println("Drive Benz");
}
}
案例2 有參有返回值
public class Demo02 {
public static void main(String[] args) {
// 1.有參無返回
IEat eat1 = (String thing) -> System.out.println("eat " + thing);
eat1.eat("apple");
// 參數數據類型能夠省略
IEat eat2 = (thing) -> System.out.println("eat " + thing);
eat2.eat("banana");
// 2.多個參數
ISpeak speak1 = (who, content) -> System.out.println(who + " talk " + content);
speak1.talk("John", "hello word");
// 3.返回值
IRun run1 = () -> {
return 10;
};
run1.run();
// 4.返回值簡寫
IRun run2 = () -> 10;
run2.run();
}
}
interface IEat {
void eat(String thing);
}
interface ISpeak {
void talk(String who, String content);
}
interface IRun {
int run();
}
案例3 final類型參數
public class Demo03 {
public static void main(String[] args) {
// 全寫
IAddition addition1 = (final int a, final int b) -> a + b;
System.out.println(addition1.add(1, 2));
// 簡寫
IAddition addition2 = (a, b) -> a+b;
System.out.println(addition2.add(2, 3));
}
}
interface IAddition {
int add(final int a, final int b);br/>}
Java8內置的函數式接口
Java8提供了一個java.util.function包,包含了不少函數式接口,咱們來介紹最爲基本的4個(爲了節省篇幅,去掉了源碼中的註釋)
Function接口
@FunctionalInterface
public interface Function<T, R> {
R apply(T t);
default <V> Function<V, R> compose(Function<? super V, ? extends T> before) {
Objects.requireNonNull(before);
return (V v) -> apply(before.apply(v));
}
default <V> Function<T, V> andThen(Function<? super R, ? extends V> after) {
Objects.requireNonNull(after);
return (T t) -> after.apply(apply(t));
}
static <T> Function<T, T> identity() {
return t -> t;
}
}
Function接口的惟一抽象方法是apply,做用是接收一個指定類型的參數,返回一個指定類型的結果
public class FunctionTest1 {
public static void main(String[] args) {
FunctionTest1 ft = new FunctionTest1();
//使用lambda表達式實現apply方法,返回入參+10。形式上如同傳遞了一個方法做爲參數
int res = ft.compute(1, v -> v + 10);
System.out.println(res);//11
}
public int compute(int a, Function<Integer, Integer> function) {
//使用者在使用本方法時,須要去編寫本身的apply,
//傳遞的funtion是一個行爲方法,而不是一個值
return function.apply(a);
}
}
默認方法compose做用是傳入參數後,首先執行compose方法內的Function的apply方法,而後將其返回值做爲本Function方法的入參,調用apply後獲得最後返回值
public class FunctionTest2 {
public static void main(String[] args) {
FunctionTest2 ft = new FunctionTest2();
//調用compose
//先+8,而後將獲得的值3
System.out.println(ft.compute(2, v -> v 3, v -> v + 8));//30
}
public int compute(int a, Function<Integer, Integer> function1, Function<Integer, Integer> function2) {
//將function2先接收入參a,調用apply後,將返回值做爲新的入參,傳入function1,調用apply返回最後結果
return function1.compose(function2).apply(a);
}
}
默認方法andThen與compose正好相反,先執行本Function的apply,而後將結果做爲andThen方法參數內的Function的入參,調用apply後返回最後結果
public class FunctionTest3 {
public static void main(String[] args) {
FunctionTest3 ft = new FunctionTest3();
//調用andThen
//先3,而後將獲得的值+8
System.out.println(ft.compute(2, v -> v 3, v -> v + 8));//14
}
public int compute(int a, Function<Integer, Integer> function1, Function<Integer, Integer> function2) {
//將function2先接收入參a,調用apply後,將返回值做爲新的入參,傳入function1,調用apply返回最後結果
return function1.andThen(function2).apply(a);
}
}
靜態方法identity的做用是傳入啥返回啥,這裏就不寫例子了
Consumer接口
package java.util.function;
import java.util.Objects;br/>@FunctionalInterface
public interface Consumer<T> {
void accept(T t);
default Consumer<T> andThen(Consumer<? super T> after) {
Objects.requireNonNull(after);
return (T t) -> { accept(t); after.accept(t); };
}
}
Consumer接口中accept方法的做用是接收指定參數類型,無返回值,重點在於內部消費
Consumer<String> consumer = s -> System.out.println("hello " + s);
consumer.accept("mike");// hello mike
默認方法andThen做用是連續消費,從本Consumer開始,從外到內,針對同一入參。
Consumer<String> consumer = s -> System.out.println("hello " + s);
Consumer<String> consumer2 = s -> System.out.println("nice to meet you " + s);
consumer.andThen(consumer2).accept("mike");
//hello mike
//nice to meet you mike
Predicate接口
package java.util.function;
import java.util.Objects;br/>@FunctionalInterface
public interface Predicate<T> {
boolean test(T t);
default Predicate<T> and(Predicate<? super T> other) {
Objects.requireNonNull(other);
return (t) -> test(t) && other.test(t);
}
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);
}
static <T> Predicate<T> isEqual(Object targetRef) {
return (null == targetRef)
? Objects::isNull
: object -> targetRef.equals(object);
}
}
Predicate中的test方法,傳入指定類型參數,返回布爾類型的結果,用於判斷,斷言
//判斷一個數是不是偶數
Predicate<Integer> predicate = b -> n % 2 == 0;
System.out.println(predicate.test(3));//false
默認方法and顧名思義,將本Predicate和and參數中的Predicate對同一入參進行test的結果進行【與】操做。
negate方法對test的結果進行【非】操做
or方法對兩個Predicate的test結果進行【或】操做
靜態方法isEqual將其入參與test方法的入參進行equals比較
System.out.println(Predicate.isEqual(1).test(1));//true
Supplier接口
package java.util.function;br/>@FunctionalInterface
public interface Supplier<T> {
T get();
}
Supplier意爲供應,只有一個方法get,不接收任何參數,只返回指定類型結果
Supplier<String> sup = () -> "hello world";
System.out.println(sup.get());
經常使用方法:
一、 Stream filter(Predicate<? super T> predicate); 過濾(方法參數有參有返回值,返回值爲boolean類型)boolean test(T t)
二、 Stream map(Function<? super T, ? extends R> mapper); 將當前流中的T類型數據轉換爲另外一種R類型的流。(有參有返回值) R apply(T t)
三、 void forEach(Consumer<? super T> action); (有參無返回值)void accept(T t)
四、 函數原型爲Stream distinct(),做用是返回一個去除重複元素以後的Stream。
五、 sorted()
排序函數有兩個,一個是用天然順序排序,一個是使用自定義比較器排序,函數原型分別爲Stream sorted()和Stream sorted(Comparator<? super T> comparator)。
Comparator接口方法:int compare(T o1, T o2)
Stream stream= Stream.of(「I」, 「love」, 「you」, 「too」);stream.sorted((str1, str2) -> str1.length()-str2.length()) .forEach(str -> System.out.println(str));六、 Collect
// 將Stream轉換成容器或Map
Stream stream = Stream.of(「I」, 「love」, 「you」, 「too」);
List list = stream.collect(Collectors.toList());
Set set = stream.collect(Collectors.toSet());
Map<String, Integer> map = stream.collect(Collectors.toMap(Function.identity(), String::length));上述代碼可以知足大部分需求,但因爲返回結果是接口類型,咱們並不知道類庫實際選擇的容器類型是什麼,有時候咱們可能會想要人爲指定容器的實際類型,這個需求可經過Collectors.toCollection(Supplier collectionFactory)方法完成。// 使用toCollection()指定規約容器的類型
ArrayList arrayList = stream.collect(Collectors.toCollection(ArrayList::new));
HashSet hashSet = stream.collect(Collectors.toCollection(HashSet::new));使用collect()生成Map
1.使用Collectors.toMap()生成的收集器,用戶須要指定如何生成Map的key和value。
2.使用Collectors.partitioningBy()生成的收集器,對元素進行二分區操做時用到。
3.使用Collectors.groupingBy()生成的收集器,對元素作group操做時用到。html