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

30 分鐘掌握 Java 8 Lambda 表達式:從匿名內部類到函數式編程

     編輯:Java 程式設計入門

為什麼需要 Lambda?

Java 7 時代,給按鈕註冊點擊事件需寫匿名內部類:

button.setOnClickListener(new View.OnClickListener() {
    @Override
    public void onClick(View v) {
        Log.d("TAG", "按鈕被點擊");
    }
});

痛點:6 行樣板代碼只為 1 行業務邏輯,可讀性差、冗長、難維護。

Java 8 Lambda 一行解決:

button.setOnClickListener(v -> Log.d("TAG", "按鈕被點擊, Lambda"));

Lambda 結構拆解

(參數列表) -> { 方法體 }
元素規則示例
參數列表零個或多個,類型可省略 (編譯器推導),單參數可省略括號(), x, (int x, int y)
箭頭-> 固定符號-
方法體單語句可省略大括號和 return;多語句需大括號並显式 returnx + 1, { System.out.println(x); return x; }

常見寫法速查

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

// 單參無返回 (類型推導)
x -> System.out.println(x);

// 多參有返回 (隱式 return)
(x, y) -> x + y

// 多參多語句 (顯式 return)
(int x, int y) -> {
    int sum = x + y;
    System.out.println("計算中...");
    return sum;
}

// 方法引用 (更簡潔)
System.out::println
String::length
Math::max
obj::instanceMethod
Class::staticMethod

函數式接口:Lambda 的類型契約

定義:有且僅有一個抽象方法的接口 (SAM)。可標註 @FunctionalInterface 編譯期校驗。

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

接口抽象方法輸入輸出典型場景
Predicate<T>boolean test(T t)Tboolean過濾、判斷
Function<T,R>R apply(T t)TR轉換、映射
Consumer<T>void accept(T t)Tvoid消費、打印、存儲
Supplier<T>T get()無T工廠、延遲加載

另有 BiFunction、BinaryOperator、UnaryOperator 等變體。

實戰演示:四大接口用法

public class FunctionalInterfaceDemo {
    public static void main(String[] args) {
        // Predicate: 判斷字符串長度 > 5
        Predicate longEnough = s -> s.length() > 5;
        System.out.println(longEnough.test("Lambda")); // true

        // Function: 字符串轉長度
        Function strLen = String::length; // 方法引用
        System.out.println(strLen.apply("Hello")); // 5

        // Consumer: 打印每個元素
        Consumer printer = System.out::println;
        printer.accept("Consumer 輸出");

        // Supplier: 惰性生成隨機數
        Supplier random = Math::random;
        System.out.println(random.get());

        // 組合使用: 過濾 -> 轉換 -> 消費
        List words = Arrays.asList("a", "ab", "abc", "abcd", "abcde");
        words.stream()
             .filter(longEnough)           // Predicate
             .map(strLen)                  // Function
             .forEach(printer);            // Consumer
    }
}

變量捕獲:Effectively Final 規則

Lambda 只能引用最終語義最終的局部變量 (不可變),即編譯期可推導為 final:

int factor = 2;
// factor = 3; // 取消註釋會編譯錯誤
Function multiplier = x -> x * factor; // OK,factor 未改變

// 需可變狀態?用最終數組/Atomic 包裝
final int[] counter = {0};
Runnable r = () -> counter[0]++; // OK
// 或
AtomicInteger atomic = new AtomicInteger();
Runnable r2 = () -> atomic.incrementAndGet();

方法引用四種形式

類型語法對應 Lambda示例
靜態方法Class::staticMethod(args) -> Class.staticMethod(args)Integer::parseInt
實例方法 (特定對象)obj::instanceMethod(args) -> obj.instanceMethod(args)System.out::println
實例方法 (任意對象)Class::instanceMethod(obj, args) -> obj.instanceMethod(args)String::length
構造方法Class::new(args) -> new Class(args)ArrayList::new

Android 開發配置 (歷史參考,現 AGP 4.0+ 原生支持)

// app/build.gradle (舊版 Jack 工具鏈,已棄用)
android {
    defaultConfig {
        jackOptions { enabled true }
    }
    compileOptions {
        sourceCompatibility JavaVersion.VERSION_1_8
        targetCompatibility JavaVersion.VERSION_1_8
    }
}

// 現代做法 (AGP 4.0+): 無需額外配置,直接用 Java 8 語法
// 確保 compileSdk 29+、minSdk 24+ (API 24 以下需啟用 coreLibraryDesugaring)
android {
    compileOptions {
        coreLibraryDesugaringEnabled true
    }
}
dependencies {
    coreLibraryDesugaring 'com.android.tools:desugar_jdk_libs:1.2.2'
}

總結:Lambda 核心清單

  1. 語法:(params) -> body,類型推導、單語句省略 {}/return。
  2. 類型:函數式接口 (單抽象方法),@FunctionalInterface 校驗。
  3. 四大接口:Predicate/Function/Consumer/Supplier 覆蓋 90% 場景。
  4. 方法引用::: 讓代碼更聲明式、更簡潔。
  5. 變量捕獲:effectively final,可變狀態用數組/Atomic 包裝。
  6. 避坑:別在 Lambda 內拋受檢異常 (需自行 try-catch)、別濫用導致可讀性下降、注意序列化問題 (實現 Serializable 的函數式接口才可序列化)。
  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved