携手创作,共同成长!这是我参与「掘金日新计划 · 8 月更文挑战」的第5天,点击查看活动详情
引入
相信现在搞Java的童鞋都知道启动一个线程可以这样写:
- Thread t = new Thread(() -> System.out.println("Hello"));
- t.start();
- 复制代码
Thread里面需要传的是一个Runnable接口,为什么我们可以直接一个括号 ()
,加上 ->
,然后直接 sout
呢?
这得益于Java 8引入的新特性:Lambda表达式
。
如果没有Lambda表达式的写法,我们需要老老实的这么写:
- Thread t1 = new Thread(new Runnable() {
- @Override
- public void run() {
- System.out.println("Hello");
- }
- });
- t1.start();
- 复制代码
Lambda表达式
为我们省了多少事啊!
关于Lambda表达式
Lambda表达式是 Java8 中最重要的新功能之一。
使用Lambda表达式可以替代 只有一个抽象函数的接口 实现,告别匿名内部类,代码看起来更简洁易懂。
Lambda可以极大的减少代码冗余,同时代码的可读性要好过冗长的内部类,匿名类。
Lambda表达式同时还提升了对集合、框架的迭代、遍历、过滤数据的操作效率(结合 Stream API
)。
Lambda表达式有以下特点:
- 函数式编程
- 参数类型自动推断
- 代码量少,简洁
Lambda表达式的应用场景:任何有 函数式接口 的地方!
函数式接口
只有一个抽象方法(Object类中的方法除外)的接口是函数式接口。
那么,Lambda表达式怎么写呢?
Lambda表达式参数 -> Lambda函数体
-
()
参数的个数,根据函数式接口里面抽象方法的参数个数来决定,当参数只有一个的时候,()
可以省略 -
当 expr 逻辑非常简单的时候, {} 和 return 可以省略
Lambda表达式的套路
既然 Lambda 表达式可以简化实现函数式接口的方法,那么针对这些个抽象方法的 输入
( 参数 )、 输出
( 返回值 )我们来做个研究,总结一下各种方法对应的 Lambda 表达式的写法。
先看一下 Runnable
:
- @FunctionalInterface
- public interface Runnable {
- public abstract void run();
- }
- 复制代码
这个我们都会用 Lambda 表达式:
- //方法体内只有一条语句
- Runnable r1 = () -> System.out.println("欢迎关注行百里er");
- //处理逻辑有多条语句
- Runnable r2 = () -> {
- System.out.println("你好!");
- System.out.println("欢迎关注行百里er");
- };
- r1.run();
- r2.run();
- 复制代码
再看一个Callable
:
- @FunctionalInterface
- public interface Callable<V> {
- V call() throws Exception;
- }
- 复制代码
这个抽象方法没有参数,但是有返回值,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());
- 复制代码
再看一个带参数的函数式接口:
- @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); };
- }
- }
- 复制代码
它的Lambda实现:
- Consumer<String> c = (str) -> System.out.println(str);
- c.accept("欢迎关注行百里er");
- 复制代码
还有,两个输入参数,一个返回值的函数式接口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;
- }
- }
- 复制代码
这种的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"));
- 复制代码
JDK中有很多函数式接口,我们都可以根据这些套路写出酷炫简洁的Lambda表达式。
总结一下,Lambda表达式写法的一般套路:
- 抽象函数没有参数,也没有返回值
- //处理逻辑,如果只有一条语句,可以省略大括号(下同)
- () -> {xxx;}
- 复制代码
- 有参数,无返回值
- (param) -> {xxx;}
- 复制代码
- 没有参数,有返回值
- () -> {return xxx;}
- 复制代码
- 有1个参数
- (param)-> {
- //处理逻辑
- }
- 复制代码
- 有多个参数
- (p1, p2,...)-> {
- //处理逻辑
- }
- 复制代码
Lambda表达式实现JDK中一些函数式接口
Supplier
代表一个输出
- @FunctionalInterface
- public interface Supplier<T> {
- T get();
- }
- 复制代码
Lambda
实现:
- Supplier<String>s1 = () -> "欢迎关注Java公众号:行百里er";
- Supplier<String> s2 = () -> {
- return "欢迎关注Java公众号:行百里er";
- };
- System.out.println(s1.get());
- System.out.println(s2.get());
- 复制代码
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);
- };
- }
- }
- 复制代码
Lambda实现
- BiConsumer<String, String> bc = (x, y) -> System.out.println(x +"," + y);
- bc.accept("Hello", "World");
- 复制代码
UnaryOperator
代表一个输入,一个输出(输入和输出是相同类型的)
源码
- @FunctionalInterface
- public interface UnaryOperator<T> extends Function<T, T> {
- static <T> UnaryOperator<T> identity() {
- return t -> t;
- }
- }
- 复制代码
Lambda实现
- UnaryOperator<String> uo = (str) -> str;
- System.out.println(uo.apply("Hello,World!"));
- 复制代码
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));
- }
- }
- 复制代码
Lambda实现
- BiFunction<String, String, Integer> bf = (x, y) -> {return x.length() + y.length();
- };
- bf.apply("Hello", "World");
- 复制代码
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;
- }
- }
- 复制代码
乍一看比较懵逼,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"));
- 复制代码
用Lambda表达式实现自定义的函数式接口
定义一个简单的OrderDao接口,里面只有一个 int getOrderCount(Order order)
方法,所以这是一个函数式接口。
OrderDao接口
- public interface OrderDao {
- int getOrderCount(Order order);
- }
- 复制代码
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;
- }
- }
- 复制代码
我们来通过OrderDao接口获取orderCount,用Lambda很方便就能实现:
- OrderDaoo = (order) -> order.getCount();
- System.out.println(o.getOrderCount(new Order(1800)));
- 复制代码