如果你刚接触 Java,一定写过这样的代码:为了实现一个简单的 “遍历集合” 或 “排序” 功能,不得不写一大段匿名内部类,明明核心逻辑只有一两行,却被一堆模板代码包围。比如用Comparator给列表排序时,代码可能长这样:

import java.util.Arrays;
import java.util.Comparator;

public class Main {
   public static void main(String\[] args) {
       Integer\[] numbers = {3, 1, 4, 1, 5};
       // 用匿名内部类实现排序
      Arrays.sort(numbers, new Comparator\<Integer>() {
           @Override
           public int compare(Integer a, Integer b) {
               // 核心逻辑就这一行:升序排序
               return a - b;
           }
       });
       System.out.println(Arrays.toString(numbers));
   }
}

这段代码里,new Comparator<>()、@Override、public int compare(...)都是 “模板代码”,真正有用的只有return a - b。有没有办法去掉这些冗余,只保留核心逻辑?答案就是 ——Java 8 引入的Lambda 表达式。

一、Lambda 是什么?先搞懂 “函数式接口”

在学 Lambda 之前,必须先明确一个前提:Lambda 表达式本质是 “函数式接口的匿名实现”。

什么是 “函数式接口”?简单说就是:只有一个抽象方法的接口。比如上面的Comparator接口(只有compare一个抽象方法)、Runnable接口(只有run一个抽象方法),这些都是 Java 自带的函数式接口。

为什么要强调这个?因为 Lambda 只能用来简化 “函数式接口” 的实现 —— 它帮我们省略了 “new 接口对象”“写方法签名” 这些模板代码,直接写 “方法体里的核心逻辑”。

二、Lambda 语法:3 部分搞定,超简单!

Lambda 的语法可以拆解成 3 个部分,格式如下:

(参数列表) -> { 方法体 }
部分说明简化规则(可选)
(参数列表)对应函数式接口中抽象方法的参数,参数类型可省略(Java 会自动推断)若只有 1 个参数,可省略括号(如a -> { ... })
->箭头符号,分隔参数和方法体,固定写法无
{方法体}对应抽象方法的实现逻辑,若只有 1 行代码,可省略大括号和return(若有返回值)单行逻辑时,{}和return可一起省略

我们用刚才的 “排序” 例子改造一下,看看 Lambda 有多简洁:

import java.util.Arrays;

public class Main {
   public static void main(String\[] args) {
       Integer\[] numbers = {3, 1, 4, 1, 5};
       // Lambda简化Comparator实现
       Arrays.sort(numbers, (a, b) -> a - b); // 核心逻辑直接写!
       System.out.println(Arrays.toString(numbers)); // 输出:\[1, 1, 3, 4, 5]
  }
}

对比之前的匿名内部类,Lambda 直接把核心逻辑a - b拎了出来,模板代码全没了!再看一个更常见的例子:用Runnable创建线程。

传统匿名内部类写法:

new Thread(new Runnable() {

   @Override
   public void run() {
       System.out.println("线程执行了");
   }
}).start();

Lambda 简化后:

new Thread(() -> System.out.println("线程执行了")).start();

Runnable的run方法没有参数,所以(参数列表)写成();方法体只有 1 行,所以{}和分号都省了,代码瞬间清爽。

三、Lambda 核心应用场景:这些地方用,代码减一半

Lambda 最常用的场景,就是替代 “只有一个抽象方法的匿名内部类”,尤其是结合 Java 集合框架的常用方法,比如遍历、排序、过滤等。下面列举 3 个初学者必用的场景:

场景 1:遍历集合(forEach 方法)

以前遍历List,我们可能用for循环或增强for循环,现在用forEach+Lambda,一行搞定:

import java.util.ArrayList;
import java.util.List;

public class Main {
   public static void main(String\[] args) {
       List\<String> fruits = new ArrayList<>();

       fruits.add("苹果");

       fruits.add("香蕉");

       fruits.add("橙子");

       // Lambda遍历集合,打印每个元素

       fruits.forEach(fruit -> System.out.println("水果:" + fruit));
   }
}

输出结果:

水果:苹果

水果:香蕉

水果:橙子

场景 2:集合排序(sort 方法)

除了前面的数组排序,List的sort方法也能结合 Lambda。比如给自定义对象(如Person)排序:

import java.util.ArrayList;
import java.util.List;

// 自定义Person类
class Person {

   private String name;
   private int age;

   public Person(String name, int age) {
       this.name = name;
       this.age = age;
   }

   // getter方法(简化代码,实际开发需写完整)
   public String getName() { return name; }
   public int getAge() { return age; }

   @Override
   public String toString() {
       return name + "(" + age + "岁)";
   }
}

public class Main {

   public static void main(String\[] args) {
       List\<Person> people = new ArrayList<>();
       people.add(new Person("张三", 25));
       people.add(new Person("李四", 20));
       people.add(new Person("王五", 30));

       // 按年龄升序排序(核心逻辑:比较两个人的年龄)
       people.sort((p1, p2) -> p1.getAge() - p2.getAge());
       
       // 遍历打印排序结果
       people.forEach(person -> System.out.println(person));
   }
}

输出结果:

李四(20岁)

张三(25岁)

王五(30岁)

场景 3:方法引用(进一步简化 Lambda)

如果 Lambda 的方法体,只是 “调用一个已有的方法”,还能进一步用 “方法引用”(::符号)简化。比如上面的遍历打印,System.out.println(fruit)其实是调用System.out的println方法,可用方法引用写成:

// 方法引用简化:System.out::println 等价于 fruit -> System.out.println(fruit)

fruits.forEach(System.out::println);

再比如给Person按姓名排序,若已经有get_name方法,可用:

// 按姓名排序(String的compareTo方法已实现字符串比较)
people.sort((p1, p2) -> p1.getName().compareTo(p2.getName()));

// 方法引用简化:Person::getName 表示“调用Person对象的getName方法”
people.sort(Comparator.comparing(Person::getName));

方法引用不用死记,记住 “能省则省”—— 当 Lambda 里只有一个方法调用时,就可以试试::。

四、初学者必踩的 3 个坑,提前避开!

Lambda 虽然简洁,但初学者容易犯一些小错误,这里提前提醒:

坑 1:Lambda 只能对应 “函数式接口”

如果接口有多个抽象方法,不能用 Lambda。比如下面的接口有 2 个抽象方法,写 Lambda 会报错:

// 错误:这个接口不是函数式接口(有2个抽象方法)

interface MyInterface {

   void method1();
   void method2();

}

// 报错:Lambda表达式不能实现有多个抽象方法的接口

MyInterface obj = () -> System.out.println("test");

坑 2:外部变量 “隐式 final”

Lambda 里引用的外部变量,不能被修改(即使没写final,也默认是 “有效 final”)。比如下面的代码会报错:

public class Main {
   public static void main(String\[] args) {
       int count = 0; // 外部变量
       // 报错:Lambda中引用的变量必须是final或有效final
       new Thread(() -> {
           count++; // 试图修改外部变量,编译错误
           System.out.println(count);
       }).start();
   }
}

原因:Lambda 运行在新线程中,外部变量可能被主线程修改,导致线程安全问题,所以 Java 强制禁止修改。

坑 3:Lambda 没有 “this”

Lambda 里的this,指向的是 “外部类的对象”,而不是 Lambda 自身(因为 Lambda 不是对象,没有自己的this)。比如:

public class Main {
   public void test() {
       // Lambda中的this,指向Main类的实例(即调用test()的对象)
       new Thread(() -> System.out.println(this.getClass().getName())).start();
   }
   public static void main(String\[] args) {
       new Main().test(); // 输出:Main
   }
}

五、总结:Lambda 的核心价值 ——“简化” 与 “聚焦”

学完 Lambda,你会发现它的核心不是 “新语法”,而是帮我们剥离模板代码,聚焦核心逻辑。以前需要写 10 行的匿名内部类,现在 1 行 Lambda 就能搞定,不仅减少了代码量,还让逻辑更清晰 —— 别人看你的代码时,能一眼找到 “真正在做事的部分”。

对于初学者,建议从 “遍历集合”“简单排序” 这些场景开始练手,不用急于掌握复杂的方法引用或 Stream API(Lambda 是 Stream 的基础,学好 Lambda 再学 Stream 会更轻松)。慢慢你会发现:用 Lambda 写代码,不仅快,还更易读,真正告别 “冗长代码” 的烦恼!

Logo

码道开发者社区,聚焦华为云码道 CodeArts 代码智能体,沉淀 Agent、Skill、鸿蒙开发实战内容,供开发者查阅资料、交流技术、分享工程实践

更多推荐