Java8 是自 java5 之后最重大的一次更新, 它给 JAVA 语言带来了很多新的特性(包括编译器, 类库, 工具类, JVM 等), 其中最重要的升级是它给我们带来了 Lambda 表达式和 Stream API.
1, 什么是 Lambda 表达式?
Lambda 是一个匿名函数, 可以理解为是一段可以传递的代码, 可以将代码像传递参数, 传递数据一样进行传输. 使用 Lambda 表达式, 可以写出更加紧凑, 更加简洁, 更加灵活的代码.
2, 使用 Lambda 的限制条件
Lambda 并不是任何地方都可以使用, Lambda 表达式需要 "函数式接口" 的支持.
3, 什么是函数式接口?
接口中只有一个抽象方法的接口, 称为函数式接口, 可以用 @FunctionalInterface 修饰一下, 这里需要注意的是: 未使用 @FunctionalInterfaces 注解的接口未必就不是函数式接口, 一个接口是不是函数式接口的条件只有一条, 即接口中只有一个抽象方法的接口(Object 类中的方法不算). 而使用 @FunctionalInterface 注解修饰了的接口就一定是函数式接口, 添加 @FunctionalInterface 注解可以帮助我们检查是否是函数式接口.
JDK 中常见的函数式接口有:
- @FunctionalInterface
- public interface Runnable {
- void run();
- }
- @FunctionalInterface
- public interface Callable<V> {
- V call() throws Exception;
- }
以下接口中虽然有两个方法, 但因 hashCode()是 Object 类中的方法, 因此该接口也是函数式接口:
- @FunctionalInterface
- public interface FuncInterface {
- void doSomething();
- int hashCode(); // Object 类中的方法
- }
4,Lambda 表达式示例
需求一: 开启一个线程, 在线程中打印出 "Hello World"
未使用 Lambda 表达式时的写法:
- public class LambdaTest {
- public void print() {
- Thread thread = new Thread(new Runnable() {
- @Override
- public void run() {
- System.out.println("Hello World");
- }
- });
- thread.start();
- }14 }
使用 Lambda 时的写法:
- public class LambdaTest {
- public void print() {
- Thread thread = new Thread(() -> System.out.println("Hello World"));
- }
- }
需求二: 模拟一个计算器, 使其可以进行简单的加, 减, 乘操作
(1), 计算器操作函数式接口
- @FunctionalInterface
- public interface Calculator<T> {
- T operation(T t1,T t2);
- }
(2)具体操作
- public class CalculatorTest {
- public Integer operator(Integer v1,Integer v2,Calculator<Integer> calculator) {
- return calculator.operation(v1,v2);
- }
- public Integer add(Integer v1,Integer v2) {
- return operator(v1,v2,(x,y) -> x + y);
- }
- public Integer subtr(Integer v1,Integer v2) {
- return operator(v1,v2,(x,y) -> x - y);
- }
- public Integer multi(Integer v1,Integer v2) {
- return operator(v1,v2,(x,y) -> x * y);
- }
- public static void main(String[] args) {
- CalculatorTest calculatorTest = new CalculatorTest();
- // 加法
- Integer add = calculatorTest.add(1,2);
- // 减法
- Integer sub = calculatorTest.subtr(100,82);
- // 乘法
- Integer multi = calculatorTest.multi(5,3);
- System.out.println(add);
- System.out.println(sub);
- System.out.println(multi);
- }
- }
运行结果:
3 18 15
从这需求一中, 我们可以看出, 使用 Lambda 比使用匿名内部类代码更加简洁, 同时, 也可以理解为什么 Lambda 必须需要函数式接口的支持. 我们假设 Runnable 中有两个方法, 那么,"() -> System.out.println(Thread.currentThread().getName())" 应该去找哪个方法去实现?
从需求二的例子中, 我们可以更加理解 "一段可以传递的代码" 这句话的含义. 对数据的操作方法定义在 Calculator 接口中, 而加, 减, 乘的具体实现代码在各自的方法中, 并将这些实现作为参数传递给 CalculatorTest 类的 operator()方法, 最终返回操作结果.
5,Lambda 表达式的语法
5.1 Lambda 表达式的语法结构
(参数列表, 对应的是接口中对应的抽象方法的参数列表) -> {对抽象方法的实现}
Lambda 表达式语法分在 3 个部分:
左边的参数列表, 对应的是函数式接口中抽象方法的参数列表;
中间的符号:->, 为固定写法;
右边大括号内对函数接口抽象方法的实现.
Lambda 表达式的在具体场景下可以有简略写法.
5.2 语法格式一: 无参数, 无返回值
- Runnable runnable = () -> {System.out.println("Hello World");}
- Runnable runnable = () -> System.out.println("Hello World"); // 简写形式
此时, 如果右边的代码简单, 只有一行代码时,{}可以省略.
5.2 语法格式二: 有一个参数, 无返回值
- public class CalculatorTest {
- public void print(Consumer<String> msg) {
- System.out.println(msg);
- }
- public void doPrint(String msg) {
- print((str) -> System.out.println(msg));
- print(str -> System.out.println(msg)); // 简写
- }
- }
此时, 左边的 () 可以省略.
5.3 语法格式三: Lambda 体内只有一条语句, 且有返回值, return 可省略
- public Integer subtr(Integer v1,Integer v2) {
- return operator(v1,v2,(x,y) -> x - y);
- }
5.4 语法格式四: 有两个以上参数, 且 Lambda 体中有多条语句
- public Integer add(Integer v1,Integer v2) {
- return operator(v1,v2,(x,y) -> {
- System.out.println("进行加法运算");
- return x + y;
- });
- }
5.5 语法格式五: Lambda 表达式的数据类型可以省略不写
JVM 编译器通过上下文可以推断出数据类型, 但要注意的是, 当多个参数时, 要么都写, 要么都不写, 不能有的写, 有的不写:
- public Integer subtr(Integer v1,Integer v2) {
- return operator(v1,v2,(Integer x,y) -> x - y); // 错误
- }
- public Integer subtr(Integer v1,Integer v2) {
- return operator(v1,v2,(x,y) -> x - y); // 正确
- }
6,Java8 四大内置核心函数式接口
Consumer<T> : 消费型接口(无返回值, 有去无回)
void accept(T t);
Supplier<T> : 供给型接口
T get();
Function<T,R> : 函数型接口
R apply(T t);
Predicate<T> : 断言型接口
boolean test(T t);
四大核心接口的 -->扩展子接口
示例:
- import lombok.Getter;
- import lombok.Setter;
- import java.util.function.Consumer;
- import java.util.function.Function;
- import java.util.function.Predicate;
- import java.util.function.Supplier;
- @Getter
- @Setter
- class User {
- private String username;
- private int age;
- @Override
- public String toString() {
- return "User{" +
- "username='" + username + '\'' +
- ", age=" + age +
- '}';
- }
- }
- public class InnerInterface {
- /**
- * 打印 user 信息
- */
- public void print(User user,Consumer<User> userConsumer) {
- userConsumer.accept(user);
- }
- /**
- * 返回一个 user
- */
- public User getUser(Supplier<User> userSupplier) {
- return userSupplier.get();
- }
- /**
- * 转换一个 user
- */
- public User transformUser(User user,Function<User,User> function) {
- return function.apply(user);
- }
- /**
- * 检验 User 是否合法
- */
- public boolean checkUser(User user, Predicate<User> predicate) {
- return predicate.test(user);
- }
- public static void main(String[] args) {
- User userObj = new User();
- userObj.setUsername("西门吹雪");
- userObj.setAge(22);
- // 测试 Consumer
- InnerInterface mainInst = new InnerInterface();
- mainInst.print(userObj,user -> System.out.println(user));
- // 测试 Supplier
- final User user1 = mainInst.getUser(() -> {
- User user = new User();
- user.setUsername("叶孤城");
- user.setAge(22);
- return user;
- });
- System.out.println(user1);
- // 将西门吹雪的年龄改为 25
- final User user2 = mainInst.transformUser(userObj, (user -> {
- user.setAge(25);
- return user;
- }));
- System.out.println(user2);
- // 判断 User 是否是西门吹雪
- final boolean checkUser = mainInst.checkUser(userObj, (user -> user.getUsername().equals("西门吹雪")));
- System.out.println(checkUser);
- }
- }
运行结果:
- User{
- username='西门吹雪', age=22
- }
- User{
- username='叶孤城', age=22
- }
- User{
- username='西门吹雪', age=25
- }
- true
以上四大核心内置接口是我们日常开发中经常要用到的, 同时, 它们还有一些变种, 如:
BiConsumer,Consumer 的增强版, 接受两个参数:
- @FunctionalInterface
- public interface BiConsumer<T, U> {
- void accept(T t, U u);
- }
BiFunction 类似, Function 的增强版, 接受两个参数, 返回一个参数:
- @FunctionalInterface
- public interface BiFunction<T, U, R> {
- R apply(T t, U u);
- default <V> BiFunction<T, U, V> andThen(Function<? super R, ? extends V> after) {
- Objects.requireNonNull(after);
- return (T t, U u) -> after.apply(apply(t, u));
- }
- }
其他的类似, 这些函数式接口都在 java.util.function 包下, 读者可去这个包下去查询.
来源: https://www.cnblogs.com/wuhenzhidu/p/lambda.html