行百里er 行百里er
首页
  • 分类
  • 标签
  • 归档
设计模式
  • JVM
  • Java基础
MySQL
Elastic Stack
Redis
  • Kafka
  • RocketMQ
分布式
Spring Cloud Alibaba
云原生
数据结构与算法
关于
GitHub (opens new window)

行百里er

Java程序员一枚
首页
  • 分类
  • 标签
  • 归档
设计模式
  • JVM
  • Java基础
MySQL
Elastic Stack
Redis
  • Kafka
  • RocketMQ
分布式
Spring Cloud Alibaba
云原生
数据结构与算法
关于
GitHub (opens new window)
  • JVM

  • Java基础

    • 【优雅的避坑】从验证码生成代码的优化到 JVM 栈和堆
    • 【优雅的避坑】HashMap正确的避免扩容带来的额外开销
    • 【优雅的避坑】不要轻易使用==比较两个Integer的值
    • 【优雅的避坑】为什么0.1+0.2不等于0.3了!?
    • 【优雅的避坑】不安全!别再共享SimpleDateFormat了
    • new Object在内存中占多少字节?
    • Java 8之Lambda表达式的写法套路
      • 引入
      • 关于Lambda表达式
      • Lambda表达式的套路
      • Lambda表达式实现JDK中一些函数式接口
        • Supplier 代表一个输出
        • BiConsumer 代表两个输入
        • UnaryOperator 代表一个输入,一个输出(输入和输出是相同类型的)
        • BiFunction 代表两个输入,一个输出(一般输入和输出是不同类型的)
        • BinaryOperator 代表两个输入,一个输出(输入和输出是相同类型的)
      • 用Lambda表达式实现自定义的函数式接口
    • Java 8 Stream API可以怎么玩?
    • Java最强大的技术之一:反射
    • 从一道面试题进入Java并发新机制---JUC
  • Java
  • Java基础
行百里er
2020-08-29
目录

Java 8之Lambda表达式的写法套路

作者:行百里er

博客:https://chendapeng.cn (opens new window)

提示

这里是 行百里er 的博客:行百里者半九十,凡事善始善终,吾将上下而求索!

# 引入

相信现在搞Java的童鞋都知道启动一个线程可以这样写:

Thread t = new Thread(() -> System.out.println("Hello"));
t.start();
1
2

Thread里面需要传的是一个Runnable接口,为什么我们可以直接一个括号 () ,加上 -> ,然后直接 sout 呢?

这得益于Java 8引入的新特性:Lambda表达式。

如果没有Lambda表达式的写法,我们需要老老实的这么写:

Thread t1 = new Thread(new Runnable() {
    @Override
    public void run() {
        System.out.println("Hello");
    }
});
t1.start();
1
2
3
4
5
6
7

Lambda表达式为我们省了多少事啊!

# 关于Lambda表达式

Lambda表达式是 Java8 中最重要的新功能之一。

使用Lambda表达式可以替代 只有一个抽象函数的接口 实现,告别匿名内部类,代码看起来更简洁易懂。

Lambda可以极大的减少代码冗余,同时代码的可读性要好过冗长的内部类,匿名类。

Lambda表达式同时还提升了对集合、框架的迭代、遍历、过滤数据的操作效率(结合 Stream API )。

Lambda表达式有以下特点:

  • 函数式编程
  • 参数类型自动推断
  • 代码量少,简洁

Lambda表达式的应用场景:任何有 函数式接口 的地方!

函数式接口

只有一个抽象方法(Object类中的方法除外)的接口是函数式接口。

那么,Lambda表达式怎么写呢?

Lambda表达式参数 -> Lambda函数体

Lambda写法.png

  • () 参数的个数,根据函数式接口里面抽象方法的参数个数来决定,当参数只有一个的时候,()可以省略

  • 当 expr 逻辑非常简单的时候, {} 和 return 可以省略

# Lambda表达式的套路

既然 Lambda 表达式可以简化实现函数式接口的方法,那么针对这些个抽象方法的 输入( 参数 )、 输出 ( 返回值 )我们来做个研究,总结一下各种方法对应的 Lambda 表达式的写法。

先看一下 Runnable :

@FunctionalInterface
public interface Runnable {
    public abstract void run();
}
1
2
3
4

这个我们都会用 Lambda 表达式:

//方法体内只有一条语句
Runnable r1 = () -> System.out.println("欢迎关注行百里er");
//处理逻辑有多条语句
Runnable r2 = () -> {
    System.out.println("你好!");
    System.out.println("欢迎关注行百里er");
};
r1.run();
r2.run();
1
2
3
4
5
6
7
8
9

再看一个Callable:

@FunctionalInterface
public interface Callable<V> {
    V call() throws Exception;
}
1
2
3
4

这个抽象方法没有参数,但是有返回值,Lambda表达式实现:

//方式1
Callable<Integer> c1 = () -> 666;
Callable<String> c2 = () -> "行百里er";
//方式2
Callable<Object> c3 = () -> {
    return new Object();
};
System.out.println(c1.call());
System.out.println(c2.call());
System.out.println(c3.call());
1
2
3
4
5
6
7
8
9
10

再看一个带参数的函数式接口:

@FunctionalInterface
public interface Consumer<T> {
    void accept(T t);

    default Consumer<T> andThen(Consumer<? super T> after) {
        Objects.requireNonNull(after);
        return (T t) -> { accept(t); after.accept(t); };
    }
}
1
2
3
4
5
6
7
8
9

它的Lambda实现:

Consumer<String> c = (str) -> System.out.println(str);
c.accept("欢迎关注行百里er");
1
2

还有,两个输入参数,一个返回值的函数式接口Function:

@FunctionalInterface
public interface Function<T, R> {
    R apply(T t);

    default <V> Function<V, R> compose(Function<? super V, ? extends T> before) {
        Objects.requireNonNull(before);
        return (V v) -> apply(before.apply(v));
    }
    
    default <V> Function<T, V> andThen(Function<? super R, ? extends V> after) {
        Objects.requireNonNull(after);
        return (T t) -> after.apply(apply(t));
    }

    static <T> Function<T, T> identity() {
        return t -> t;
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18

这种的Lambda实现如下:

//返回类型为Integer,这里代码使其返回传入字符串的长度
Function<String, Integer> f1 = (s) -> s.length();
System.out.println(f1.apply("欢迎关注行百里er"));
//返回类型为String,代码控制其直接返回传入的字符串
Function<String, String> f2 = (s) -> s;
System.out.println(f2.apply("欢迎关注行百里er"));
1
2
3
4
5
6

JDK中有很多函数式接口,我们都可以根据这些套路写出酷炫简洁的Lambda表达式。

总结一下,Lambda表达式写法的一般套路:

  • 抽象函数没有参数,也没有返回值
//处理逻辑,如果只有一条语句,可以省略大括号(下同)
() -> {xxx;}
1
2
  • 有参数,无返回值
(param) -> {xxx;}
1
  • 没有参数,有返回值
() -> {return xxx;}
1
  • 有1个参数
(param) -> {
    //处理逻辑
}
1
2
3
  • 有多个参数
(p1, p2,...) -> {
    //处理逻辑
}
1
2
3

# Lambda表达式实现JDK中一些函数式接口

# Supplier 代表一个输出

@FunctionalInterface
public interface Supplier<T> {
    T get();
}
1
2
3
4

Lambda实现:

Supplier<String> s1 = () -> "欢迎关注Java公众号:行百里er";
Supplier<String> s2 = () -> {
    return "欢迎关注Java公众号:行百里er";
};
System.out.println(s1.get());
System.out.println(s2.get());
1
2
3
4
5
6

# BiConsumer 代表两个输入

BiConsumer源码

@FunctionalInterface
public interface BiConsumer<T, U> {
    void accept(T t, U u);

    default BiConsumer<T, U> andThen(BiConsumer<? super T, ? super U> after) {
        Objects.requireNonNull(after);

        return (l, r) -> {
            accept(l, r);
            after.accept(l, r);
        };
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13

Lambda实现

BiConsumer<String, String> bc = (x, y) -> System.out.println(x + "," + y);
bc.accept("Hello", "World");
1
2

# UnaryOperator 代表一个输入,一个输出(输入和输出是相同类型的)

源码

@FunctionalInterface
public interface UnaryOperator<T> extends Function<T, T> {
    static <T> UnaryOperator<T> identity() {
        return t -> t;
    }
}
1
2
3
4
5
6

Lambda实现

UnaryOperator<String> uo = (str) -> str;
System.out.println(uo.apply("Hello,World!"));
1
2

# BiFunction 代表两个输入,一个输出(一般输入和输出是不同类型的)

BiFunction的源码

@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));
    }
}
1
2
3
4
5
6
7
8
9

Lambda实现

BiFunction<String, String, Integer> bf = (x, y) -> {
    return x.length() + y.length();
};
bf.apply("Hello", "World");
1
2
3
4

# BinaryOperator 代表两个输入,一个输出(输入和输出是相同类型的)

BinaryOperator 源码

@FunctionalInterface
public interface BinaryOperator<T> extends BiFunction<T,T,T> {
    public static <T> BinaryOperator<T> minBy(Comparator<? super T> comparator) {
        Objects.requireNonNull(comparator);
        return (a, b) -> comparator.compare(a, b) <= 0 ? a : b;
    }

    public static <T> BinaryOperator<T> maxBy(Comparator<? super T> comparator) {
        Objects.requireNonNull(comparator);
        return (a, b) -> comparator.compare(a, b) >= 0 ? a : b;
    }
}
1
2
3
4
5
6
7
8
9
10
11
12

乍一看比较懵逼,BinaryOperator 其实是二元操作符,传入的两个参数的类型和返回类型相同,继承BiFunction。

BinaryOperator<T>:两个作为输入,返回一个T作为输出,对于 “reduce” 操作很有用。

比如:

//返回一个Integer,值是输入的两个Integer只差
BinaryOperator<Integer> bo1 = (i, j) -> i - j;
System.out.println(bo1.apply(3, 8));
//返回一个String,值是输入的两个字符串的拼接结果
BinaryOperator<String> bo2 = (str1, str2) -> str1 + "," + str2;
System.out.println(bo2.apply("Hello", "World"));
1
2
3
4
5
6

# 用Lambda表达式实现自定义的函数式接口

定义一个简单的OrderDao接口,里面只有一个 int getOrderCount(Order order) 方法,所以这是一个函数式接口。

OrderDao接口

public interface OrderDao {
    int getOrderCount(Order order);
}
1
2
3

Order实体类

public class Order {
    private int count;

    public Order(int count) {
        this.count = count;
    }

    public int getCount() {
        return count;
    }

    public void setCount(int count) {
        this.count = count;
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15

我们来通过OrderDao接口获取orderCount,用Lambda很方便就能实现:

OrderDao o = (order) -> order.getCount();
System.out.println(o.getOrderCount(new Order(1800)));
1
2

提示

首发公众号 行百里er ,欢迎老铁们关注阅读指正。

#Java
上次更新: 2022/10/04, 18:14:30
new Object在内存中占多少字节?
Java 8 Stream API可以怎么玩?

← new Object在内存中占多少字节? Java 8 Stream API可以怎么玩?→

最近更新
01
重要数据不能丢!MySQL数据库定期备份保驾护航!
05-22
02
分布式事务解决方案之 Seata(二):Seata AT 模式
09-09
03
Seata 番外篇:使用 docker-compose 部署 Seata Server(TC)及 K8S 部署 Seata 高可用
09-05
更多文章>
Theme by Vdoing | Copyright © 2020-2023 行百里er | MIT License | 豫ICP备2022020385号-1
  • 跟随系统
  • 浅色模式
  • 深色模式
  • 阅读模式