扩展函数
声明一个扩展函数,我们需要用一个 接收者类型 也就是被扩展的类型来作为他的前缀
其实就是通过import指定的路径,把我们自定义的函数作为list,map等一些对象的方法,比在java中封装util更为方法,直接以list.的方式调用
val list = mutableListOf(1, 2, 3)
list.swap(0, 2) // “swap()”内部的“this”会保存“list”的值
//该函数可以被所有的MutableList对象调用
fun <T> MutableList<T>.swap(index1: Int, index2: Int) {
val tmp = this[index1] // “this”对应该列表
this[index1] = this[index2]
this[index2] = tmp
}
扩展属性
与扩展函数类似,就是对指定对象的属性的一种扩展
val <T> List<T>.lastIndex: Int
get() = size - 1
- 扩展属性不能有初始化器
伴生对象
Kotlin没有静态成员,伴生对象是kotlin为java静态成员提供替代方案
因为java没有全局变量,一切属性和方法都是在类里面,Kotlin 之所以能抛弃静态成员,主要原因在于它允许包级属性和函数的存在,包级属性和包级函数可以理解为全局常量和工具函数
class MyClass {
//伴生对象的声明
companion object {
@JvmStatic val anonymous = Person("Anonymous")
fun say() = println("Hello")
}
}
@JvmStatic 注解只能用在伴生对象里,修饰伴生对象内的属性和函数,用来告诉编译器将属性和函数编译为真正的 JVM 静态成员。需要注意到,如果在伴生对象声明里使用 @JvmStatic 注解,那么没有加该注解的属性和函数将不会被编译为静态成员
委托
委托类实现的printMessage覆盖了原有的实现,有点像java的动态代理
interface Base {
fun printMessage()
fun printMessageLine()
}
class BaseImpl(val x: Int) : Base {
override fun printMessage() { print(x) }
override fun printMessageLine() { println(x) }
}
//Derived 的超类型列表中的 by-子句表示 b 将会在 Derived 中内部存储,
//并且编译器将生成转发给 b 的所有 Base 的方法。
class Derived(b: Base) : Base by b {
override fun printMessage() { print("abc") }
}
fun main() {
val b = BaseImpl(10)
Derived(b).printMessage()
Derived(b).printMessageLine()
}
委托属性
延迟属性
懒加载,初始化代码块,只有第一次加载时,调用,之后就是直接使用 "Hello"的值,
下面打印的结果为computed! Hello Hello
val lazyValue: String by lazy {
println("computed!")
"Hello"
}
fun main() {
println(lazyValue)
println(lazyValue)
}
可观察属性
属性变化时,监听器会收到有关此属性变更的通知
class User {
var name: String by Delegates.observable("init") {
prop, old, new ->
println("$old -> $new")
}
}
fun main() {
val user = User()
user.name = "first"
user.name = "second"
}
把属性储存在映射中map中
把多个属性储存在一个映射(map)中,而不是每个存在单独的字段中
class User(val map: Map<String, Any?>) {
val name: String by map
val age: Int by map
}
构造函数接受一个映射参数
val user = User(mapOf(
"name" to "John Doe",
"age" to 25
))
println(user.name) // Prints "John Doe"
println(user.age) // Prints 25
委托属性在实际应用中,有时候就是对get,set增加了校验,比如判null或者一些其它的判断
class Delegate {
operator fun getValue(thisRef: Any?, property: KProperty<*>): String {
return "$thisRef, thank you for delegating '${property.name}' to me!"
}
operator fun setValue(thisRef: Any?, property: KProperty<*>, value: String) {
println("$value has been assigned to '${property.name}' in $thisRef.")
}
}
参考一个sp的写法
调用处
private val zipCode: Long by DelegatesExt.preference(this, SettingsActivity.ZIP_CODE,
SettingsActivity.DEFAULT_ZIP)
实现处
object DelegatesExt {
fun <T> preference(context: Context, name: String,
default: T) = Preference(context, name, default)
}
class Preference<T>(private val context: Context, private val name: String,
private val default: T) {
private val prefs: SharedPreferences by lazy {
context.getSharedPreferences("default", Context.MODE_PRIVATE)
}
operator fun getValue(thisRef: Any?, property: KProperty<*>): T = findPreference(name, default)
operator fun setValue(thisRef: Any?, property: KProperty<*>, value: T) {
putPreference(name, value)
}
@Suppress("UNCHECKED_CAST")
private fun findPreference(name: String, default: T): T = with(prefs) {
val res: Any = when (default) {
is Long -> getLong(name, default)
is String -> getString(name, default)
is Int -> getInt(name, default)
is Boolean -> getBoolean(name, default)
is Float -> getFloat(name, default)
else -> throw IllegalArgumentException("This type can be saved into Preferences")
}
res as T
}
@SuppressLint("CommitPrefEdits")
private fun putPreference(name: String, value: T) = with(prefs.edit()) {
when (value) {
is Long -> putLong(name, value)
is String -> putString(name, value)
is Int -> putInt(name, value)
is Boolean -> putBoolean(name, value)
is Float -> putFloat(name, value)
else -> throw IllegalArgumentException("This type can't be saved into Preferences")
}.apply()
}
}
内联函数
关键字inline,一般适合方法循环或者递归调用其它方法,方法进栈出栈次数多,成本比较高
扩展函数使用inline的也比较多,因为这些函数都做为了参数
默认参数
fun foo(title1: Int = 0, title2: Int) { /*……*/ }
foo(title1 = 1)那么title2就是用了默认参数
单表达式函数
也就是单行函数的写法,省略了花括号
fun double(x: Int): Int = x * 2
中缀表示法
标有 infix 关键字的函数也可以使用中缀表示法(忽略该调用的点与圆括号)调用。中缀函数必须满足以下要求:
- 它们必须是成员函数或扩展函数
- 它们必须只有一个参数
- 其参数不得接受可变数量的参数
infix fun Int.shl(x: Int): Int { …… }
// 用中缀表示法调用该函数
1 shl 2
// 等同于这样
1.shl(2)
局部函数
Kotlin 支持局部函数,即一个函数在另一个函数内部,局部函数可以访问外部函数(即闭包)的局部变量
fun dfs(graph: Graph) {
val visited = HashSet<Vertex>()
fun dfs(current: Vertex) {
if (!visited.add(current)) return
for (v in current.neighbors)
dfs(v)
}
dfs(graph.vertices[0])
}
尾递归函数
这允许一些通常用循环写的算法改用递归函数来写,而无堆栈溢出的风险。 当一个函数用 tailrec 修饰符标记并满足所需的形式时,编译器会优化该递归,留下一个快速而高效的基于循环的版本:
val eps = 1E-10 // "good enough", could be 10^-15
tailrec fun findFixPoint(x: Double = 1.0): Double
= if (Math.abs(x - Math.cos(x)) < eps) x else findFixPoint(Math.cos(x))
等同于下面这种写法
val eps = 1E-10 // "good enough", could be 10^-15
private fun findFixPoint(): Double {
var x = 1.0
while (true) {
val y = Math.cos(x)
if (Math.abs(x - y) < eps) return x
x = Math.cos(x)
}
}
lamda表达式
这些类型具有与函数签名相对应的特殊表示法,即它们的参数和返回值:
-
所有函数类型都有一个圆括号括起来的参数类型列表以及一个返回类型:
(A, B) -> C
表示接受类型分别为A
与B
两个参数并返回一个C
类型值的函数类型。 参数类型列表可以为空,如() -> A
。Unit
返回类型不可省略。 -
函数类型可以有一个额外的接收者类型,它在表示法中的点之前指定: 类型
A.(B) -> C
表示可以在A
的接收者对象上以一个B
类型参数来调用并返回一个C
类型值的函数。 带有接收者的函数字面值通常与这些类型一起使用。 -
挂起函数属于特殊种类的函数类型,它的表示法中有一个 suspend 修饰符 ,例如
suspend () -> Unit
或者suspend A.(B) -> C
。挂起函数我们放到协程那部分讲
list.forEachIndexed {
index, value ->
}
实现
(index: Int, T)是传入函数的类型 -> Unit 就是实现
public inline fun <T> Iterable<T>.forEachIndexed(action: (index: Int, T) -> Unit): Unit {
var index = 0
for (item in this)
//这里是对传入函数的调用,并且返回了index和item,
//这里调用一次,外边就会响应一次
action(checkIndexOverflow(index++), item)
}
//传入string和int两个参数,返回string 返回值将取决于函数{}中的逻辑,但必须返回string,但是函数{}只能接收int作为参数
val repeatFun1: String.(Int) -> String = { times -> "times$times" }
//传入string和int两个参数,返回string 返回值将取决于函数{}中的逻辑,但必须返回string
val repeatFun2: (a:String,b:Int) -> String = { a,b -> "a$a b$b" }
//函数{}中的逻辑扩展性很高,repeat重复append自己
val repeatFun3: String.(Int) -> String = { times -> this.repeat(times) }
fun runTransformation(f: (String, Int) -> String): String {
return f("hello", 3)
}
::操作符,函数类型的值可以通过其 invoke(……)
操作符调用:f.invoke(x)
或者直接 f(x)
。但必须要满足一下要求才可以使用::
- 顶层、局部、成员、扩展函数:
::isOdd
、String::toInt
, - 顶层、成员、扩展属性
List<Int>::size
, - 构造函数:
::Regex
val stringPlus: (String, String) -> String = String::plus
val intPlus: Int.(Int) -> Int = Int::plus
println(stringPlus.invoke("<-", "->"))
println(stringPlus("Hello, ", "world!"))
println(intPlus.invoke(1, 1))
println(intPlus(1, 2))
println(2.intPlus(3)) // 类扩展调用
解构声明
有时把一个对象 解构 成很多变量会很方便
val (name, age) = person
println(name)
println(age)
作用域函数
let,run,with,apply,also,

各个函数执行场景
- 对非空对象执行lambda let
- 在局部作用域中引入表达式作为变量 let
- 对象配置 apply
- 对象配置和计算结果 run
- 在需要表达式的地方运行语句:非扩展 run
- 额外的效果 also
- 对对象的函数调用进行分组 with
注意避免过度使用和嵌套过多
简单看几个例子
val numberList = mutableListOf<Double>()
numberList.also { println("Populating the list") }
.apply {
add(2.71)
add(3.14)
add(1.0)
}
.also { println("Sorting the list") }
.sort()
val numbers = mutableListOf("one", "two", "three", "four", "five")
val resultList = numbers.map { it.length }.filter { it > 3 }
println(resultList)
优化成
val numbers = mutableListOf("one", "two", "three", "four", "five")
numbers.map { it.length }.filter { it > 3 }.let {
println(it)
}
网友评论