如果你是从 Java 8 之后才开始接触这门语言的那你无疑是幸运的。在现代的 Java 代码里我们经常能看到像() - System.out.println(Hello)这样清爽、优雅的代码。但在 Java 8 诞生之前的漫长岁月里Java 程序员为了实现同样的一句打印往往要写下十几行不知所云的“废话”代码。为什么会这样因为 Java 是一门极其纯粹的面向对象语言OOP。在早期的 Java 世界里有一条铁律万物皆对象。这意味着如果你想传递一段“行为一段执行逻辑”你不能直接把逻辑扔过去你必须先造一个“对象”把这段逻辑塞进对象的方法里然后把整个对象传递过去。今天我们就通过一个极简的例子重走一遍 Java 从“重度冗余”到“极简优雅”的进化之路。看看 Lambda 表达式到底是怎么把一层层的外衣“扒”掉的。零、 我们的靶子一个简单的接口假设我们现在有一个非常简单的需求定义一个接口里面只有一个方法。我们要在主函数里去实现并调用它。Java// 核心接口只定义了一个行为 public interface Printer { void print(); }为了调用这个print()Java 程序员开始了漫长的进化。第一阶段外部类原始社会的笨重在最古老的写法里接口是不能直接实例化的你必须老老实实地建一个新的.java文件写一个实现类。Java// 1. 必须单独新建一个类去实现接口 class MyPrinter implements Printer { Override public void print() { System.out.println(1. 使用外部类实现打印这太笨重了); } } public class LambdaEvolution { public static void main(String[] args) { // 2. 实例化并调用 Printer printer new MyPrinter(); printer.print(); } }痛点分析太重了我仅仅是为了打印一句话却要专门去建一个独立的类文件。如果这个打印逻辑我只用这一次以后再也不用了这个MyPrinter类就会永远像垃圾一样躺在我的包结构里严重污染命名空间。第二阶段静态内部类向内收缩为了不让包里到处都是这种“一次性”的类文件程序员想了个办法把这个类移到主类的里面作为静态内部类。Javapublic class LambdaEvolution { // 静态内部类它属于 LambdaEvolution不再污染外部的包空间 static class StaticPrinter implements Printer { Override public void print() { System.out.println(2. 使用静态内部类稍微好了一点。); } } public static void main(String[] args) { Printer printer new StaticPrinter(); printer.print(); } }痛点分析虽然包空间干净了但仔细想想这个StaticPrinter其实只有在main方法里才会被用到。把它放在类的级别甚至别的静态方法也能调用它它的作用域依然太大了。第三阶段局部内部类极致的作用域既然只在main方法里用那我就干脆把类的定义写在main方法的花括号里面就像定义一个局部变量一样。Javapublic class LambdaEvolution { public static void main(String[] args) { // 局部内部类离开这个 main 方法没人认识它 class LocalPrinter implements Printer { Override public void print() { System.out.println(3. 使用局部内部类作用域够小了但名字很多余。); } } Printer printer new LocalPrinter(); printer.print(); } }痛点分析逻辑上已经很严密了。但是仔细看这段代码你不觉得很滑稽吗 我们给这个类起了个名字叫LocalPrinter但紧接着就在下一行new LocalPrinter()将它实例化了。起这个名字仅仅是为了在下一行使用一次既然只用一次我为什么要费尽心思给它起名字第四阶段匿名内部类Java 8 之前的巅峰Java 官方受不了了推出了匿名内部类。它允许你在实例化的同时直接就地写出类的实现连名字都省了。Javapublic class LambdaEvolution { public static void main(String[] args) { // 匿名内部类没有类名直接 new 接口并实现 Printer printer new Printer() { Override public void print() { System.out.println(4. 匿名内部类以前的 Android 源码里全是这种代码。); } }; printer.print(); } }痛点分析这是 Java 8 之前的标准写法我们在老代码的点击事件监听OnClickListener、多线程Runnable里见得太多了。 但请你盯着这段代码看 5 秒钟。真正有价值的代码是哪一句 只有中间的System.out.println(...) 外面的new Printer()、public void print()全都是为了迎合 Java 语法而写的“八股文废话”。代码的“信噪比”低得令人发指。第五阶段Lambda 表达式函数式编程的觉醒随着 Java 8 的发布设计师们终于迎来了顿悟既然Printer接口里只有一个方法当我把它赋值给Printer类型的变量时编译器难道猜不出我要实现的是哪一个方法吗既然编译器能猜出来我们为什么还要把接口名、方法名再抄一遍扒掉把所有废话都扒掉只留下最核心的参数列表和方法体于是Lambda 表达式诞生了Javapublic class LambdaEvolution { public static void main(String[] args) { // Lambda 表达式极简主义的巅峰 // () 代表 print() 方法的参数 // - 是一个指向符指引下一步动作 Printer printer () - { System.out.println(5. Lambda终于摆脱了繁文缛节); }; // 还没完如果方法体只有一行代码连花括号都能脱掉 Printer ultimatePrinter () - System.out.println(究极进化一行代码秒杀全场); ultimatePrinter.print(); } } 终局思考这只是一颗语法糖吗很多初学者觉得Lambda 表达式仅仅是一个“能少写几行代码”的语法糖。其实不然。从“外部类”到“匿名内部类”前四个阶段虽然一直在精简但它们的核心思想依然是经典的 OOP“我要传递一个包含这段逻辑的 对象”。而 Lambda 表达式的出现标志着 Java 引入了函数式编程Functional Programming的思想。它的本质是“我不再传递对象了我直接传递 这段行为逻辑本身”。正是为了支撑这种思想的转变Java 引入了FunctionalInterface函数式接口的概念。只要一个接口只有一个抽象方法它就是一张可以签发 Lambda 的通行证。从繁杂的类定义退化成一行极简的箭头符号。这不是语法的退化而是思想的升华。下次当你在流式计算Stream API里敲下-时不妨回味一下为了这一行代码的清爽Java 走过了怎样漫长的扒皮之路。
讲明白Lambda 表达式的进化史
如果你是从 Java 8 之后才开始接触这门语言的那你无疑是幸运的。在现代的 Java 代码里我们经常能看到像() - System.out.println(Hello)这样清爽、优雅的代码。但在 Java 8 诞生之前的漫长岁月里Java 程序员为了实现同样的一句打印往往要写下十几行不知所云的“废话”代码。为什么会这样因为 Java 是一门极其纯粹的面向对象语言OOP。在早期的 Java 世界里有一条铁律万物皆对象。这意味着如果你想传递一段“行为一段执行逻辑”你不能直接把逻辑扔过去你必须先造一个“对象”把这段逻辑塞进对象的方法里然后把整个对象传递过去。今天我们就通过一个极简的例子重走一遍 Java 从“重度冗余”到“极简优雅”的进化之路。看看 Lambda 表达式到底是怎么把一层层的外衣“扒”掉的。零、 我们的靶子一个简单的接口假设我们现在有一个非常简单的需求定义一个接口里面只有一个方法。我们要在主函数里去实现并调用它。Java// 核心接口只定义了一个行为 public interface Printer { void print(); }为了调用这个print()Java 程序员开始了漫长的进化。第一阶段外部类原始社会的笨重在最古老的写法里接口是不能直接实例化的你必须老老实实地建一个新的.java文件写一个实现类。Java// 1. 必须单独新建一个类去实现接口 class MyPrinter implements Printer { Override public void print() { System.out.println(1. 使用外部类实现打印这太笨重了); } } public class LambdaEvolution { public static void main(String[] args) { // 2. 实例化并调用 Printer printer new MyPrinter(); printer.print(); } }痛点分析太重了我仅仅是为了打印一句话却要专门去建一个独立的类文件。如果这个打印逻辑我只用这一次以后再也不用了这个MyPrinter类就会永远像垃圾一样躺在我的包结构里严重污染命名空间。第二阶段静态内部类向内收缩为了不让包里到处都是这种“一次性”的类文件程序员想了个办法把这个类移到主类的里面作为静态内部类。Javapublic class LambdaEvolution { // 静态内部类它属于 LambdaEvolution不再污染外部的包空间 static class StaticPrinter implements Printer { Override public void print() { System.out.println(2. 使用静态内部类稍微好了一点。); } } public static void main(String[] args) { Printer printer new StaticPrinter(); printer.print(); } }痛点分析虽然包空间干净了但仔细想想这个StaticPrinter其实只有在main方法里才会被用到。把它放在类的级别甚至别的静态方法也能调用它它的作用域依然太大了。第三阶段局部内部类极致的作用域既然只在main方法里用那我就干脆把类的定义写在main方法的花括号里面就像定义一个局部变量一样。Javapublic class LambdaEvolution { public static void main(String[] args) { // 局部内部类离开这个 main 方法没人认识它 class LocalPrinter implements Printer { Override public void print() { System.out.println(3. 使用局部内部类作用域够小了但名字很多余。); } } Printer printer new LocalPrinter(); printer.print(); } }痛点分析逻辑上已经很严密了。但是仔细看这段代码你不觉得很滑稽吗 我们给这个类起了个名字叫LocalPrinter但紧接着就在下一行new LocalPrinter()将它实例化了。起这个名字仅仅是为了在下一行使用一次既然只用一次我为什么要费尽心思给它起名字第四阶段匿名内部类Java 8 之前的巅峰Java 官方受不了了推出了匿名内部类。它允许你在实例化的同时直接就地写出类的实现连名字都省了。Javapublic class LambdaEvolution { public static void main(String[] args) { // 匿名内部类没有类名直接 new 接口并实现 Printer printer new Printer() { Override public void print() { System.out.println(4. 匿名内部类以前的 Android 源码里全是这种代码。); } }; printer.print(); } }痛点分析这是 Java 8 之前的标准写法我们在老代码的点击事件监听OnClickListener、多线程Runnable里见得太多了。 但请你盯着这段代码看 5 秒钟。真正有价值的代码是哪一句 只有中间的System.out.println(...) 外面的new Printer()、public void print()全都是为了迎合 Java 语法而写的“八股文废话”。代码的“信噪比”低得令人发指。第五阶段Lambda 表达式函数式编程的觉醒随着 Java 8 的发布设计师们终于迎来了顿悟既然Printer接口里只有一个方法当我把它赋值给Printer类型的变量时编译器难道猜不出我要实现的是哪一个方法吗既然编译器能猜出来我们为什么还要把接口名、方法名再抄一遍扒掉把所有废话都扒掉只留下最核心的参数列表和方法体于是Lambda 表达式诞生了Javapublic class LambdaEvolution { public static void main(String[] args) { // Lambda 表达式极简主义的巅峰 // () 代表 print() 方法的参数 // - 是一个指向符指引下一步动作 Printer printer () - { System.out.println(5. Lambda终于摆脱了繁文缛节); }; // 还没完如果方法体只有一行代码连花括号都能脱掉 Printer ultimatePrinter () - System.out.println(究极进化一行代码秒杀全场); ultimatePrinter.print(); } } 终局思考这只是一颗语法糖吗很多初学者觉得Lambda 表达式仅仅是一个“能少写几行代码”的语法糖。其实不然。从“外部类”到“匿名内部类”前四个阶段虽然一直在精简但它们的核心思想依然是经典的 OOP“我要传递一个包含这段逻辑的 对象”。而 Lambda 表达式的出现标志着 Java 引入了函数式编程Functional Programming的思想。它的本质是“我不再传递对象了我直接传递 这段行为逻辑本身”。正是为了支撑这种思想的转变Java 引入了FunctionalInterface函数式接口的概念。只要一个接口只有一个抽象方法它就是一张可以签发 Lambda 的通行证。从繁杂的类定义退化成一行极简的箭头符号。这不是语法的退化而是思想的升华。下次当你在流式计算Stream API里敲下-时不妨回味一下为了这一行代码的清爽Java 走过了怎样漫长的扒皮之路。