关于kotlin:kotlin中的使用小技巧总结

5次阅读

共计 7494 个字符,预计需要花费 19 分钟才能阅读完成。

1.kotlin 中 lateinit 和 by lazy 的区别

  • lazy {...}只能被用在被 val 润饰的变量上,而 lateinit 只能被用 var 润饰的变量上,因为被 lateinit 润饰的字段无奈被编译为一个 final 字段、因而无奈保障它的不可变性。
  • lateinit 润饰的变量有一个幕后字段用来存储它的值,而 by lazy {...} 创立了一个蕴含 by lazy {...} 中代码返回值的实例对象,实例对象持有这个值并生成一个能够在实例对象中调用的这个值的getter。所以如果你须要在代码中应用幕后字段的话,应用lateinit
  • lateinit 润饰的变量能够在对象(代码)的任何中央进行初始化,而且同一个类的不同对象能够对这个变量进行屡次的初始化(赋值)。然而,对于 by lazy {...} 润饰的变量, 只领有惟一一个申明在 {} 中的初始化结构器,如果你想要批改它,你只能通过 在子类中覆写 的形式来批改它的值。

    所以,如果你想要你的属性在其余中央以不是你当时定义好的值初始化的话,应用lateinit

  • by lazy {...}的初始化默认是线程平安的,并且能保障 by lazy {...} 代码块中的代码最多被调用一次。而 lateinit var 默认是不保障线程平安的,它的状况齐全取决于使用者的代码。
  • Lazy实例是有值的,这个值能够被存储、传递和应用。然而,被 lateinit var 润饰的变量不存储任何多余的运行时状态,只有值还未被初始化的 null 值。
  • 如果你持有一个 Lazy 实例的援用,你能够应用它的 isInitialized() 办法来判断它是否曾经被初始化。从 Kotlin1.2 开始,你也能够应用办法援用的形式来获取这个值。
  • by lazy {...}中传递的 lambda 表达式可能会捕捉它的闭包中应用的上下文的援用,援用会始终被持有直到变量被初始化。因而这样可能会导致内存透露,所以认真思考你在 lambda 表达式中应用的值是否正当。

原文链接:https://stackoverflow.com/que…

Here are the significant differences between lateinit var and by lazy {...} delegated property:

lazy {...} delegate can only be used for val properties, whereas lateinit can only be applied to vars, because it can't be compiled to a final field, thus no immutability can be guaranteed;

lateinit var has a backing field which stores the value, and by lazy {...} creates a delegate object in which the value is stored once calculated, stores the reference to the delegate instance in the class object and generates the getter for the property that works with the delegate instance. So if you need the backing field present in the class, use lateinit;

In addition to vals, lateinit cannot be used for non-nullable properties and Java primitive types (this is because of null used for uninitialized value);

lateinit var can be initialized from anywhere the object is seen from, e.g. from inside a framework code, and multiple initialization scenarios are possible for different objects of a single class. by lazy {...}, in turn, defines the only initializer for the property, which can be altered only by overriding the property in a subclass. If you want your property to be initialized from outside in a way probably unknown beforehand, use lateinit.

Initialization by lazy {...} is thread-safe by default and guarantees that the initializer is invoked at most once (but this can be altered by using another lazy overload). In the case of lateinit var, it's up to the user's code to initialize the property correctly in multi-threaded environments.

A Lazy instance can be saved, passed around and even used for multiple properties. On contrary, lateinit vars do not store any additional runtime state (only null in the field for uninitialized value).

If you hold a reference to an instance of Lazy, isInitialized() allows you to check whether it has already been initialized (and you can obtain such instance with reflection from a delegated property). To check whether a lateinit property has been initialized, you can use property::isInitialized since Kotlin 1.2.

A lambda passed to by lazy {...} may capture references from the context where it is used into its closure.. It will then store the references and release them only once the property has been initialized. This may lead to object hierarchies, such as Android activities, not being released for too long (or ever, if the property remains accessible and is never accessed), so you should be careful about what you use inside the initializer lambda.

Also, there's another way not mentioned in the question: Delegates.notNull(), which is suitable for deferred initialization of non-null properties, including those of Java primitive types.

在我的项目中的理论使用示例:

1. 实例化对话框

注意事项

1.lateinit 中的未被初始化的值为 null,留神应用前查看。

lateinit

这个关键字其实应用的很多,在定义全局变量为空的时候并不是非得用问号设置为可空的,如果你能够确定肯定不为空能够应用 lateinit 这个关键字来定义全局变量,举个栗子:

lateinit var zhuJ: ZhuJ

当这样定义全局变量的时候就无需设置为可空了,比方安卓我的项目中的 adapter,咱们必定能确认会赋值,不会为空,那么就能够应用 lateinit 了。

这块须要留神的是,即便咱们感觉不会为空,但必定会有非凡状况须要进行判断,须要进行判断的话要应用 isInitialized,应用办法如下:

if (::zhuJ.isInitialized){// 判断是否曾经进行赋值}

2.!! 和?. 的区别

**”?” 加在变量名后,零碎在任何状况不会报它的空指针异样。
“!!” 加在变量名后,如果对象为 null,那么零碎肯定会报异样!**

?: 对象 A ?: 对象 B 表达式:

意思为,当对象 A 值为 null 时,那么它就会返回前面的对象 B。

foo?:bar ==>

if(foo!=bar){foo}else{bar}

foo?.bar ==>

if(foo!=null){foo.bar}
else if(foo==null){null}

3. with、let、apply、run 的区别

Kotlin 之 let,apply,run,with 等函数区别 2

应用实例 1:

// 一般应用
var user = User()
user.id = 1
user.name = "test1"
user.hobbies = listOf("aa", "bb", "cc")
println("user = $user")


user.let {
    it.id = 2
    it.name = "test2"
    it.hobbies = listOf("aa", "bb", "cc")
}
println("user = $user")

user.also {
    it.id = 3
    it.name = "test3"
    it.hobbies = listOf("aa", "bb", "cc")
}
println("user = $user")

user.apply {
    id = 2
    name = "test2"
    hobbies = listOf("aa", "bb", "cc")
    Date()}
println("user = $user")

user.run {
    id = 3
    name = "test3"
    hobbies = listOf("aa", "bb", "cc")
    Date()}
println("user = $user")

with(user) {
    id = 4
    name = "test4"
    hobbies = listOf("aa", "bb", "cc")
    Date()}
println("user = $user")

应用实例 2:

一个 http 的 response 构造体。

class Resp<T> {
    var code: Int = 0
    var body: T? = null
    var errorMessage: String? = null

    fun isSuccess(): Boolean = code == 200

    override fun toString(): String {return "Resp(code=$code, body=$body, errorMessage=$errorMessage)"
    }
}

在解决网络数据的时候,须要各种判断,比方。

fun main(args: Array<String>) {var resp: Resp<String>? = Resp()

    if (resp != null) {if (resp.isSuccess()) {
            // do success
            println(resp.body)
        } else {
            // do fail 
            println(resp.errorMessage)
        }
    }
}

// 用了操作符号后

fun main(args: Array<String>) {var resp: Resp<String>? = Resp()

//    if (resp != null) {//        if (resp.isSuccess()) {
//            // do success
//            println(resp.body)
//        } else {//            println(resp.errorMessage)
//        }
//    }

    resp?.run {if (isSuccess()) {
            // do success
            println(resp.body)
        } else {println(resp.errorMessage)
        }
    }

    resp?.apply {if (isSuccess()) {
            // do success
            println(resp.body)
        } else {println(resp.errorMessage)
        }
    }

    resp?.let {if (it.isSuccess()) {
            // do success
            println(it.body)
        } else {println(it.errorMessage)
        }
    }

    resp?.also {if (it.isSuccess()) {
            // do success
            println(it.body)
        } else {println(it.errorMessage)
        }
    }
}

4.as 运算符和 as? 运算符

as 运算符用于执行援用类型的显式类型转换。如果要转换的类型与指定的类型兼容,转换就会胜利进行;

如果类型不兼容,应用 as? 运算符就会返回值 null。在 Kotlin 中,父类是禁止转换为子类型的。

我的项目中使用

    private fun initBuyDialog(): BuyDialog {
        //as?如果不兼容 会返回为 null  ?: 为空时会初始化
        return supportFragmentManager.findFragmentByTag(BuyDialog.TAG) as? BuyDialog ?: BuyDialog()}

5. kotlin 中对于 null 的解决

Kotlin 异样:method kotlin.jvm.internal.Intrinsics.checkParameterIsNotNull

场景:java 代码调用 kotlin 办法,kotlin 办法参数后边不加?,且实参为 null

fun kotlinFun(arg1:String,...)

java 代码中调用 kotlin 办法 kotlinFun,如果参数传 null,就会间接抛如题异样

起因:kotlin 的空平安机制,如果参数后边不加?, 则该参数为非空参数,实参为 null 就会抛如题异样.

解决办法kotlin 办法参数加?, 承受 null 空参数

fun kotlinFun(arg1:String?,...)

对于服务器返回的 null 值的优雅解决:

val l:Int = if(b!=null){b.length}else{-1}

// 等价于

val l = b?.length?:-1

如果 b 为 null 返回 -1,否则返回 b.length。

var b: String? = "abc"
val l = b!!.length()

它的返回值有两种可能,如果 b 不为 null,返回 b.length(),否则,抛出一个空指针异样,如果 b 为 null,你不想返回 null,而是抛出一个空指针异样,你就能够应用它。

空援用的调用,上面还有第三种方面来调用它的成员函数和变量。

6. 优雅的解决空字符串

重点:ifEmpty{}

当字符串为空字符串的时候,返回一个默认值,常见的写法如下所示:

val target = ""val name = if (target.isEmpty())"dhl" else target

其实有一个更简洁的办法,可读性更强,应用 ifEmpty 办法,当字符串为空字符串时,返回一个默认值,如下所示。

val name = target.ifEmpty {"dhl"}

其原理跟咱们应用 if 表达式是一样的,来剖析一下源码。

public inline fun <C, R> C.ifEmpty(defaultValue: () -> R): R where C : CharSequence, C : R =
    if (isEmpty()) defaultValue() else this

ifEmpty 办法是一个扩大办法,承受一个 lambda 表达式 defaultValue,如果是空字符串,返回 defaultValue,否则不为空,返回调用者自身。

除了 ifEmpty 办法,Kotlin 库中还封装很多其余十分有用的字符串,例如:将字符串转为数字。常见的写法如下所示:

val input = "123"
val number = input.toInt()

其实这种写法存在肯定问题,假如输出字符串并不是纯数字,例如 123ddd 等等,调用 input.toInt() 就会报错,那么有没有更好的写法呢?如下所示。

val input = "123"
//    val input = "123ddd"
//    val input = ""
val number = input.toIntOrNull() ?: 0

链接:https://juejin.im/post/5f0747…

7.sealed

这个关键字之前始终没有进行应用,它用来润饰类,含意为 密封类,之前始终没搞懂这个密封类有啥说啥用,这两天好好看了下,我了解的作用就是:能够使代码更加紧密。

这样说感觉有点形象,再举个栗子吧,平时咱们在封装一些工具的时候个别只会有胜利和失败,咱们的做法个别是定义一个接口,而后再定义一个胜利类和失败类来实现这个接口,最初再进行判断:

class Success(val msg: String) : Result
class Fail(val error: Throwable) : Result

fun getResult(result: Result) = when (result) {
    is Success -> result.msg
    is Fail -> result.error.message
    else -> throw IllegalArgumentException()}

下面代码都是咱们个别写的,尽管只有两种状况,然而必须再写 else 来进行判断,如果不写的话编译就过不了。但如果应用密封类的话就不会有这种状况呈现:

sealed class Results
class Success(val mag: String) : Results()
class Failure(val error: Exception) : Results()

fun getMessage(result: Results) {when (result) {
        is Success -> {println(result.mag)
        }
        is Failure -> {println(result.error.toString())
        }
    }
}

不仅不必再写 else,而且在进行 when 判断时,kotlin 会查看条件是否蕴含了所有的子类,如果没有会提醒你加上,这样就大大提高的代码的鲁棒性,也不会呈现没有判断到的问题。
链接:https://juejin.im/post/5eeffd…

正文完
 0