kotlin中的使用小技巧總結

1.kotlin中lateinit和by lazy的區別

  • lazy { ... }只能被用在被val修飾的變量上,而lateinit只能被用var修飾的變量上,由於被lateinit修飾的字段沒法被編譯爲一個final字段、所以沒法保證它的不可變性。
  • lateinit修飾的變量有一個幕後字段用來存儲它的值,而by lazy { ... }建立了一個包含by lazy { ... }中代碼返回值的實例對象,實例對象持有這個值並生成一個能夠在實例對象中調用的這個值的getter。因此若是你須要在代碼中使用幕後字段的話,使用lateinit
  • lateinit修飾的變量能夠在對象(代碼)的任何地方進行初始化,並且同一個類的不一樣對象能夠對這個變量進行屢次的初始化(賦值)。可是,對於by lazy { ... }修飾的變量,只擁有惟一一個聲明在{}中的初始化構造器,若是你想要修改它,你只能經過在子類中覆寫的方式來修改它的值。

    因此,若是你想要你的屬性在其餘地方以不是你事先定義好的值初始化的話,使用lateinitjava

  • by lazy { ... }的初始化默認是線程安全的,而且能保證by lazy { ... }代碼塊中的代碼最多被調用一次。而lateinit var默認是不保證線程安全的,它的狀況徹底取決於使用者的代碼。
  • Lazy實例是有值的,這個值能夠被存儲、傳遞和使用。可是,被lateinit var修飾的變量不存儲任何多餘的運行時狀態,只有值還未被初始化的null值。
  • 若是你持有一個Lazy實例的引用,你可使用它的isInitialized()方法來判斷它是否已經被初始化。從Kotlin1.2開始,你也可使用方法引用的方式來獲取這個值。
  • by lazy { ... }中傳遞的lambda表達式可能會捕獲它的閉包中使用的上下文的引用,引用會一直被持有直到變量被初始化。所以這樣可能會致使內存泄漏,因此仔細考慮你在lambda表達式中使用的值是否合理。

原文連接:https://stackoverflow.com/que...ios

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 這個關鍵字來定義全局變量,舉個栗子:app

lateinit var zhuJ: ZhuJ

當這樣定義全局變量的時候就無需設置爲可空了,好比安卓項目中的 adapter ,我們確定能確認會賦值,不會爲空,那麼就可使用 lateinit 了。jvm

這塊須要注意的是,即便我們以爲不會爲空,但確定會有特殊狀況須要進行判斷,須要進行判斷的話要使用 isInitialized ,使用方法以下:ide

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...

相關文章
相關標籤/搜索