阅读 222

Android:Kotlin详细入门学习指南-高阶函数-基础语法(七)

本人也是在初学Kotlin,如有错误,请帮忙指出,持续更新

Android:Kotlin详细入门学习指南-高阶函数-基础语法(七)

建议先看看前面的文章 Android:Kotlin详细入门学习指南-基础语法(一)

Android:Kotlin详细入门学习指南-基本类型-基础语法(二)

Android:Kotlin详细入门学习指南-包-控制流-返回与跳转-基础语法(三)

Android:Kotlin详细入门学习指南-类和对象-基础语法(四)

Android:Kotlin详细入门学习指南-类和对象(下)-基础语法(五)

Android:Kotlin详细入门学习指南-函数-基础语法(六)

这篇文章分享的内容比较多,建议先关注收藏,再查看,以免迷路

高阶函数

高阶函数就是可以接受函数作为参数并返回一个函数的函数

字面函数和函数表达式

字面函数或函数表达式就是一个 "匿名函数",也就是没有声明的函数,但立即作为 表达式传递下去。

max(strings, {a, b -> a.length < b.length })
复制代码

max 函数就是一个高阶函数,它接受函数作为第二个参数。第二个参数是一个表达 式所以本生就是一个函数,即字面函数。作为一个函数,相当于:

fun compare(a: String, b: String) : Boolean = a.length < b.lengt h
复制代码
函数类型

一个函数要接受另一个函数作为参数,我们得给它指定一个类型。

fun max<T>(collection: Collection<out T>, less: (T, T) -> Boolean ): T? { 
	var max: T? = null 
	for (it in collection) 
		if (max == null || less(max!!, it)) 
			max = it 
			return max 
}
复制代码

参数 less 是 (T, T) -> Boolean 类型,也就是接受俩个 T 类型参数返回一 个 Boolean :如果第一个参数小于第二个则返回真。 在函数体第四行, less 是用作函数

val compare: (x: T,y: T) -> Int = ...
复制代码
函数文本语法
val sum = {x: Int,y: Int -> x + y}
复制代码

函数文本总是在大括号里包裹着,在完全语法中参数声明是在括号内,类型注解是 可选的,函数体是在 -> 之后,像下面这样:

val sum: (Int, Int) -> Int = {x, y -> x+y }
函数文本有时只有一个参数。如果 kotlin 可以从它本生计算出签名,那么可以省略 这个唯一的参数,并会通过 it 隐式的声明它:

```kotlin
ints.filter {it > 0}//这是 (it: Int) -> Boolean 的字面意思
复制代码

注意如果一个函数接受另一个函数做为最后一个参数,该函数文本参数可以在括号 内的参数列表外的传递。

函数表达式

上面没有讲到可以指定返回值的函数。在大多数情形中,这是不必要的,因为返回 值是可以自动推断的。然而,如果你需要自己指定,可以用函数表达式来做:

fun(x: Int, y: Int ): Int = x + y
复制代码
闭包

一个字面函数或者表达式函数可以访问闭包,即访问自身范围外的声明的变量。不 像 java 那样在闭包中的变量可以被捕获修改:

var sum = 0 ins filter {it > 0} forEach { 
	sum += it 
}
print(sum)
复制代码
内联函数

inline 标记即影响函数本身也影响传递进来的 lambda 函数:所有的这些都将被 关联到调用点。 内联可能会引起生成代码增长,但我们可以合理的解决它(不要内联太大的函数)

inline fun lock<T>(lock: Lock,body: ()-> T): T { //... }
复制代码

为了你想要一些 lambda 表达式传递给内联函数时是内联的,你可以给你的一些函 数参数标记 @noinline 注解:

inline fun foo(inlined: () -> Uint, @noinline notInlined: () -> Unit) { //... }
复制代码

内联的 lambda 只能在内联函数中调用,或者作为内联参数,但 @noinline 标记 的可以通过任何我们喜欢的方式操控:存储在字段,( passed around etc)

返回到非局部

在 kotlin 中,我们可以不加条件的使用 return 去退出一个命名函数或表达式函 数。这意味这退出一个 lambda 函数,我们不得不使用标签,而且空白的 return 在 lambda 函数中是禁止的,因为 lambda 函数不可以造一个闭合函数返回:

fun foo() { 
	ordinaryFunction { 
		return // 错误 不可以在这返回 
		} 
}
复制代码

但如果 lambda 函数是内联传递的,则返回也是可以内联的,因此允许下面这样:

fun foo() { 
	inlineFunction { 
		return // 
		] 
}
复制代码

注意有些内联函数可以调用传递进来的 lambda 函数,但不是在函数体,而是在另 一个执行的上下文中,比如局部对象或者一个嵌套函数。在这样的情形中,非局部 的控制流也不允许在lambda 函数中。 内联 lambda 不允许用 break 或 continue ,但在以后的版本可能会支持。

实例化参数类型

有时候我们需要访问传递过来的类型作为参数:

fun <T> TreeNode.findParentOfType(clazz: Class<T>): T? { 
	var p = parent 
	while (p != null && !clazz.isInstance(p)) { 
		p = p?.parent 
	}
	@suppress("UNCHECKED_CAST") 
	return p as T 
}
复制代码

现在,我们创立了一颗树,并用反射检查它是否是某个特定类型。一切看起来很 好,但调用点就很繁琐了:

myTree.findParentOfType(javaClass<MyTreeNodeType>() )
复制代码

我们想要的仅仅是给这个函数传递一个类型,即像下面这样:

myTree.findParentOfType<MyTreeNodeType>()
复制代码

为了达到这个目的,内联函数支持具体化的类型参数,因此我们可以写成这样:

inline fun <reified T> TreeNode.findParentOfType(): T? { 
	var p = parent 
	while (p != null && p !is T) { 
		p = p?.parent 
	}
	return p as T 
}
复制代码

用 refied 修饰符检查类型参数,既然它可以在函数内部访问了,也就基本上接 近普通函数了。因为函数是内联的,所以不许要反射,像 !is `as`这样的操作 都可以使用。同时,我们也可以像上面那样调用它了

myTree.findParentOfType<MyTreeNodeType>()
复制代码

尽管在很多情况下会使用反射,我们仍然可以使用实例化的类型参数 javaClass() 来访问它:

inline fun methodsOf<reified T>() = javaClass<T>().getMethods() 
fun main(s: Array<String>) { 
	println(methodsOf<String>().joinToString('\n')) 
}
复制代码

普通的函数(没有标记为内联的)不能有实例化参数。

这篇文章主要分享Kotlin中的高阶函数.

本人也是在初学Kotlin,如有错误,请帮忙指出,持续更新

文章分类
Android
文章标签