Wazing


无醇酒美人,不愿来此人间,无快剑挚友,不愿老此江湖。


如何移除你的Kotlin代码中所有!!

[TOC]

原标题:How to remove all !! from your Kotlin code

原文地址:https://android.jlelse.eu/how-to-remove-all-from-your-kotlin-code-87dc2c9767fb

原文作者:David Vávra

作者本篇源码地址:https://gist.github.com/davidvavra

如何移除你的Kotlin代码中所有!!

空安全特性是Kotlin语言最好语法特性之一。它让你在语言层面来考虑可空性,以致于你可以避免很多在Java中常见的隐藏空指针异常。然而当你通过工具自动将Java代码转化成Kotlin时,你会发现有很多的 !! 标记出现。按道理在你的代码中不应该有任何的 !! 出现,除非它是一个快速原型。并且我相信这是对的,因为 !! 的出现基本上的意味着 “你这里有可能存在未处理的 KotlinNullPointerException.

Kotlin有一些智能的机制去避免这些空指针的问题,但是弄明白它并不是那么直接和容易。这里有6种方法去做到这一点:

1)使用val而不是var

Kotlin让你在语言层面思考不变性,这点看起来很不错。 val是只读的,var是可变的。 建议尽可能多地使用只读属性。 它们是线程安全的,并且在函数式编程方面效果很好。 如果你使用它们作为不可变项,你不必关心可空性。 只要注意val可以实际上是可变的。

2)使用lateinit

有时你不能使用不可变属性。例如,它发生在Android上,当一些属性在onCreate()调用中被初始化时。对于这些情况,Kotlin具有称为lateinit的语言功能。

它可以让你取代这个:

private var mAdapter: RecyclerAdapter<Transaction>? = null

override fun onCreate(savedInstanceState: Bundle?) {
   super.onCreate(savedInstanceState)
   mAdapter = RecyclerAdapter(R.layout.item_transaction)
}

fun updateTransactions() {
   mAdapter!!.notifyDataSetChanged()
}

等同这个:

private lateinit var mAdapter: RecyclerAdapter<Transaction>

override fun onCreate(savedInstanceState: Bundle?) {
   super.onCreate(savedInstanceState)
   mAdapter = RecyclerAdapter(R.layout.item_transaction)
}

fun updateTransactions() {
   mAdapter.notifyDataSetChanged()
}

请注意,访问未初始化的lateinit属性将导致UninitializedPropertyAccessException

遗憾的是lateinit不适用于原始数据类型,比如像Int这样的基本数据类型。对于原始类型,您可以使用这样的代理:

private var mNumber: Int by Delegates.notNull<Int>()

3) 使用let函数

这是Kotlin代码中的常见编译时错误:

img

令我恼火的是:我知道这个可变属性在空检查后不能改变。许多开发人员通过以下方式快速修复它:

private var mPhotoUrl: String? = null

fun uploadClicked() {
    if (mPhotoUrl != null) {
        uploadPhoto(mPhotoUrl!!)
    }
}

但是有一个使用let函数的优雅解决方案:

private var mPhotoUrl: String? = null

fun uploadClicked() {
    mPhotoUrl?.let { uploadPhoto(it) }
}

4)创建全局函数来处理更复杂的案例

let是一个简单的空检查的很好的替代品,但可能会有更复杂的情况。例如:

if (mUserName != null && mPhotoUrl != null) {
   uploadPhoto(mUserName!!, mPhotoUrl!!)
}

你可以嵌套两个let调用,但那不会很好读。在Kotlin中,你可以拥有全局可访问的功能,所以你可以轻松地构建你需要的功能,就像这样使用:

ifNotNull(mUserName, mPhotoUrl) {
   userName, photoUrl ->
   uploadPhoto(userName, photoUrl)
}

源码为:

fun <T1, T2> ifNotNull(value1: T1?, value2: T2?, bothNotNull: (T1, T2) -> (Unit)) {
   if (value1 != null && value2 != null) {
       bothNotNull(value1, value2)
   }
}

5)使用Elvis操作符

如果您有空情况下的回退值,则Elvis运算符非常好。所以你可以取代这个:

fun getUserName(): String {
   if (mUserName != null) {
       return mUserName!!
   } else {
       return "Anonymous"
   }
}

等同这个:

fun getUserName(): String {
   return mUserName ?: "Anonymous"
}		

6)按自己的条件崩溃

即使类型必须为空,仍然有些情况下您知道某些内容不能为空。 如果它为空,这是代码中的一个bug,你应该知道它。 但是抛弃!!,系统会给你一个很难debug的KotlinNullPointerException。 使用内置函数requireNotNullcheckNotNull与随附的异常消息以便于调试。

例如:

uploadPhoto(intent.getStringExtra("PHOTO_URL")!!)

等同如:

uploadPhoto(requireNotNull(intent.getStringExtra("PHOTO_URL"), { "Activity parameter 'PHOTO_URL' is missing" }))

结论

如果你按照这6个提示,你可以删除所有!!来自你的Kotlin代码。您的代码将更安全,更可调试,更清洁。如果您了解更多关于如何处理无效安全的方法,请在评论中告诉我们。

最近的文章

Kotlin多层循环跳出

​ 在Java中,使用break可以跳出循环,默认情况下是跳出最近一层的循环,假如我们要跳出多层循环怎么办呢,Java替我们已经做好了这一点,就是用 循环标签 :即是对某个循环定义一个名字,然后在 break 后面加上这个名字,当符合 break 条件时,程序就会跳到规定的循环。out:for (int i = 0; i < 3; i++) { System.out.println("i = " + i); for (int j = 0; j < 3; j++) {...…

Android Kotlin继续阅读
更早的文章

使用 Kotlin 进行 Android 开发

[TOC]KotlinKotlin是一种在Java虚拟机上运行的静态类型编程语言,它也可以被编译成为JavaScript源代码。它主要是由俄罗斯圣彼得堡的JetBrains开发团队所发展出来的编程语言,其名称来自于圣彼得堡附近的科特林岛。2012年1月,著名期刊《Dr. Dobb’s Journal》中Kotlin被认定为该月的最佳语言。虽然与Java语法并不兼容,但Kotlin被设计成可以和Java代码相互运作,并可以重复使用如Java集合框架等的现有Java类库。 摘自 Kotlin...…

Android Kotlin继续阅读