首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> Java 程式設計 >> Java 綜合教學

Java 8 Lambda 表達式全面教程:語法、函數式接口、Stream 流操作與方法引用實戰

     編輯:Java 綜合教學

Java 8 Lambda 表達式全面教程:從語法基礎到流式編程實戰

Lambda 表達式是 Java 8 最具革命性的特性,標誌著 Java 正式引入函數式編程風格。它不僅簡化了匿名內部類的冗長語法,更配合 Stream API 實現了集合的「內部迭代」與並行處理。本文系統講解 Lambda 語法、目標類型推斷、核心函數式接口、Stream 流操作模式、變量捕獲、方法引用及默認方法等核心知識點。

一、 Lambda 語法基礎

Lambda 本質是匿名方法,語法結構:(參數列表) -> { 方法體 } 或 (參數列表) -> 表達式。

1. 基本形式示例

// 無參無返回 (Runnable)
() -> System.out.println("Hello Lambda")

// 單參數,類型可推斷,單行表達式 (Function)
s -> s.length()

// 多參數,顯式類型,代碼塊帶返回
(int x, int y) -> { return x + y; }

// 多參數,類型推斷,表達式體 (隱式返回)
(x, y) -> x + y

2. 結構三要素

  • 參數列表:括號內,類型可省略(上下文推斷),單參數可省括號。
  • 箭頭 ->:分隔參數與主體。
  • 主體:單行表達式(隱式返回)或 { ... } 代碼塊(需显式 return)。

二、 目標類型與函數式接口

Lambda 無獨立類型,其類型由上下文推斷,稱為「目標類型」。目標類型必須是函數式接口:有且僅有一個抽象方法的接口(可用 @FunctionalInterface 標註)。

1. JDK 內置四大核心函數式接口 (java.util.function)

接口抽象方法語義典型場景
Predicate<T>boolean test(T t)斷言/過濾stream.filter()
Function<T, R>R apply(T t)轉換/映射stream.map()
Consumer<T>void accept(T t)消費/處理stream.forEach()
Supplier<T>T get()供給/生成Stream.generate()

此外還有 Operator (一元/二元運算)、Comparator、Runnable、Callable 等。

2. 類型推斷示例

 c = () -> "Result";          // 目標類型 Callable
Comparator cmp = (a, b) -> a - b;    // 目標類型 Comparator

// 錯誤:Object 不是函數式接口
// Object o = () -> {}; // Compile Error
// 必須顯式轉型
Object o = (Runnable) () -> {}; // OK

三、 Lambda 與 Stream 流式編程實戰

Stream API 實現集合的內部迭代(由庫控制迭代),支持鏈式操作(Pipeline)、懶加載與並行計算。

1. Stream 創建

List list = Arrays.asList("a", "b", "c");
Stream s1 = list.stream();        // 串行流
Stream s2 = list.parallelStream(); // 並行流
Stream s3 = Stream.of("a", "b", "c");
Stream s4 = Stream.iterate(0, n -> n + 2).limit(5); // 0,2,4,6,8

2. 中間操作 (懶加載,返回新 Stream)

list.stream()
    .filter(s -> s.length() > 3)        // Predicate 過濾
    .map(String::toUpperCase)           // Function 映射 (方法引用)
    .distinct()                         // 去重
    .sorted()                           // 排序
    .peek(s -> System.out.println(s))   // 側寫調試
    .limit(10)                          // 截取
    .skip(2);                           // 跳過

3. 終端操作 (急加載,產生結果)

// 收集
List result = stream.collect(Collectors.toList());
Set set = stream.collect(Collectors.toSet());
Map map = stream.collect(Collectors.toMap(String::length, s -> s));

// 歸約
Optional max = stream.max(Comparator.naturalOrder());
int sum = stream.mapToInt(String::length).sum(); // 原始流優化

// 遍歷/匹配
stream.forEach(System.out::println);
boolean any = stream.anyMatch(s -> s.startsWith("a"));
boolean all = stream.allMatch(s -> s.length() > 0);

4. 經典 Filter-Map-Reduce 實戰

// 需求:從字符串列表中提取不重複的素數並求和
List inputs = Arrays.asList("2", "3", "4", "5", "5", "7", "11", "abc");

int sum = inputs.stream()
    .map(Integer::valueOf)              // String -> Integer (Function)
    .filter(Primes::isPrime)            // Predicate 過濾素數
    .distinct()                         // 去重
    .reduce(0, Integer::sum);           // Reduce 求和 (BinaryOperator)

System.out.println("Distinct Prime Sum: " + sum); // 2+3+5+7+11 = 28

5. 分組統計 (Collectors.groupingBy)

// 統計不同年齡段人數
Map ageGroupCount = persons.parallelStream() // 並行
    .collect(Collectors.groupingBy(
        p -> p.getAge() < 18 ? "Minor" : "Adult",
        Collectors.counting()
    ));

// 多級分組:性別 -> 年齡段 -> 列表
Map>> multi = persons.stream()
    .collect(Collectors.groupingBy(
        Person::getSex,
        Collectors.groupingBy(p -> p.getAge() > 30 ? "Senior" : "Junior")
    ));

四、 進階特性

1. 變量捕獲與 Effectively Final

Lambda 可訪問外部局部變量,但變量必須是 final 或 effectively final(僅賦值一次)。這解決了多線程下的共享可變狀態問題。

int factor = 2; // effectively final
Function multiplier = x -> x * factor;
// factor = 3; // 編譯錯誤:不再 effectively final

// 實例字段、靜態字段無限制
this.instanceField = 10;
Function f = x -> x + this.instanceField; // 捕獲 this

2. 方法引用 (Method Reference) ::

當 Lambda 僅調用現有方法時,用方法引用更簡潔:類名::方法名 或 對象::方法名。

// 靜態方法引用
Function parser = Integer::valueOf; // s -> Integer.valueOf(s)

// 實例方法引用 (特定對象)
Consumer printer = System.out::println; // s -> System.out.println(s)

// 實例方法引用 (任意對象) - 參數成為調用者
Comparator cmp = String::compareToIgnoreCase; // (s1,s2) -> s1.compareToIgnoreCase(s2)

// 構造器引用
Supplier> listFactory = ArrayList::new; // () -> new ArrayList()
Function userCreator = User::new;      // name -> new User(name)

// 數組構造器引用
IntFunction arrayMaker = String[]::new;    // len -> new String[len]

3. 默認方法與靜態方法 (Interface Evolution)

Java 8 允許接口定義默認實現 (default) 和靜態方法,解決接口擴展兼容性問題(如 Collection.stream())。

interface MyInterface {
    void abstractMethod();
    
    default void defaultMethod() { // 默認實現
        System.out.println("Default impl");
    }
    
    static void staticMethod() { // 靜態工具方法
        System.out.println("Static helper");
    }
}

菱形繼承衝突:實現類必須顯式覆蓋並選擇調用 SuperInterface.super.defaultMethod()。

4. 生成器與無限流

// 無限隨機數流,需配合 limit() 短路
Stream.generate(Math::random)
      .limit(5)
      .forEach(System.out::println);

// 迭代生成斐波那契數列
Stream.iterate(new int[]{0, 1}, t -> new int[]{t[1], t[0]+t[1]})
      .limit(10)
      .map(t -> t[0])
      .forEach(System.out::println);

五、 最佳實踐與避坑指南

  1. 優先用方法引用:代碼更簡潔、可讀性更高(list.forEach(System.out::println))。
  2. 避免副作用:Stream 操作應為純函數,不要在 map/filter 中修改外部狀態。
  3. 並行流慎用:僅在 CPU 密集型、數據量大、無共享狀態時使用 parallelStream(),否則上下文切換開銷得不償失。
  4. 基本類型流優化:使用 IntStream/LongStream/DoubleStream 避免裝箱拆箱開銷。
  5. 不要在 Lambda 中拋受檢異常:函數式接口未聲明拋出,需內部 try-catch 或自定義函數式接口。

六、 總結

Lambda 表達式 + Stream API 徹底改變了 Java 集合處理方式,從「外部迭代、命令式」轉向「內部迭代、聲明式、可並行」。掌握四大核心函數式接口、中間/終端操作分類、方法引用、變量捕獲規則,即可編寫優雅、高效、現代化的 Java 代碼。這也是進階學習 Reactive Streams (Reactor/RxJava)、並行計算 (ForkJoin/CompletableFuture) 的基石。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved