Thread thread = new Thread(() -> {
System.out.println("HelloWorld");
});
thread.start();
複製代碼
「Lambda表達式
理解爲簡潔地表示可傳遞的匿名函數的一種方式:它沒有名稱,但它 有參數列表、函數主體、返回類型,可能還有一個能夠拋出的異常列表」(參考Java8實戰)java
Lambda表達式包含3部分:express
參數列表使用括號()來表示,其中參數能夠聲明參數類型,也能夠不聲明參數類型(編譯器會根據上下文推斷)。bash
Lambda表達式的函數體,既能夠是代碼塊
,也能夠是表達式
。 代碼塊:和普通方法的方法主體一致; 表達式:表達式會被執行並返回結果,其本質上是一種return語句的簡寫(省略了return和{})app
Lambda不只能夠省略參數類型、用表達式表示方法體,其實還能夠直接使用方法引用
來替代Lambda表達式,例如以下三種方式是等價的:dom
Consumer<String> consumer1 = (String str) -> {
System.out.println(str);
};
Consumer<String> consumer2 = (str) -> {
System.out.println(str);
};
Consumer<String> consumer3 = System.out::println;
複製代碼
Lambda表達式擁有以下特色:ide
Lambda表達式
用來爲某個抽象方法提供實現,我的能夠粗略地講其理解爲一種匿名內部類
更加通俗易懂,但二者並不徹底相同。函數
二者有以下相同點:ui
final String word = "Hello";
Runnable runnable = () -> {
// 不能夠在Lambda表達式中修改外部變量,不然會提示Variable used in lambda expression should be final or effectively final
// word = "HelloWorld";
function1();
System.out.println(word);
};
runnable.run();
Runnable runnable1 = new Runnable() {
@Override
public void run() {
function1();
// 不能夠在內部類中修改外部變量,不然會提示Variable is accessed within inner class. Needs to be final or effectively final
// word = "HelloWorld";
System.out.println(word);
}
};
runnable1.run();
複製代碼
this
指的是外部類的對象,而內部類中this
指的是內部類對象。見做用域示例1。 (2)Lamda表達式中調用的方法也都是外部類的,但內部類中調用的方法是優先內部的。public class HelloLambda {
Runnable r1 = () -> {
System.out.println("Lambda, 1: " + this);
System.out.println("Lambda, 2: " + toString());
};
Runnable r2 = new Runnable() {
@Override
public void run() {
System.out.println("Inner class, 1: " + this);
System.out.println("Inner class, 2: " + toString());
}
};
@Override
public String toString() {
return "Hello, lambda!";
}
public static void main(String[] args) {
new HelloLambda().r1.run();
new HelloLambda().r2.run();
}
}
複製代碼
輸出:this
Lambda表達式r1因爲外部類中沒有run()方法,因此報錯;但r2因爲內部類中有run()方法,全部能夠經過編譯,實際上就是循環調用了。 編碼
函數式接口
就是有且僅有一個抽象方法的接口。
Lambda表達式容許直接之內聯的形式爲函數式接口的抽象方法提供實現,並把整個表達式做爲函數式接口的實例。(其實能夠將Lambda表達式看作匿名內部類,該匿名內部類實現了函數式接口)。
接下來介紹幾種常見函數式接口。
Runnable接口源碼
@FunctionalInterface
public interface Runnable {
public abstract void run();
}
複製代碼
觀察Runnable源碼,能夠發現該接口中只有一個抽象方法run(),故能夠使用Lambda表達式來做爲該函數式接口的實例,並提供抽象方法的實現。
Runnable runnable = () -> {
System.out.println("Hello world!");
};
複製代碼
Callable接口源碼:
@FunctionalInterface
public interface Callable<V> {
V call() throws Exception;
}
複製代碼
Callable接口的Lambda表達式示例:
Callable<String> callable = () -> {
return "HelloWorld";
};
複製代碼
Callable接口有返回值,Lambda主體能夠直接使用表達式替代return語句,以下:
Callable<String> callable = () -> "HelloWorld";
複製代碼
@FunctionalInterface
public interface Comparator<T> {
// 抽象方法
int compare(T o1, T o2);
// 重寫Obejct類public方法的抽象方法,不進行計數
boolean equals(Object obj);
// 非抽象方法
default Comparator<T> reversed() {
return Collections.reverseOrder(this);
}
}
複製代碼
觀察源碼,能夠發現Comparator實現類:(1)須要聲明泛型;(2)須要實現compare方法,且參數列表有兩個,參數類型與泛型類上聲明一致。
Comparator<Integer> comparator = (o1, o2) -> o1.compareTo(o2);
複製代碼
List<Integer> numbers = Lists.newArrayList(1, 2, 3);
numbers.sort((x1, x2) -> x2.compareTo(x1));
numbers.forEach((x) -> {
System.out.println(x);
});
複製代碼
輸出:
按user年齡倒序排序:
List<User> users = Lists.newArrayList(new User(18), new User(22), new User(24));
users.sort((u1, u2) -> u2.age.compareTo(u1.age));
users.forEach((u) -> {
System.out.println(u.age);
});
複製代碼
輸出:
Consumer接口是一種有參無返回值的消費型接口。
@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); };
}
}
複製代碼
Consume接口能夠理解爲一種通用消費型接口(能夠接收參數,但不能返回),經過lambda表達式定義好要執行的動做,經過調用accept方法執行。
Consumer<String> consumer = s -> System.out.println(s);
consumer.accept("Hello world1!");
複製代碼
拿其與Runable接口比較更好理解,Runnable接口也能夠直接執行run方法(就沒有線程特性了),可是沒法接受傳入的參數。比較
Consumer<String> consumer = s -> System.out.println(s);
consumer.accept("Hello world1!");
Runnable runnable = () -> {
System.out.println("Hello world2!");
};
runnable.run();
複製代碼
我的理解,Consumer接口最多見的用途是做爲回調。
以下所示,function1接收參數list、以及回調函數consumer,function1自己不關心如何處理、直接執行consumer.accept(),具體的處理由傳入的consumer變量(lambda表達式、回調函數)決定,能夠經過修改傳入的consumer變量來修改函數的實現。
public static void function1(List<Integer> list, Consumer<List<Integer>> consumer) {
consumer.accept(list);
}
public static void main(String[] args) {
// 存儲lambda表達式
Consumer<List<Integer>> consumer1 = list -> {
list.sort((x1, x2) -> x2.compareTo(x1));
};
Consumer<List<Integer>> consumer2 = list -> {
list.sort((x1, x2) -> x1.compareTo(x2));
};
List<Integer> numbers = Lists.newArrayList(1, 2, 3);
// 傳遞lambda表達式
function1(numbers, consumer1);
numbers.forEach((x) -> {
System.out.println(x);
});
function1(numbers, consumer2);
numbers.forEach((x) -> {
System.out.println(x);
});
}
複製代碼
BiConsumer接口則是Consumer接口升級版,能夠接受兩個參數:
BiConsumer<String, Integer> biConsumer = (str, num) -> {
System.out.println(str + num);
};
biConsumer.accept("Hello, ", 123);
複製代碼
還有其餘消費型接口,好比IntConsumer。
Supplier
接口是一種無參有返回值的供給型接口。能夠理解爲一個容器,能夠生成、存儲對象,供其它方法調用其中get()
方法獲取對象。
@FunctionalInterface
public interface Supplier<T> {
T get();
}
複製代碼
Supplier<User> supplier = () -> {
return new User(123);
};
User user = supplier.get();
System.out.println(user.age);
複製代碼
以下所示,getId方法用於返回一個int型整數,但不一樣的地方可能使用不一樣的生成策略,因此使用Supplier接口做爲參數,將id生成策略回調出去。
public static Integer getId(Supplier<Integer> supplier) {
return supplier.get();
}
public static void main(String[] args) {
Supplier<Integer> supplier1 = () -> (new Random()).nextInt(10);
Supplier<Integer> supplier2 = () -> (new Random()).nextInt(100);
System.out.println(getId(supplier1));
System.out.println(getId(supplier2));
}
複製代碼
可是我的理解上Supplier接口意義很低,不能接受參數、直接return,即使做爲回調也難以有太多實際使用場景。
Predicate接口一種有參數返回值類型爲布爾型的斷言型接口。
@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);
}
}
複製代碼
觀察源碼能夠發現,須要實現test()方法,而且還支持and/or聯合判斷條件。
Predicate<Integer> predicate = (x) -> x < 100;
Predicate<Integer> predicate1 = (x) -> x > 0;
System.out.println(predicate.test(1));
System.out.println(predicate.and(predicate1).test(-1));
複製代碼
輸出結果:
@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;
}
}
複製代碼
觀察源碼獲得須要實現其中apply()
方法,而lambda表達式的參數<T, R>,其中T表明函數的參數類型,R表明返回類型(第一個是參數,第二個是返回值)。
Function
接口使用的場景較多Consumer/Predicate/Supplier接口能實現的功能用Function接口也均可以實現。
Function<Integer, String> function = (x) -> {
if (x < 0) {
return "小於0";
} else {
return "大於等於0";
}
};
System.out.println(function.apply(3));
複製代碼
Function
接口一樣也有用來接收兩個參數的BiFunction
接口。
BiFunction<Integer, Integer, String> function = (x1, x2) -> {
return new StringBuilder("Receive x1=").append(x1).append(", x2=").append(x2).toString();
};
複製代碼
Java8新增了一個接口java.util.Stream,能夠將Collection、List、Set、Map等使用流進行處理,編碼更加方便快捷。Stream接口依賴於Lambda表達式。 詳見:。。。。。。。。。