转载请标明出处:一片枫叶的专栏
新的Java8 API中提供了不少新的特性,其中就有Lambda表达式。而本文我们将主要介绍一下在Android开发过程中如何使用Lambda表达式,这里主要是为我们后续介绍RxAndroid、RxJava相关知识做铺垫的。
- Lambda表达式的概念
Lambda表达式是Java8中提供的一种新的特性,它支持Java也能进行简单的“函数式编程”,即Lambda允许你通过表达式来代替功能接口。其实Lambda表达式的本质只是一个”语法糖”,由编译器推断并帮你转换包装为常规的代码,因此你可以使用更少的代码来实现同样的功能。
Lambda表达式就和方法一样,它提供了一个正常的参数列表和一个使用这些参数的主体(body,可以是一个表达式或一个代码块)
咋样很厉害吧?下面我们将慢慢看一下Lambda表达式的相关知识。
- 标准的Lambda表达式写法
那么Lambda表达式具体如何编写呢?下面我们可以看一个具体的Lambda表达式实例。
(int x, int y) -> {
Log.i("TAG", "x:" + x + " y:" + y);
return x + y;
}
这是一个标准的Lambda表达式的写法,一个Lambda表达式通常有三部分组成:
参数:(int a, int b)是这个lambda expression的参数部分,包括参数类型和参数名
箭头:->
代码块:就是用”{}”包含着的那两句代码。
其中由于参数的类型是可以通过系统上下文推断出来的,所以在很多情况下,参数的类型是可以省略的,可以省略的写成:
(x, y) -> {
Log.i("TAG", "x:" + x + " y:" + y);
return x + y;
}
其实不光参数类型是可以省略的,代码块也是可以省略的,比如如果我们的代码块中只有一句代码,我们可以写成:
(x, y) ->
return x + y;
也可以写成:
(x, y) -> return x + y
而这个时候其实return关键字也是可以省略的,这时候我们就可以这样写:
(x, y) -> x + y
好精简有木有…
- Lambda表达式的几个特性
(1)类型推导
编译器负责推导Lambda表达式的类型。它利用Lambda表达式所在上下文所期待的类型进行推导, 这个被期待的类型被称为目标类型。就是说我们传入的参数可以无需写类型了!
(2)变量捕获
对于lambda表达式和内部类, 我们允许在其中捕获那些符合有效只读(Effectively final)的局部变量。简单的说,如果一个局部变量在初始化后从未被修改过,那么它就符合有效只读的要求, 换句话说,加上final后也不会导致编译错误的局部变量就是有效只读变量
(3)方法引用
如果我们想要调用的方法拥有一个名字,我们就可以通过它的名字直接调用它:
Comparator byName = Comparator.comparing(Person::getName);
此处无需再传入参数,Lambda会自动装配成Person类型进来然后执行getName()方法,而后返回getName()的String
方法引用有很多种,它们的语法如下:
静态方法引用:ClassName::methodName
实例上的实例方法引用:instanceReference::methodName
超类上的实例方法引用:super::methodName
类型上的实例方法引用:ClassName::methodName
构造方法引用:Class::new
数组构造方法引用:TypeName[]::new
- 学习使用Lambda表达式
/**
* 定义线程测试例子
*/
public static void m1() {
new Thread(new Runnable() {
@Override
public void run() {
Log.i("TAG", "定义一个简单测试例子...");
}
}).start();
}
以上是我们使用的普通的Java代码实现的一个简单的线程例子,那么如果使用Lambda表达式是什么形式的呢?
/**
* 使用Lambda表达式实现
*/
public static void m2() {
new Thread(
() -> {
Log.i("TAG", "使用Lambda表达式的例子...");
}
).start();
}
可以看到通过Lambda表达式代码还是相当简洁的
(1)我们直接在编辑器里面写Lambda表达式是会报错的,因为Lambda不可以这样使用。在关于Lambda的使用其实需要跟一个叫做函数接口(Functional Interface)的东西绑定在一起。什么是函数接口呢?函数接口是在Java8 中引入的概念,其就是为了Lambda表达式而引入的。我们知道Java中的接口的概念,而函数接口其实就是:
一个只定义了一个抽象方法的接口
比如ClickListener这个接口就只有一个onClick方法,那么它就是一个函数接口。在Android开发过程中我们经常会这样使用OnClickListener:
/**
* 定义OnClickListener,处理按钮点击事件
*/
View.OnClickListener onClickListener = newView.OnClickListener() {
@Override
public void onClick(View view) {
// 处理按钮点击事件
doSomeThing();
}
});
findViewById(R.id.titleView).setOnClickListener(onClickListener);
其实我们除了上面定义的OnClickListener我们也可以直接使用匿名内部类:
/**
* 定义匿名内部类处理组件的点击事件
*/
findViewById(R.id.titleView).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
// 处理组件的点击事件
doSomeThing();
}
});
在上面的代码中,我们可以发现其实我们主要是调用其他的doSomeThing()方法,该方法中实现了我们的按钮点击事件,并且这里我们通过一系列的缩进,括号来实现了这个调用操作,有木有觉得很繁琐?在Java 8出现之前,在代码中经常有这样的实现。现在好了,有了Lambda达表示,我们可以这样写了:
/**
* 自定义OnClickListener按钮点击事件
*/
View.OnClickListener onClickListener = view -> doSomeThing();
findViewById(R.id.titleView).setOnClickListener(onClickListener);
至于匿名内部类我们也可以这样写:
findViewById(R.id.titleView).setOnClickListener(view -> doSomeThing());
通过使用Lambda表达式之后代码就变得相当简洁了。从上面的例子可以看到,Lambda表达式简化了函数接口的实例的创建,可以在代码层次简化函数接口,内部匿名类的写法等。
- Lambda表达式的优缺点
上面我们简单的介绍了Lambda表达式的概念,写法与特性等,那么它具体有什么优缺点呢?
优点:
- 使用Lambda表达式可以极大的简化代码。去除了很多无用的Java代码,使得代码更为简洁明了;
缺点:
可读性差。在代码简洁的情况下,另一方面又让大多程序员很难读懂。因为很少程序员接触使用它。
如何在Android Studio中使用Lambda表达式
Android Studio默认使用Lambda表达式是会报错的,即使你使用的是Java 8,升级Android Studio的Language level为1.8
如果是非Java8,那么我们如何使用Lambda表达式呢?
幸好有Lambda的gradle插件gradle-retrolambda,通过这个插件我们可以在Android Studio中使用Lambda表达式了,其可以兼容到Java5。
至于具体的使用方式我们可以参考其Github地址:gradle-retrolambda
具体如:
/**
* 自定义组件点击事件
*/
imageView.setOnClickListener { view ->
Log.i("TAG", "按钮点击事件...")
}
总结:
相对来说使用Lambda表达式还是能够优化一些代码的,但是相应的代码的可能性会有相应的下降,在实际的开发过程中可根据具体的情况作出相应的选择。