几个特性,快速上手Kotlin

前言

由于工做须要,因此最近大量的时间都用在了对Kotlin的提高上。因此最近的文章基本都是关于Kotlin的了内容。html

这不是一个死扣细节的文章,而是一个帮助小伙伴们快速由Java(Android)转向Kotlin的文章,所以更多的是一种语法与思想混在的模式。java

几个特性,快速上手Kotlin面试

充分理解Kotlin,快速上手写业务设计模式

快速切换至Kotlin for Android模式闭包

聊一聊Kotlin中的协程,真香ide

正文

很少扯淡,点进来的小伙伴们确定都是来学技术的,开搞。函数

1、属性

各位,千万不要由于标题的属性,就以为没什么养分,觉得我要说什么var、val。不不不,往下看,kotlin中的属性大有文章。post

1.一、可观察属性Observable

这个语言特性,是很是很是用意思,且实用的。不信?往下看,来一个demo:性能

class User {
    var name: String by Delegates.observable("<no name>") {
        prop, old, new ->
        println("$old -> $new")
    }
}

fun main() {
    val user = User()
    user.name = "first"
    user.name = "second"
}
复制代码

效果好很差,我们看“疗效”,运行结果以下:学习

是否是以为有点意思?咱们name这个属性的任何变化,都被观察了。咱们能够在回调函数中,“胡搞瞎搞”~至于怎么搞?whatever。 有小伙伴可能想说,既然我监听了属性的变化,我能够不以偷偷改些属性呢?固然能够,不过咱们须要下面这个函数。

1.二、vetoable

vetoable比较有意思,能够理解为一个hook函数,它接受一个用于判断的表达式,知足这个表达式的才会被赋值,不然丢弃。很简单,上demo:

// 过滤不知足条件的set
var max: Int by Delegates.vetoable(0) { property, oldValue, newValue ->
    newValue > oldValue
}

println(max) // 0

max = 10
println(max) // 10

max = 5
println(max) // 10
复制代码

2.一、延迟属性 Lazy

官方解释(罗里吧嗦,不看也罢) lazy() 是接受一个 lambda 并返回一个 Lazy 实例的函数,返回的实例能够做为实现延迟属性的委托: 第一次调用 get() 会执行已传递给 lazy() 的 lambda 表达式并记录结果, 后续调用 get() 只是返回记录的结果。

说白了就是懒加载。被lazy标识的变量,在被调用的时候会触发咱们实现的表达式,

(下文会重点聊表达式,这里咱们只须要以后,表达式的最后一行,表明着return)

并拿到表达式的返回值。但表达式只会执行一次,后续的调用,直接回去表达式的返回值。也就是咱们常说的懒加载。

val lazyValue: String by lazy {
    println("computed!")
    "Hello"
}

fun main() {
    println(lazyValue)
    println(lazyValue)
}
复制代码

看结果咱们就很清晰了吧。

委托

上边说的这些东西,在Kotlin之中都统一称之为委托/委托属性。委托是比较好用的一种语言特性,甚至能够很巧妙的帮咱们解决一些复杂的设计模式上的问题。 这其中的有趣,还望小伙伴们本身去探索呦~


2、表达式

对于个人学习来讲,表达式的不理解,最开始对我阅读代码形成了很大的困惑。主要是少了“相濡以沫”的return,搞得本身有点懵。因此这里,让咱们聊一聊表达式,也算填了上文挖的坑。

1.一、if表达式

if这个关键字,在Kotlin中表明一个表达式,它会默认有一个return,就是表达式中的最后一行。好比:

var num = if(...某个判断){
    666
}else{
    66666
}
复制代码

这里num的值就是666。既然咱们的if有自带return的功能,那么咱们Java中经常使用的?:(三元运算符)是否是就没办法用了?的确要三元运算符(条件 ? 而后 : 不然)在Kotlin中换了写法(但并不是不能用),由于普通的 if 就能胜任这个角色。

// 做为表达式
val max = if (a > b) a else b

val max = if (a > b) {
    print("Choose a")
    a
} else {
    print("Choose b")
    b
}
复制代码

?:在Kotlin中表示: 若是左侧的值为空,就取右侧的值。

更多有趣的符号用法,能够参考官网:www.kotlincn.net/docs/refere…

1.二、关于return

既然咱们从if中,了解了if中的隐式return,那这里可能会有一个疑问,能不能显示的写一个return呢?答案是:不行。 由于在Kotlin中,return的语义是这样的:从最直接包围它的函数或者匿名函数返回。

表示式不属于函数,因此不行,一样Lambda表达式也不行。不这里有些特殊状况,因此咱们好好聊一聊:

要退出一个 Lambda 表达式,咱们必须使用一个标签,而且在 Lambda 表达式内部禁止使用裸 return,由于 Lambda 表达式不能使包含它的函数返回:

// 这里forEach是一个Lambda表达式,咱们使用**标签**的形式,使其return
listOf(1, 2, 3, 4, 5).forEach lit@{
    if (it == 3) return@lit // 局部返回到该 lambda 表达式的调用者,即 forEach 循环
    print(it)
}
复制代码

这里确定有小伙伴质疑:我能够在forEach里直接return啊!没错,的确是能够。由于forEach内联函数。内联函数是能够return的:

关于内联函数后文会有篇幅展开它。

官方介绍:内联是被容许的(这种返回(位于 lambda 表达式中,但退出包含它的函数)称为非局部返回。)

inline fun inlined(block: () -> Unit) {
    println("hi!")
}
fun foo() {
    inlined {
        return // OK:该 lambda 表达式是内联的
    }
}
fun main() {
    foo()
}
复制代码

好比,这种return是合法的,由于foreach是内联函数

fun hasZeros(ints: List<Int>): Boolean {
    ints.forEach {
        if (it == 0) return true // 从 hasZeros 返回
    }
    return false
}

public inline fun <T> Iterable<T>.forEach(action: (T) -> Unit): Unit {
    for (element in this) action(element)
}
复制代码

嘚吧嘚说了这么,平常能用到么?说实话,没有diao用。不过,遇到了我们知道该如何解释,这也算是一种收获吧。

2.一、When 表达式

when是咱们经常使用的switch的加强plus版。其最简单的形式以下:

when (x) {
    1 -> print("x == 1")
    2 -> print("x == 2")
    else -> { // 注意这个块
        print("x is neither 1 nor 2")
    }
}
复制代码

咱们也能够检测一个值在(in)或者不在(!in)一个区间或者集合中。即知足某些条件:

when (x) {
    in 1..10 -> print("x is in the range")
    in validNumbers -> print("x is valid")
    !in 10..20 -> print("x is outside the range")
    else -> print("none of the above")
}
复制代码

另外一种用法是检测一个值是(is)或者不是(!is)一个特定类型的值。

因为智能转换,咱们能够访问该类型的方法与属性而无需任何额外的检测。

fun hasPrefix(x: Any) = when(x) {
    is String -> x.startsWith("prefix")
    else -> false
}
复制代码

由于,说when是switch的plus不为过吧。更多有趣的用法,欢迎各位小伙伴留言补充呦~


3、杂乱的小细节

1.一、Class

val c = MyClass::class
复制代码

返回的是KClass,若是咱们须要Class,则要这样MyClass::class.java

1.二、this

要访问来自外部做用域的this(一个类 或者扩展函数, 或者带标签的带有接收者的函数字面值)咱们使用this@label,其中 @label 是一个代指 this 来源的标签:

class A { // 隐式标签 @A
    inner class B { // 隐式标签 @B
        fun Int.foo() { // 隐式标签 @foo
            val a = this@A // A 的 this
            val b = this@B // B 的 this

            val c = this // foo() 的接收者,一个 Int
            val c1 = this@foo // foo() 的接收者,一个 Int

            val funLit = lambda@ fun String.() {
                val d = this // funLit 的接收者
            }


            val funLit2 = { s: String ->
                // foo() 的接收者,由于它包含的 lambda 表达式
                // 没有任何接收者
                val d1 = this
            }
        }
    }
}
复制代码

1.三、匿名函数

var list = arrayListOf(1, 2, 3, 4, 5, 6)
        // lambda表达式
        list.filter {
            it > 3
        }
        // 规规矩矩的匿名函数
        list.filter(fun(it): Boolean {
            return it > 3
        })
        // 自动类型推断后简写的匿名函数
        list.filter(fun(it) = it > 3)
  
复制代码

对于具备表达式函数体的匿名函数将自动推断返回类型,而具备代码块函数体的返回类型必须显式指定(或者已假定为 Unit) 匿名函数参数老是在括号内传递。 容许将函数留在圆括号外的简写语法仅适用于 lambda 表达式。

Lambda表达式与匿名函数之间的另外一个区别是非局部返回的行为。一个不带标签的 return 语句老是在用 fun 关键字声明的函数中返回。这意味着 lambda 表达式中的 return 将从包含它的函数返回,而匿名函数中的 return 将从匿名函数自身返回。

1.四、内联函数

使用高阶函数(关于高阶函数,能够看我以前的一篇文章)会带来一些运行时的效率损失。由于每个函数都是一个对象,而且会捕获一个闭包( 即那些在函数体内会访问到的变量)。 咱们都清楚,内存分配(对于函数对象和类)和虚拟调用会引入运行时的开销。因此此时就须要内联函数,来消除这种开销。 官方使用了lock这个函数来解释这个问题:

fun <T> lock(lock: Lock, body: () -> T): T {
  lock.lock()
  try {
    return body()
  }
  finally {
    lock.unlock()
  }
}
复制代码

正常咱们会这样调用这个函数lock(l) { foo() }。若是不加修饰的话,由于高阶函数的存在,会形成大量的对象对建立出来,因此咱们这里咱们须要用内联的方式消除这种额外。

// 使用内联,代码编译后将变成这样
l.lock()
try {
    foo()
}
finally {
    l.unlock()
}
复制代码

可能有小伙伴在这里会有一些懵逼,一时没有理解内联作了什么。接下来用三个函数,解释一下这个问题:

// 一个函数中,调用了另外一个函数。虚拟机势必要为funtion1的调用,增长不少开销。
fun funtion(){
    var a =1+1+1
    funtion1()
}

fun funtion1(){
    var aa =1+1+1
    var bb =1+1+1
    var cc =1+1+1
}

// 那么咱们使用内联以后,就变成了这样:
fun funtion(){
    var a =1+1+1
    var aa =1+1+1
    var bb =1+1+1
    var cc =1+1+1
}
复制代码

咱们能够看到,内联以后,咱们funtion1函数中的实现,仿佛就在funtion函数中同样。所以就相应的下降了这部分的消耗。

内联可能致使生成的代码增长;不过若是咱们使用得当(即避免内联过大函数),性能上会有所提高,尤为是在循环中的“超多态(megamorphic)”调用处。

尾声

OK,到此差很少关于Kotlin的基于语法的内容差很少就要告一段落了。接下来的文章基本就会围绕着Android中的Kotlin进行开展。 但愿各位小伙伴们能够从中有所收获呦~

我是一个应届生,最近和朋友们维护了一个公众号,内容是咱们在从应届生过渡到开发这一路所踩过的坑,以及咱们一步步学习的记录,若是感兴趣的朋友能够关注一下,一同加油~

我的公众号:IT面试填坑小分队
相关文章
相关标签/搜索