Lambda编程

参考链接

《知乎:Lambda 表达式》
《Kotlin实战》
《Kotlin中文文档》

Lambda表达式本质上就是可以传递给其他函数一小段代码。

有了Lambda可以轻松地把通用代码结构抽取成函数库,Kotlin标准库大量使用了它。

什么是Lambda

我们知道,对于一个Java变量,我们可以赋给其一个“值”。如果你想把“一块代码”赋给一个Java变量,应该怎么做呢?

在Java 8之前,这个是做不到的。但是Java 8问世之后,利用Lambda特性,就可以做到了。

1
2
3
aBlockOfCode = public void doSomeShit(String s){
System.out.println(s);
}

简便写法

1、public和函数名是多余的,因为已经赋值给了aBlockOfCode.
2、编译器可以自动判断参数类型和返回类型。
3、只有一行可以省略大括号。

1
aBlockOfCode = (s) -> System.out.println(s);

这样我们就将一个代码块成功赋值给了变量,那么aBlockOfCode的类型是什么呢?

在Java8中,所有Lambda类型都是一个接口,而等号右边则是接口的实现。

一个接口函数需要被实现的接口类型,我们叫它“函数式接口”,为了避免后来的人在这个接口中增加接口函数导致其有多个接口函数需要被实现,变成”非函数接口”,我们可以在这个上面加上一个声明@FunctionalInterface, 这样别人就无法在里面添加新的接口函数了

1
2
3
4
@FunctionalInterface
interface MyLambdaInterface{
void doSomeShit(String s);
}

这样,我们就得到了一个完整的Lambda表达式声明:

1
MyLambdaInterface aBlockOfCode = (s) -> System.out.println(s);

Lambda表达式有什么作用

使代码变的简洁

Java7实现过程:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public static void enact(MyLambdaInterface myLambda, String s){
myLambda.doSomeShit(s);
}

public class MyInterfaceImpl implements MyLambdaInterface{

@Override
public void doSomeShit(String s){
System.out.println(s);
}
}

MyLambdaInterface anInterfaceImpl = new MyInterfaceImpl();

enact(anInterfaceImpl, "Hello World");

Java8实现过程:

1
enact(s -> System.out.println(s), "Hello World");

有些情况下,这个接口实现只需要用到一次。传统的Java 7必须要求你定义一个“污染环境”的接口实现MyInterfaceImpl,而相较之下Java 8的Lambda, 就显得干净很多。

另外

在kotlin中可以像Java8中那样使用Lambda.

1
button.setOnclickListener{  /*点击后处理*/ }

Lambda和集合

需求:找出列表中年龄最大的那个人

1
2
3
4
5
6
7
8
9
10
11
12
13
data class Person(val name: String, val age: Int)

fun findTheOldest(people: List<Person>){
var maxAge = 0;
var theOldest: Person? = null
for (person in people){
if(person.age > maxAge){
maxAge = person.age
theOldest = person
}
}
println(theOldest)
}

Lambda实现

1
2
3
data class Person(val name: String, val age: Int)

people.maxBy({ it.age })

maxBy函数可以在任何集合上调用,而且只有一个实参(一个函数)指定比较那个值来找到最大元素。

花括号中的{it.age}就是实现了这个逻辑的Lambda.

Kotlin中Lambda的语法

1
{x: Int, y: Int -> x + y}

Kotlin的lambda表达式始终使用花括号包围,箭头将实参和函数体隔开。

1
2
3
4
5
6
7
8
9
10
val sum = {x: Int, y: Int -> x+y}
println(sum(1, 2))

//上下对比

interface LambdaSumInterface{
int sum(int x, int y);
}
LambdaSumInterface sumInterface = (x, y) -> x+y;
System.out.println(suminterface.sum(1, 2))

上面我们有一个需求:找出列表中年龄最大的那个人,简化过程如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
people.maxBy({p: Person -> p.age})
|
//kotlin约定:lambda表达式调用的最后一个实参可以放到括号外面
|
people.maxBy(){p: Person -> p.age}
|
//kotlin约定:函数只有唯一实参时可以去掉括号
|
people.maxBy{p: Person -> p.age}
|
//参数类型可以省略
|
people.maxBy{p -> p.age}
|
//只有一个参数,默认参数名称it代替命名参数
|
people.maxBy{it.age}

Lambda结合forEach和函数接口包

需求:遍历集合,找出名字中LastName以”Z”开头的FirstName

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
public class MainActivity extends AppCompatActivity {

class Person{
private String firstName;
private String lastName;
private int age;
public Person(String fname, String lname, int age){
firstName = fname;
lastName = lname;
this.age = age;
}

public String getFirstName() {
return firstName;
}

public String getLastName() {
return lastName;
}

public int getAge() {
return age;
}
}

@SuppressLint("NewApi")
public static void checkAndExecute(List<Person> personList,
Predicate<Person> predicate,
Consumer<Person> consumer){
for(Person p : personList){
if(predicate.test(p)){
consumer.accept(p);
}
}
}

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);

List<Person> guiltyPersons = Arrays.asList(
new Person("YiXing", "Zhao", 25),
new Person("XiaoHong", "Li", 23),
new Person("DaLi", "Wang", 22)
);
checkAndExecute(guiltyPersons,
p -> p.getLastName().startsWith("Z"),
d -> System.out.println(d.getFirstName()));
}
}

在Java8中有一个函数式接口包,里面定义了大量可能用到的函数式接口 java.util.function

使用iterable自带的forEach来替代。

1
2
3
4
5
6
@SuppressLint("NewApi")
public static void checkAndExecute(List<Person> personList,
Predicate<Person> predicate,
Consumer<Person> consumer){
personList.forEach(p -> {if(predicate.test(p)) consumer.accept(p);});
}

利用stream()替代静态函数

1
2
3
4
5
6
7
8
9
List<Person> guiltyPersons = Arrays.asList(
new Person("YiXing", "Zhao", 25),
new Person("XiaoHong", "Li", 23),
new Person("DaLi", "Wang", 22)
);

guiltyPersons.stream()
.filter(p -> p.getLastName().startsWith("Z"))
.forEach(p -> System.out.println(p.getFirstName()));

kotlin作用域中访问变量

和Java中不同的是,在Kotlin中不仅仅可以访问非final变量,也可以修改这些变量。

1
2
3
4
5
6
7
8
9
10
11
fun testFunction(messages: Collections<String>, prefix: String){
var clientErrNum = 0
var serverErrNum = 0
message.forEach{
if(it.startWith("4")){
clientErrNum++
}else if(it.startWith("5")){
serverErrNum++
}
}
}

成员引用

1
val getAge = Person::age

这种表达式称为成员引用,可以引用一个属性或者一个方法,注意方法不能带括号。

等价于

1
val getAge = {p: Person -> p.age}

我们知道Lambda可以将代码块作为参数传递给函数,如果要传递一个被定义的函数可以使用引用。

还可以引用顶层函数

1
2
3
fun salute() = println("Salute!")

>>> run(::salute)

也可以用构造方法引用

1
2
3
4
5
data class Person(val name: String, val age: Int)

val createPerson = ::Person
val p = createPerson("Alice", 26)
println(p)

还可以用同样的方式引用扩展函数

1
2
fun Person.isAdult() = age >= 21
val predicate = Person::isAdult

评论

Ajax Android AndroidStudio Animation Anroid Studio AppBarLayout Banner Buffer Bulma ByteBuffer C++ C11 C89 C99 CDN CMYK COM1 COM2 CSS Camera Raw, 直方图 Chrome ContentProvider CoordinatorLayout C语言 DML DOM Dagger Dagger2 Darktable Demo Document DownloadManage Element Error Exception Extensions File FileProvider Fresco GCC Git GitHub GitLab Gradle Groovy HTML5 Handler HandlerThread Hexo Hybrid I/O IDEA IO ImageMagick IntelliJ Intellij Interpolator JCenter JNI JS Java JavaScript JsBridge Kotlin Lab Lambda Lifecycle Lint Linux Looper MQTT MVC MVP Maven MessageQueue Modbus Momentum MySQL NDK NIO NexT Next Nodejs ObjectAnimator Oracle VM Permission PhotoShop Physics Python RGB RS-232 RTU Remote-SSH Retrofit Runnable RxAndroid RxJava SE0 SSH Spring SpringBoot Statubar Task Theme Thread Tkinter UI UIKit UML VM virtualBox VS Code ValueAnimator ViewPropertyAnimator Web Web前端 Workbench api apk bookmark by关键字 compileOnly css c语言 databases demo hexo hotfix html iOS icarus implementation init jQuery javascript launchModel logo merge mvp offset photos pug query rxjava2 scss servlet shell svg tkinter tomcat transition unicode utf-8 vector virtual box vscode 七牛 下载 中介者模式 串口 临潼石榴 主题 书签 事件 享元模式 仓库 代理模式 位运算 依赖注入 修改,tables 光和色 内存 内核 内部分享 函数 函数式编程 分支 分析 创建 删除 动画 单例模式 压缩图片 发布 可空性 合并 同向性 后期 启动模式 命令 命令模式 响应式 响应式编程 图层 图床 图片压缩 图片处理 图片轮播 地球 域名 基础 增加 备忘录模式 外观模式 多线程 大爆炸 天气APP 太白山 头文件 奇点 字符串 字符集 存储引擎 宇宙 宏定义 实践 属性 属性动画 岐山擀面皮 岐山肉臊子 岐山香醋 工具 工厂模式 年终总结 开发技巧 异常 弱引用 恒星 打包 技巧 指针 插件 摄影 操作系统 攻略 故事 数据库 数据类型 数组 文件 新功能 旅行 旋转木马 时序图 时空 时间简史 曲线 杂谈 权限 枚举 架构 查询 标准库 标签选择器 样式 核心 框架 案例 桥接模式 检测工具 模块化 模板引擎 模板方法模式 油泼辣子 泛型 洛川苹果 浅色状态栏 源码 瀑布流 热修复 版本 版本控制 状态栏 状态模式 生活 留言板 相册 相对论 眉县猕猴桃 知识点 码云 磁盘 科学 笔记 策略模式 类图 系统,发行版, GNU 索引 组件 组合模式 结构 结构体 编码 网易云信 网格布局 网站广播 网站通知 网络 美化 联合 膨胀的宇宙 自定义 自定义View 自定义插件 蒙版 虚拟 虚拟机 补码 补齐 表单 表达式 装饰模式 西安 观察者模式 规范 视图 视频 解耦器模式 设计 设计原则 设计模式 访问者模式 语法 责任链模式 贪吃蛇 转换 软件工程 软引用 运算符 迭代子模式 适配器模式 选择器 通信 通道 配置 链表 锐化 错误 键盘 闭包 降噪 陕西地方特产 面向对象 项目优化 项目构建 黑洞
Your browser is out-of-date!

Update your browser to view this website correctly. Update my browser now

×