对Lamda表达式的简单理解
第一次见Lamda表达式
第一次看到这行代码的时候,我是百思不得姐:
1 | Toolkit.let(mUserTabFragment, f -> f.setUserVisibleHint(true)); |
点击进入let函数看到其定义如下:
1 | public static <T> void let( { T input, Consumer<T> consumer) |
可以看出let的第二个参数是一个Consumer,但是上面的f->f.setUserVisibleHint(isVisibleToUser));是什么鬼,是Consumer吗?答案是 是的,这样看就明白了,首先,我们创建一个Consumer对象:
1 | Consumer<Fragment> consumer = new Consumer<Fragment>() { |
上面代码的Lamda形式为:
1 | Consumer<Fragment> consumer = (f)-> f.setUserVisibleHint(true)); |
所以 可以直接将等号右边带入let函数的第二个参数,就变成了那个样子:
1 | Toolkit.let(mUserTabFragment, f -> f.setUserVisibleHint(true)); |
现在应该就明白了第二个参数到底是啥了,其实就是一种Lamda表达式。
函数式接口(Functional Interface)
那么到底什么是Lamda表达式呢?在使用Java的Lamda表达式之前,有一个重要的概念,叫函数式接口:
函数式接口(Functional Interface)就是有且仅有一个方法的接口
例如我们平时经常用到的Callable, Runnable都是函数式接口,在这里的Consumer也不例外,Consumer也是java1.8版本新增的函数式接口,与Runnable和Callable一样。假如你想定义一个回调,这个时候你又不像去专门定义一个回调接口On,就可以用Runnable或Callable等这样的接口实现:
1 | class A{ |
可以看出Lamda表达式就是对函数式接口的一种省略的写法,上面的例子中,你可以用Lamda表达式写成一行,减小代码量,也可以不用Lamda表达式,写成6行,它们的运行结果都是一样的。
讲到这可能有的同学又有点蒙了,我Lamda表达式还没搞明白,又给我讲一堆函数式接口干嘛?别急,Lamda表达式的写法也有一定的规则,怎么写Lamda表达式跟这些函数式接口有紧密的关系,那么我们先说明下Consumer、Runnable、Callable这些接口是干什么用的呢?他们的区别是什么?
不同函数式接口的区别就是参数的个数和有无返回值而已,你可以根据需要,选择使用哪个接口。
- 例如上述例子,我只是需要类B在某个时刻通知A执行一个操作,不需要返回值,也不带参数,那就用Runnable就行,意味着这段代码注重的是运行;
- 如果不要参数,但是需要返回值,就用Callable,其名字也意味这点;
- 相反如果需要一个参数,但是不需要返回值,就可以用Consumer,其名字也是一样,消费,给你一个参数,你拿去用就好,不用返回值给我;
- 同样的接口还有很多:BiFunction, Function0, Function1,…,Function22。Funtion22就是有22个参数和一个返回值(Function的意思就是即有参数又有返回值)
列几个函数式接口给大家感受下:
1 |
|
1 |
|
Lamda表达式的语法规则
说完函数式接口,我们再回来说Lamda表达式的语法格式,Lamda表达式语法主要分为参数和表达式两部分:
(parameters) ->{ expression; }
其中,如果表达式只有一行,则可以省略花括号和分号;如果参数只有一个,也可以省略小括号,但是如果参数是空的,记住不能省略小括号。
例如上面的那几种函数式接口的写法就是:
1 | // 1. 没有餐素,也不返回任何值,类似于Runnable的接口的Lamda表达式形式: |
以上就是对Java中Lamda表达式的基本知识点的理解。
关于 @FunctionalInterface 注解
Java 8为函数式接口引入了一个新注解@FunctionalInterface,主要用于编译级错误检查,加上该注解,当你写的接口不符合函数式接口定义的时候,编译器会报错。
正确例子,没有报错:
1 | @FunctionalInterface |
错误例子,接口中包含了两个抽象方法,违反了函数式接口的定义,Eclipse 报错提示其不是函数式接口。
提醒:加不加 @FunctionalInterface 对于接口是不是函数式接口没有影响,该注解只是提醒编译器去检查该接口是否仅包含一个抽象方法
函数式接口里允许定义默认方法
函数式接口里是可以包含默认方法,因为默认方法不是抽象方法,其有一个默认实现,所以是符合函数式接口的定义的;
如下代码不会报错:
1 |
|
函数式接口里允许定义静态方法
函数式接口里是可以包含静态方法,因为静态方法不能是抽象方法,是一个已经实现了的方法,所以是符合函数式接口的定义的;
如下代码不会报错:
1 |
|
函数式接口里允许定义 java.lang.Object 里的 public 方法
函数式接口里是可以包含Object里的public方法,这些方法对于函数式接口来说,不被当成是抽象方法(虽然它们是抽象方法);因为任何一个函数式接口的实现,默认都继承了 Object 类,包含了来自 java.lang.Object 里对这些抽象方法的实现;
如下代码不会报错:
1 |
|