本文纲要
- Lambda表达式初体验与函数式编程思想
- Lambda表达式的标准格式和使用前提
- Lambda表达式练习:带参数无返回值
- Lambda表达式练习:无参数有返回值
- Lambda表达式练习:带参数带返回值
- Lambda表达式的省略模式
- 匿名内部类与Lambda表达式的区别
项目代码结构
text
src/
└── com/
└── wb/
├── test1/
│ └── TestSwimming.java
├── test2/
│ └── TestLambda.java
├── test3/
│ └── StringHandlerDemo.java
├── test4/
│ └── RandomNumHandlerDemo.java
├── test5/
│ └── CalculatorDemo.java
├── test6/
│ └── Test6.java
└── test7/
└── Test.java
Lambda表达式初体验与函数式编程思想
1 ) 从匿名内部类到Lambda
以前我们调用一个接口方法时,通常需要传入一个匿名内部类对象。例如,有一个游泳接口 Swimming,里面有一个 swim() 方法,我们这样调用:
java
goSwimming(new Swimming() {
@Override
public void swim() {
System.out.println("铁汁, 我们去游泳吧");
}
});
采用 Lambda表达式 后,可以简化为:
java
goSwimming(() -> System.out.println("铁汁, 我们去游泳吧"));
运行结果完全一致。这说明 Lambda表达式可以理解为对匿名内部类的简化(本质上两者有区别,后文会说明)。
2 ) 函数式编程思想
通过对比可以发现,匿名内部类的写法里,我们不仅要关注"做什么"(打印一句话),还要关注"怎么做"(创建匿名内部类对象、重写方法)。而Lambda表达式让我们把注意力完全集中在要做什么上面,至于怎么创建对象、怎么重写方法这些语法细节,都由编译器在背后处理了。
这种更关注做什么,而不是怎么做的编程思想,就是函数式编程思想(Functional Programming)。Lambda表达式正是这种思想在Java中的体现。
Lambda表达式的标准格式和使用前提
1 ) 标准格式
Lambda表达式由三部分组成:(形式参数) -> { 代码块 }
- 形式参数:如果有多个参数,用逗号分隔;如果没有参数,留空即可
- 箭头 ->:由英文横线和大于号组成,表示将参数传递给后面的代码块
- 代码块:具体要执行的内容,相当于方法体
2 ) 使用前提
Lambda表达式并不是任何地方都能用,必须满足两个条件:
- 操作的必须是一个接口。
- 接口中有且仅有一个抽象方法。
这种接口也被称为函数式接口。
3 ) 示例:无参数无返回值
接口定义(ShowHandler):
java
interface ShowHandler {
void show();
}
测试类:
java
public class TestLambda {
public static void main(String[] args) {
// 匿名内部类方式
useShowHandler(new ShowHandler() {
@Override
public void show() {
System.out.println("我是匿名内部类中的show方法");
}
});
// Lambda方式
useShowHandler(() -> System.out.println("我是Lambda中的show方法"));
}
public static void useShowHandler(ShowHandler showHandler) {
showHandler.show();
}
}
分析:接口中只有一个 show() 方法,无参数,无返回值。
Lambda表达式 () -> System.out.println(...) 中,括号内无参数,箭头后直接写要执行的事情。
Lambda表达式练习:带参数无返回值
当接口方法带有参数时,Lambda表达式也需要在括号内声明参数。
接口定义(StringHandler):
java
interface StringHandler {
void printMessage(String msg);
}
使用示例:
java
public class StringHandlerDemo {
public static void main(String[] args) {
// 匿名内部类
useStringHandler(new StringHandler() {
@Override
public void printMessage(String msg) {
System.out.println("我是匿名内部类" + msg);
}
});
// Lambda
useStringHandler(msg -> System.out.println("我是Lambda表达式" + msg));
}
public static void useStringHandler(StringHandler stringHandler) {
stringHandler.printMessage("itheima");
}
}
要点:方法 printMessage 有一个参数,所以Lambda的括号内需要写上参数名(参数类型可以省略,后面会讲)。如果只有一个参数,括号也可以省略,如 msg -> ...。
Lambda表达式练习:无参数有返回值
如果接口方法有返回值,Lambda表达式必须使用 return 语句返回结果。
接口定义(RandomNumHandler):
java
interface RandomNumHandler {
int getNumber();
}
使用示例:
java
import java.util.Random;
public class RandomNumHandlerDemo {
public static void main(String[] args) {
// 匿名内部类
useRandomNumHandler(new RandomNumHandler() {
@Override
public int getNumber() {
Random r = new Random();
int num = r.nextInt(10) + 1;
return num;
}
});
// Lambda
useRandomNumHandler(() -> {
Random r = new Random();
int num = r.nextInt(10) + 1;
return num;
// 注意: 如果lambda所操作的接口中的方法有返回值, 一定要通过return语句将结果返回
// 否则会出现编译错误
});
}
public static void useRandomNumHandler(RandomNumHandler randomNumHandler) {
int result = randomNumHandler.getNumber();
System.out.println(result);
}
}
注意:无参数但需要返回值时,括号不能省略;方法体中的代码块如果有多条语句,大括号也不能省略。
Lambda表达式练习:带参数带返回值
这是最完整的形态:既有参数,又有返回值。
接口定义(Calculator):
java
interface Calculator {
int calc(int a, int b);
}
使用示例:
java
public class CalculatorDemo {
public static void main(String[] args) {
// 匿名内部类
useCalculator(new Calculator() {
@Override
public int calc(int a, int b) {
return a + b;
}
});
// Lambda
useCalculator((a, b) -> a + b);
}
public static void useCalculator(Calculator calculator) {
int result = calculator.calc(10, 20);
System.out.println(result);
}
}
说明:参数 a 和 b 的类型可以省略,方法体只有一条返回语句时,可以省略大括号和 return,直接写表达式。
Lambda表达式的省略模式
Lambda表达式提供了一些简化写法,让代码更简洁。
1 ) 省略规则
- 参数类型可以省略,但如果有多个参数,不能只省略一个(要么都省略,要么都保留)。
- 如果参数只有一个,小括号可以省略。
- 如果代码块只有一条语句,可以省略大括号、分号和 return(这三个必须一起省略)。
2 ) 示例:针对省略规则的综合练习
原始接口:
java
interface Inter {
double method(double a, double b);
}
完整Lambda:
java
useInter((double a, double b) -> {
return a + b;
});
应用省略规则后:
java
useInter((a, b) -> a + b);
完整调用代码:
java
public class Test6 {
public static void main(String[] args) {
useInter((a, b) -> a + b);
}
public static void useInter(Inter i) {
double result = i.method(12.3, 22.3);
System.out.println(result);
}
}
3 ) 对其他练习的省略优化
test2(无参无返回值):
java
useShowHandler(() -> System.out.println("我是Lambda中的show方法"));
因为无参,括号不能省;只有一条语句,可以省略大括号和分号。
test3(一个参数无返回值):
java
useStringHandler(msg -> System.out.println("我是Lambda表达式" + msg));
参数类型省略,且只有一个参数,小括号省略;一条语句,省略大括号和分号。
test4(对应上述无参有返回值,方法体多条语句):
无法省略大括号,因为方法体有两条语句,必须保留 {} 和 return。
test5(有参有返回值,方法体一条语句):
java
useCalculator((a, b) -> a + b);
省略了参数类型、大括号和 return。
匿名内部类与Lambda表达式的区别
| 区别点 | 匿名内部类 | Lambda表达式 |
|---|---|---|
| 所属类型 | 可以是接口、抽象类,甚至具体类 | 只能操作接口 |
| 使用限制 | 接口中抽象方法数量不限 | 接口中必须有且仅有一个抽象方法 |
| 实现原理 | 编译后生成独立的 .class 文件(如 Test$1.class) | 编译时不生成单独的 .class 文件,运行时动态生成字节码 |
1 ) 验证编译结果
接口:
java
interface Inter {
void show();
}
测试类:
java
public class Test {
public static void main(String[] args) {
// 匿名内部类
useInter(new Inter() {
@Override
public void show() {
System.out.println("匿名内部类的show方法");
}
});
}
public static void useInter(Inter i) {
i.show();
}
}
编译后,在 out 目录下可以看到:
Inter.class(接口字节码)Test.class(测试类字节码)Test$1.class(匿名内部类生成的字节码)
如果使用Lambda表达式,编译后不会生成 $1.class,对应的字节码会在运行时动态生成,不保留在硬盘上。
结语
Lambda表达式是 Java 8 引入的重要特性,它让代码更简洁,同时将编程思维从"怎么做"引导到"做什么"。
掌握Lambda的格式、使用前提、省略规则以及与匿名内部类的区别,是Java进阶的必经之路。希望本文能帮助你快速入门Lambda表达式。