变量延迟初始化 lateinit var
使用 lateinit
来标识延迟初始化,使用::adapter.isInitialized
判断是否已经初始化。
class MainActivity : AppcompatActivity(), View.OnClickListener {
private lateinit val adapter:MessageAdapter
override fun onCreate(saveInstanceState: Bundle?) {
// 判断是否已经初始化
if (!::adapter.isInitialized) {
adapter = MessageAdapter()
}
}
}
定义常量 companion object { const val TAG = "MainActivity" }
class MainActivity : AppcompatActivity() {
companion object {
const val TAG = "MainActivity"
}
}
扩展函数
扩展函数最好放在顶层函数,可以让扩展函数拥有全局访问域。
// 扩展函数语法接口
fun ClassName.methodName(param:T):T {}
fun String.lettersCount():Int {
var count = 0
for (char in this) { if (char.isLetter()) count++ }
return count
}
fun String.lettersCount() = this.fold(0) { total, char -> total + if(char.isLetter()) 1 else 0 }
运算符重载
运算符重载写法,在类中使用operator
加上指定操作符方法名。
// 运算符重载写法
class Xxx {
operator fun plus(xxx:Xxx):Xxx {}
}
// 重载加法运算符方法
class Money(val value:Int) {
operator fun plus(money: Money) = Money(value + money.value)
operator fun plus(money: Int) = Money(value + money)
}
println(Money(3) + Money(5)) // 8
println(Money(4) + 5) // 9
操作符和方法名对照
表达式 | 实际调用方法 |
---|---|
a + b | a.plus(b) |
a - b | a.minus(b) |
a * b | a.times(b) |
a / b | a.minus(b) |
a % b | a.div(b) |
a++ | a.inc() |
a-- | a.dec |
+a | a.unaryPlus() |
-a | a.unaryMinus() |
!a | a.not() |
a == b | a.equals(b) |
a > b | a.compareTo(b) > 0 |
a < b | a.compareTo(b) < 0 |
a >= b | a.compareTo(b) >= 0 |
a <= b | a.compareTo(b) <= 0 |
a..b | a.rangeTo(b) |
a[b] | a.get(b) |
a[b] = c | a.set(b, c) |
a in b | b.contains(a) |
以上操作都可以重载重新定义。 |
扩展函数重载操作符
// kotlinUitls.kt
operator fun String.times(n:Int) = repeat(n)
"abc" * 3 // abcabcabc
高阶函数
定义:如果一个函数接收另外一个函数作为参数,或者返回值的类型是另外一个函数,那么该函数就是高阶函数。
函数类型:由参数和返回值组成,相同的参数和返回值可以看做同一种类型
// 函数类型,第一个参数String,第二个参数Int,返回值Int 的函数类型
(String,Int) -> Int
fun example(func:(String, Int) -> Unit) {
func("hello", 123)
}
// HigherOrderFunction.kt
fun num1AndNum2(num1:Int,num2:Int, operator:(Int,Int) -> Int):Int {
return operator(num1, num2)
}
fun plus(a:Int,b:Int) = a + b
fun minus(a:Int,b:Int) = a - b
fun main() {
// ::plus 表示获取改函数的引用
println(num1AndNum2(3, 5, ::plus)) // 8
println(num1AndNum2(3, 5, ::minus)) // -2
// 也可以使用Lambda表达式
println(num1AndNum2(3, 5) { a,b -> a * b }) // 15
}
// 扩展StringBuilder类
fun StringBuilder.build(block: StringBuilder.() -> Unit()):StringBuilder {
block()
return this
}
StringBuilder().build {
append("aa|")
append("bb|")
}.toString() // aa|bb|
内联函数
使用高阶函数会带来一些运行时的效率损失:每一个函数都是一个对象,并且会捕获一个闭包。 即那些在函数体内会访问到的变量。 内存分配(对于函数对象和类)和虚拟调用会引入运行时间开销。
但是在许多情况下通过内联化 lambda 表达式可以消除这类的开销。 下述函数是这种情况的很好的例子。即 lock() 函数可以很容易地在调用处内联。 考虑下面的情况:
高阶函数的函数在相当于java接口的匿名内部实现类,所以高阶函数的函数一般会创建匿名内部类,造成性能开销。
在高阶函数fun前添加inline表示内联函数。内联函数的参数函数内return相当于在整个内联函数的return。
fun testInlineFun1(str:String,call:(String)->Unit) {
pritnln("call before")
call(str)
pritnln("call after")
}
fun main() {
testInlineFun1("") { str-> if(str.isEmpty()) return@testInlineFun1 }
}
//call before
//call after
inline fun testInlineFun2(str:String,call:(String)->Unit) {
pritnln("call before")
call(str)
pritnln("call after")
}
fun main() {
testInlineFun2("") { str-> if(str.isEmpty()) return }
}
//call before
在inline修饰的内联函数,可以使用noinline来消除某个函数参数内联属性。
inline fun testInlineFun(noinline call1:()->Unit,call2:(String)->Unit) {}
在inline修饰的内联函数体内嵌套匿名内部类调用函数参数,要是crossinline修饰函数参数并且该函数不能有retrun操作。
inline fun testInlineFun(crossinline call:()->Unit) {
val runnable = Runnable { call() }
runnable.run()
}
高阶函数应用
// 在 SharedPreference.Editor 定义临时函数 block,block代码块里面上下文是SharedPreference.Editor
inline fun SharedPreferences.open(call: SharedPreferences.Editor.() -> Unit) {
val editor = edit()
editor.call()
editor.apply()
}
SharedPreence("data", Context.MODE_PRIVATE).open{
put("name", "tim");
put("age", 18)
}
fun cvOf(vararg pairs:Pair<String, Any?>) = ContextValue().apply {
for(pair in pairs) {
val key = pair.first
val value = pair.second
when(value) {
is Int -> put(key, value)
is Long -> put(key, value)
...
null -> putNull(key)
}
}
}
val contextValue = cvOf("a" to "aaa", "b" To 18)