Kotlin难点

打印 上一主题 下一主题

主题 536|帖子 536|积分 1610

目录

高阶函数

高阶函数是将函数用作参数或返回值的函数,还可以把函数赋值给一个变量。
所有函数类型都有一个圆括号括起来的参数类型列表以及一个返回类型:(A, B) -> C 表示接受类型分别为 A 与 B 两个参数并返回一个 C 类型值的函数类型。 参数类型列表可以为空,如 () -> A,Unit 返回类型不可省略。
(Int) -> String
函数类型表示法可以选择性地包含函数的参数名:(x: Int, y: Int) -> Point。 这些名称可用于表明参数的含义。
(Button, ClickEvent) -> Unit
如需将函数类型指定为可空,请使用圆括号:((Int, Int) -> Int)?
  1.     fun a(funParam: (Int) -> String): String {
  2.         return funParam(1)
  3.     }
  4.     fun b(param: Int): String {
  5.         return param.toString()
  6.     }
复制代码
调用
  1. a(::b)
  2. var d = ::b
  3. b(1) // 调用函数
  4. d(1) // 实际上会调用 d.invoke(1)
  5. (::b)(1) // 用对象 :: b 后面加上括号来实现 b() 的等价操作, 实际上会调用 (::b).invoke(1)
  6. b.invoke(1) // 报错
复制代码
对象是不能加个括号来调用的,但是函数类型的对象可以。为什么?因为这其实是个假的调用,它是 Kotlin 的语法糖,实际上你对一个函数类型的对象加括号、加参数,它真正调用的是这个对象的 invoke() 函数
双冒号

:: 创建一个函数引用或者一个类引用
函数引用
  1. fun isOdd(x: Int) = x % 2 != 0
复制代码
我们可以很容易地直接调用它(isOdd(5)),但是我们也可以将其作为一个函数类型的值,例如将其传给另一个函数。为此,我们使用 :: 操作符:
  1. val numbers = listOf(1, 2, 3)
  2. println(numbers.filter(::isOdd))
复制代码
这里 ::isOdd 是函数类型 (Int) -> Boolean 的一个值。
如果我们需要使用类的成员函数或扩展函数,它需要是限定的,例如 String::toCharArray。
  1.     val args: Array<String> = arrayOf("1", "2")
  2.   args.filter(String::isNotEmpty)
  3.   
  4.   class PdfPrinter {
  5.         fun println(any: Any) {
  6.             kotlin.io.println(any)  //重名了可以用包名调用
  7.         }
  8.     }
  9.       val pdfPrinter = PdfPrinter()
  10.       args.forEach(pdfPrinter::println)
复制代码
类引用
  1. val c = MyClass::class
复制代码
该引用是 KClass 类型的值
请注意,Kotlin 类引用与 Java 类引用不同。要获得 Java 类引用, 请在 KClass 实例上使用 .java 属性。
平时写的类,其信息都可以在这个KClass来获取
属性引用
  1. data class MediaItem(val title: String, val url: String)
  2. var items= mutableListOf<MediaItem>()
  3. items
  4.     .sortedBy { it.title }
  5.     .map { it.url }
  6.     .forEach { print(it) }
  7. items
  8.     .sortedBy(MediaItem::title)
  9.     .map(MediaItem::url)
  10.     .forEach(::println)
复制代码
匿名函数

没有名字的函数
要传一个函数类型的参数,或者把一个函数类型的对象赋值给变量,除了用双冒号来拿现成的函数使用,你还可以直接把这个函数挪过来写:
  1. fun b(param: Int): String {
  2.     return param.toString()
  3. }
  4. a(fun b(param: Int): String {
  5.   return param.toString()
  6. });
  7. val d = fun b(param: Int): String {
  8.   return param.toString()
  9. }
  10. //名字没意义,省略
  11. a(fun(param: Int): String {
  12.   return param.toString()
  13. });
  14. val d = fun(param: Int): String {
  15.   return param.toString()
  16. }
复制代码
如果你在 Java 里设计一个回调的时候是这么设计的:
  1. public interface OnClickListener {
  2.   void onClick(View v);
  3. }
  4. public void setOnClickListener(OnClickListener listener) {
  5.   this.listener = listener;
  6. }
复制代码
使用的时候是这么用的:
  1. view.setOnClickListener(new OnClickListener() {
  2.   @Override
  3.   void onClick(View v) {
  4.     switchToNextPage();
  5.   }
  6. });
复制代码
kotlin写法
  1. fun setOnClickListener(onClick: (View) -> Unit) {
  2.   this.onClick = onClick
  3. }
  4. view.setOnClickListener(fun(v: View): Unit) {
  5.   switchToNextPage()
  6. })
复制代码
Lambda写法:
  1. view.setOnClickListener({ v: View ->
  2.   switchToNextPage()
  3. })
复制代码
Lambda 表达式

简化匿名函数,代码更简洁
  1.     view.setOnClickListener({ v: View ->
  2.         switchToNextPage()
  3.     })
  4. //如果 Lambda 是函数的最后一个参数,你可以把 Lambda 写在括号的外面:
  5.     view.setOnClickListener() { v: View ->
  6.         switchToNextPage()
  7.     }
  8. //而如果 Lambda 是函数唯一的参数,你还可以直接把括号去了:
  9.     view.setOnClickListener { v: View ->
  10.         switchToNextPage()
  11.     }
  12. //另外,如果这个 Lambda 是单参数的,它的这个参数也省略掉不写:
  13. //根据上下文推导,根据最后一行代码来推断出返回值类型
  14.     view.setOnClickListener {
  15.         switchToNextPage()
  16.     }
复制代码
Lambda 表达式的完整语法形式如下:
  1. val sum: (Int, Int) -> Int = { x: Int, y: Int -> x + y }
  2. val sum = { x: Int, y: Int -> x + y }
复制代码
多参数例子:
fold函数:将所提供的操作应用于集合元素并返回累积的结果
  1. val items = listOf(1, 2, 3, 4, 5)
  2. // Lambdas 表达式是花括号括起来的代码块。
  3. items.fold(0, {
  4.     // 如果一个 lambda 表达式有参数,前面是参数,后跟“->”
  5.     acc: Int, i: Int ->
  6.     print("acc = $acc, i = $i, ")
  7.     val result = acc + i
  8.     println("result = $result")
  9.     // lambda 表达式中的最后一个表达式是返回值:
  10.     result
  11. })
  12. // lambda 表达式的参数类型是可选的,如果能够推断出来的话:
  13. val joinedToString = items.fold("Elements:", { acc, i -> acc + " " + i })
复制代码
输出:
  1. acc = 0, i = 1, result = 1
  2. acc = 1, i = 2, result = 3
  3. acc = 3, i = 3, result = 6
  4. acc = 6, i = 4, result = 10
  5. acc = 10, i = 5, result = 15
  6. joinedToString = Elements: 1 2 3 4 5
复制代码
总结:
函数不能直接传递或者赋给某个变量,需要函数类型实例化,有三种方式:
使用已有声明的可调用引用
1.函数引用
使用函数字面值的代码块
2.匿名函数
3.lambda 表达式
例子

实现接口
  1. var onVideoStartCallBack: (() -> Unit)? = null
  2. onVideoStartCallBack?.invoke()
  3. videioView.onVideoStartCallBack = {
  4. }
复制代码
函数里实现接口
  1. object UploaderListHelper {
  2.     fun startTaskUpload(activity: Activity, startCallBack: ((Int) -> Unit)?) {
  3.         startCallBack.invoke(position)
  4.     }
  5. }
  6. UploaderListHelper.startTaskUpload(activity) {
  7.     refreshProgress(it)
  8. }
复制代码
作用域函数

Kotlin 标准库包含几个函数,它们的唯一目的是在对象的上下文中执行代码块。当对一个对象调用这样的函数并提供一个 lambda 表达式时,它会形成一个临时作用域。在此作用域中,可以访问该对象而无需其名称。这些函数称为作用域函数。共有以下五种:let、run、with、apply 以及 also。
这些函数基本上做了同样的事情:在一个对象上执行一个代码块。不同的是这个对象在块中如何使用,以及整个表达式的结果是什么。
目的:简洁
  1. val person = findPerson();
  2.         //person是可null的,所以需要?
  3.         println(person?.age)
  4.         println(person?.name)
  5.         //上面太麻烦,findPerson加了?,所以后面不需要了,减少的判空操作。let可以安全调用
  6.         findPerson()?.let { person ->
  7.             person.work()
  8.             println(person.age)
  9.         }
  10.         //还可以更简洁,person也不用写
  11.         findPerson()?.apply {
  12.             work()
  13.             println(age)
  14.         }
复制代码
使⽤时可以通过简单的规则作出一些判断
返回自身
返回值是它本身
从  apply 和  also 中选
作⽤域中使⽤ this 作为参数选择 apply
  1. val adam = Person("Adam").apply {
  2.     age = 32
  3.     city = "London"        
  4. }
  5. println(adam)
复制代码
作⽤域中使⽤ it 作为参数选择 also
  1. val numbers = mutableListOf("one", "two", "three")
  2. numbers
  3.     .also { println("The list elements before adding new one: $it") }
  4.     .add("four")
复制代码
with 非扩展函数
  1. val numbers = mutableListOf("one", "two", "three")
  2. with(numbers) {
  3.     println("'with' is called with argument $this")
  4.     println("It contains $size elements")
  5. }
复制代码
不需要返回自身
从 run 和 let 中选择
作用域中使用 this 作为参数,选择 run
作用域中使用 it 作为参数,选择 let, 适合配合空判断的时候
  1. val service = MultiportService("https://example.kotlinlang.org", 80)
  2. val result = service.run {
  3.     port = 8080
  4.     query(prepareRequest() + " to port $port")
  5. }
  6. // 同样的代码如果用 let() 函数来写:
  7. val letResult = service.let {
  8.     it.port = 8080
  9.     it.query(it.prepareRequest() + " to port ${it.port}")
  10. }
复制代码
it作为参数的好处
let 允许我们自定义参数名字,使可读性更强,如果倾向可读性可以选择 T.let

参考文章
Kotlin 的高阶函数、匿名函数和 Lambda 表达式
Kotlin官网

免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?立即注册

x
回复

使用道具 举报

0 个回复

倒序浏览

快速回复

您需要登录后才可以回帖 登录 or 立即注册

本版积分规则

南七星之家

金牌会员
这个人很懒什么都没写!

标签云

快速回复 返回顶部 返回列表