JDK 8 新特性 lambda
阿新 • • 發佈:2021-02-09
技術標籤:java
引言
在 JDK 8 之前介面怎麼例項化呢?
- 正經方式是定義一個類去實現介面,然後實現介面中的方法;
public class Test implements Runnable{
public static void main(String[] args) {
new Thread(new Test()).start();
}
@Override
public void run() {
System.out.println("thread name " + Thread.currentThread ().getName() + " is running.");
}
}
- 匿名內部類的方式去實現介面,這種方式與第一種相比會少一個 .java 的原始檔,但編譯後也會產生位元組碼檔案,檔名格式是類名 $ 加上從 1 開始的編號;
public class Test {
public static void main(String[] args) {
new Thread(new Runnable(){
@Override
public void run() {
System. out.println("thread name " + Thread.currentThread().getName() + " is running.");
}
}).start();
}
}
3. lambda 方式實現介面,這種方式編譯後不會產生 .class 檔案,且你不用指明該類要去實現什麼方法,從程式碼量上可以感受到它使程式碼更優雅的特性。
public class Test {
public static void main(String[] args) {
new Thread(() -> System.out.println("thread name " + Thread.currentThread().getName() + " is running."));
}
}
概念
lambda 是 JDK 8 之後的新特性,可以取代大部分需要用到匿名內部類的場景,常用在集合的遍歷操作中。
它可以對函式式介面做一個簡單的實現,但不是所有的介面實現都能使用 lambda 去編寫。它要求被實現的介面只能有一個抽象方法(函式式介面)。
JDK 提供的內建函式式介面之一:
// 該註解通常和 lambda 一同出現,它表明該介面的實現可以使用簡潔的 lambda 實現
@FunctionalInterface
public interface Consumer<T> {
void accept(T t);
// default 方法是 jdk 8 之後有的特性
default Consumer<T> andThen(Consumer<? super T> after) {
Objects.requireNonNull(after);
return (T t) -> {
accept(t);
after.accept(t);
};
}
}
語法
正常版
(Object a,Object b,Object c,...) -> {...}
小括號裡面就是被重寫的方法需要的各引數值
花括號裡面就是實現類要編寫的方法實現邏輯
lambda 表示式其實就是函數語言程式設計思想的落地,所以 lambda 就是編寫一個 Java 中的方法,既然是方法,那麼它就有引數列表(小括號裡面的內容),也有方法體(花括號中的內容)。
簡化版
- 引數列表中的所有引數的型別可以一起省略;
(a, b, c,...) -> {...}
@FunctionalInterface
public interface Running {
void speed(String day, Integer speed);
}
public class Test{
public static void main(String[] args) {
Running running = (day, speed) -> {
System.out.println(day + "---" + speed);
};
running.speed("20200123", 200);
}
}
- 當引數列表就一個引數時,可以省略 () ;
a -> {...}
@FunctionalInterface
public interface Running {
void speed(String day);
}
public class Test{
public static void main(String[] args) {
Running running = day -> {
System.out.println(day);
};
running.speed("20200123");
}
public void method(String day) {
System.out.println(day);
}
}
- 當方法體只有一條語句時,可以省略 {},如果這個方法有返回值,那麼該條語句前面會預設加上 return 關鍵字。
public class Test{
public static void main(String[] args) {
Running running = day -> System.out.println(day);
running.speed("20200123");
}
}
- 如果你重寫的那個匿名內部類的方法,有一個方法已經幫你實現了,此時我們就沒必要再去實現。語法上分兩種引用寫法,分別是靜態引用和例項引用兩種:
public class Test{
public static void main(String[] args) {
//Running running = day -> System.out.println(day);
//靜態引用
Running running = System.out::println;
//例項引用
Running running = new Test()::method;
running.speed("20200123");
}
public void method(String day) {
System.out.println(day);
}
}
常用示例
1. 在集合中的應用舉例(ArrayList )
forEach(Consumer<? super E> action) 迭代
集合遍歷的業務程式碼
public class App {
static List<String> list = new ArrayList<>();
public static void main(String[] args) {
list.add("上海");
list.add("北京");
list.add("廣州");
list.add("深圳");
// jdk 8 之前匿名內部類寫法
list.forEach(new Consumer<String>() {
@Override
public void accept(String s) {
System.out.println(s);
}
});
// 普通 lambda 寫法
list.forEach(s -> {
System.out.println(s);
});
// 簡便寫法,如果實現類重寫的方法是其他類已經實現的類方法
list.forEach(System.out::println);
// 簡便寫法,如果實現類重寫的方法是其他例項已有的方法
list.forEach(new App()::method);
}
// 例項方法
public void method(String ele) {
System.out.println(ele);
}
}
// ArrayList 複寫 Iterable 介面的 forEach 方法
@Override
public void forEach(Consumer<? super E> action) {
Objects.requireNonNull(action);
final int expectedModCount = modCount;
@SuppressWarnings("unchecked")
final E[] elementData = (E[]) this.elementData;
final int size = this.size;
// fori 迴圈依次獲取每個元素傳遞給 action 實現的 accept 方法
for (int i=0; modCount == expectedModCount && i < size; i++) {
action.accept(elementData[i]);
}
if (modCount != expectedModCount) {
throw new ConcurrentModificationException();
}
}
程式碼解讀:
forEach 方法需要傳遞 Consumer 物件,所以需要宣告一個實現了 Consumer 介面的型別,你可以單獨寫個類去做實現,一般都使用匿名內部類的方式做的實現,但在 JDK 8 之後可以使用 lambda 表示式對函式式介面做個簡單的實現。
下面是 foreach 中最主要的兩行程式碼:
action.accept(elementData[i]);
// elementData[i] 會傳遞給 s,而 s 就是方法的引數,所以方法體能拿到這個變數的引用
list.forEach(s -> System.out.println(s));
removeIf(Predicate<? super E> filter) 刪除元素
業務程式碼
// 匿名內部類寫法
list.removeIf(new Predicate<String>() {
@Override
public boolean test(String s) {
return s.equals("上海");
}
});
// lambda 寫法
list.removeIf(s -> s.equals("上海"));
@Override
public boolean removeIf(Predicate<? super E> filter) {
...
for (int i=0; modCount == expectedModCount && i < size; i++) {
@SuppressWarnings("unchecked")
final E element = (E) elementData[i];
if (filter.test(element)) {
removeSet.set(i);
removeCount++;
}
}
...
}