lambda表达式练习案例

📅 2026/7/15 23:52:04
lambda表达式练习案例
前言在Java 8问世之前我们如果想给某个方法传入一段行为只能通过匿名内部类来实现——创建一个接口的匿名实现对象然后在里面覆盖方法。这种写法虽然能完成任务但代码臃肿、可读性差充斥着大量与核心逻辑无关的样板代码。Lambda表达式的出现正是为了解决这个问题。它让我们可以用极其简洁的语法来表达行为把注意力从对象转移到操作本身。本文通过两数之和和找出偶数两个实战练习从匿名内部类写法逐步过渡到Lambda表达式写法在对比中带你理解Lambda到底简化了什么为什么它能这样简化什么情况下能用Lambda什么情况下不行一步步揭开Lambda表达式背后的设计思想和适用边界。摘要本文通过两个实战练习系统讲解Lambda表达式如何优化匿名内部类的冗余写法。首先在两数之和和找出偶数练习中分别展示匿名内部类与Lambda两种实现方式的代码对比直观呈现Lambda在代码量上的简化效果。进而深入分析简化的本质原因Lambda只能替换函数式接口即只有一个抽象方法的接口的匿名内部类因为它省略了对象创建、方法名声明、参数类型、返回值类型等所有编译器可推断的冗余信息只保留核心操作逻辑。文章进一步总结Lambda的三个简化层次和适用前提条件并指出这种写法背后是从面向对象到面向行为的思维转变。最后通过延伸思考引导读者继续深入学习自定义函数式接口、Stream流式编程和方法引用等进阶内容。目录一、练习1两数之和匿名内部类写法优化为lambda表达式二、练习2找出偶数匿名内部类写法优化为lambda表达式思考lambda是如何优化匿名内部类写法的1. 从两个练习中找规律2. 什么是函数式接口3. Lambda为什么能替换匿名内部类4. 从面向对象到面向行为的思维转变5. Lambda的三个简化层次6. 使用Lambda的前提条件7. 延伸思考一、练习1两数之和匿名内部类写法public class Main { public static void main(String[] args) { //直接调用方法获取结果 int i calculateNum(new IntBinaryOperator() { Override public int applyAsInt(int left, int right) { return left right; } }); //打印结果 System.out.println(计算结果 i); } //定义一个静态方法接收一个IntBinaryOperator参数该参数本质是一个jdk自带的接口 public static int calculateNum(IntBinaryOperator operator){ int a 10; int b 20; //返回ab的和 return operator.applyAsInt(a, b); } }运行结果如下注意上面静态方法的参数是jdk自带的一个接口它既然要求接收一个该类型的接口那我们直接创建一个接口并实现了下面的applyAsInt方法传进去即可。不难发现该接口下面只有一个方法这就叫“函数式接口”。这也是能进行lambda优化的最关键一点。优化为lambda表达式public class Main { public static void main(String[] args) { //直接调用方法获取结果 int i calculateNum((int left, int right)- { return left right; }); //打印结果 System.out.println(计算结果 i); } //定义一个静态方法接收一个IntBinaryOperator参数该参数本质是一个jdk自带的接口 public static int calculateNum(IntBinaryOperator operator){ int a 10; int b 20; //返回ab的和 return operator.applyAsInt(a, b); } }二、练习2找出偶数匿名内部类写法public class Main { public static void main(String[] args) { //调用静态方法 printNum(new IntPredicate() { Override public boolean test(int value) { return value%20;//定标准偶数则返回true奇数则返回false } }); } //定义一个静态方法接收一个IntPredicate参数该参数本质是一个jdk自带的接口 public static void printNum(IntPredicate predicate){ int[] arr {1,2,3,4,5,6,7,8,9}; //筛选出arr中符合要求的数字 for(int i: arr){ if(predicate.test(i)){ System.out.println(i);//打印出符合要求的数字 } } } }运行结果如下优化为lambda表达式public class Main { public static void main(String[] args) { //调用静态方法 printNum((int value) - { return value%20;//定标准偶数则返回true奇数则返回false }); } //定义一个静态方法接收一个IntPredicate参数该参数本质是一个jdk自带的接口 public static void printNum(IntPredicate predicate){ int[] arr {1,2,3,4,5,6,7,8,9}; //筛选出arr中符合要求的数字 for(int i: arr){ if(predicate.test(i)){ System.out.println(i);//打印出符合要求的数字 } } } }运行结果如下思考lambda是如何优化匿名内部类写法的通过上面两个练习的对比我们直观地感受到了Lambda表达式带来的代码简化。但作为学习者我们不能只停留在会用的层面更要理解它为什么能这样简化以及简化的边界在哪里。1. 从两个练习中找规律回顾练习1和练习2你会发现一个共同点练习1练习2自定义方法形参IntBinaryOperator operatorIntPredicate predicate形参类型接口接口接口抽象方法int applyAsInt(int left, int right)boolean test(int value)匿名内部类写法new IntBinaryOperator() { ... }new IntPredicate() { ... }Lambda写法(left, right) - left rightvalue - value % 2 0规律很清晰当方法的形参是一个接口时我们就可以用Lambda表达式来简化传参。但这里有一个更精确的结论——不是所有接口都可以用Lambda替换只有函数式接口才行。2. 什么是函数式接口函数式接口简单来说就是只有一个抽象方法的接口。IntBinaryOperator只有一个抽象方法applyAsIntIntPredicate只有一个抽象方法test这两个都是JDK自带的函数式接口它们都标记了一个注解FunctionalInterface这个注解不是必须的但加上后编译器会帮你检查是否只有一个抽象方法。注意接口中可以存在多个方法但只要抽象方法只有一个它就是函数式接口。默认方法default修饰和静态方法不影响。3. Lambda为什么能替换匿名内部类我们来剖析一下匿名内部类的写法new IntBinaryOperator() { Override public int applyAsInt(int left, int right) { return left right; } }这段代码包含了哪些信息对象创建new IntBinaryOperator() { ... }—— 创建一个接口的匿名实现类对象方法声明public int applyAsInt(int left, int right)—— 方法名、返回值、参数列表方法体return left right;—— 真正的业务逻辑而Lambda表达式只写了什么(left, right) - left rightLambda把冗余信息全部省略了被省略的内容原因new 接口名() { ... }编译器知道这里需要一个接口的实现不需要显式new方法名applyAsInt接口只有一个抽象方法默认就是实现它无需指名返回值类型int编译器能从上下文推断出返回int参数类型int编译器能从接口方法定义推断出参数类型{}和return只有单条语句时可以省略()只有一个参数时可以省略总结一句话Lambda表达式关注的是做什么而把谁来做和怎么做声明全部交给编译器去推断。4. 从面向对象到面向行为的思维转变这是理解Lambda最核心的一点。匿名内部类的思维方式我需要创建一个接口的实现对象这个对象里要覆盖那个抽象方法方法里写具体的操作。这是一种先造人再让人干活的面向对象思维。Lambda的思维方式我需要一个操作这个操作接收这些数据返回那个结果。这是一种直接描述动作的函数式思维。用生活中的例子来类比匿名内部类就像你点餐时餐厅给你一个厨师对象让他去做菜方法。Lambda表达式就像你直接说我要一份番茄炒蛋行为本身不需要关心是谁做的。5. Lambda的三个简化层次从练习中我们可以总结出Lambda的三个简化层级第一层完整写法省略new和接口名(int left, int right) - { return left right; }第二层省略参数类型编译器推断(left, right) - { return left right; }第三层省略花括号、return和分号单条语句时(left, right) - left right对于单参数的情况还可以省略小括号value - value % 2 0 // 而不是 (value) - value % 2 06. 使用Lambda的前提条件我们最后总结一下使用Lambda替换匿名内部类需要满足的条件条件说明① 接口类型方法的形参必须是接口不能是抽象类或普通类② 函数式接口该接口只能有一个抽象方法③ 上下文明确编译器能推断出目标类型即接口类型特别注意如果接口有多个抽象方法就无法用Lambda因为编译器不知道你要实现的是哪一个。这时候只能继续用匿名内部类。7. 延伸思考学到这里你可能会想如果我想自己定义一个这样的接口该怎么写除了替换匿名内部类Lambda还能用在什么地方::这个写法又是什么这些问题我们留到后续文章继续探讨。但无论如何请记住今天的核心收获Lambda表达式的本质是从创建对象来做事转向直接描述要做的事。它让代码更简洁也让Java具备了函数式编程的能力为后续学习Stream流式编程打下了基础。