原标题: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语言最好语法特性之一。它让你在语言层面来考虑可空性,以致于你可以避免很多在Java中常见的隐藏空指针异常。然而当你通过工具自动将Java代码转化成Kotlin时,你会发现有很多的 !!
标记出现。按道理在你的代码中不应该有任何的 !!
出现,除非它是一个快速原型。并且我相信这是对的,因为 !!
的出现基本上的意味着 “你这里有可能存在未处理的 KotlinNullPointerException
.
Kotlin有一些智能的机制去避免这些空指针的问题,但是弄明白它并不是那么直接和容易。这里有6种方法去做到这一点:
val
而不是var
Kotlin让你在语言层面思考不变性,这点看起来很不错。 val
是只读的,var
是可变的。 建议尽可能多地使用只读属性。 它们是线程安全的,并且在函数式编程方面效果很好。 如果你使用它们作为不可变项,你不必关心可空性。 只要注意val
可以实际上是可变的。
lateinit
有时你不能使用不可变属性。例如,它发生在Android上,当一些属性在onCreate()调用中被初始化时。对于这些情况,Kotlin具有称为lateinit
的语言功能。
它可以让你取代这个:
private var mAdapter: RecyclerAdapter? = null
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
mAdapter = RecyclerAdapter(R.layout.item_transaction)
}
fun updateTransactions() {
mAdapter!!.notifyDataSetChanged()
}
等同这个:
private lateinit var mAdapter: RecyclerAdapter
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>()
let
函数这是Kotlin代码中的常见编译时错误:
令我恼火的是:我知道这个可变属性在空检查后不能改变。许多开发人员通过以下方式快速修复它:
private var mPhotoUrl: String? = null
fun uploadClicked() {
if (mPhotoUrl != null) {
uploadPhoto(mPhotoUrl!!)
}
}
但是有一个使用let
函数的优雅解决方案:
private var mPhotoUrl: String? = null
fun uploadClicked() {
mPhotoUrl?.let { uploadPhoto(it) }
}
let
是一个简单的空检查的很好的替代品,但可能会有更复杂的情况。例如:
if (mUserName != null && mPhotoUrl != null) {
uploadPhoto(mUserName!!, mPhotoUrl!!)
}
你可以嵌套两个let
调用,但那不会很好读。在Kotlin中,你可以拥有全局可访问的功能,所以你可以轻松地构建你需要的功能,就像这样使用:
ifNotNull(mUserName, mPhotoUrl) {
userName, photoUrl ->
uploadPhoto(userName, photoUrl)
}
源码为:
fun ifNotNull(value1: T1?, value2: T2?, bothNotNull: (T1, T2) -> (Unit)) {
if (value1 != null && value2 != null) {
bothNotNull(value1, value2)
}
}
Elvis
操作符如果您有空情况下的回退值,则Elvis
运算符非常好。所以你可以取代这个:
fun getUserName(): String {
if (mUserName != null) {
return mUserName!!
} else {
return "Anonymous"
}
}
等同这个:
fun getUserName(): String {
return mUserName ?: "Anonymous"
}
即使类型必须为空,仍然有些情况下您知道某些内容不能为空。 如果它为空,这是代码中的一个bug,你应该知道它。 但是抛弃!!
,系统会给你一个很难debug的KotlinNullPointerException。 使用内置函数requireNotNull或checkNotNull与随附的异常消息以便于调试。
例如:
uploadPhoto(intent.getStringExtra("PHOTO_URL")!!)
等同如:
uploadPhoto(requireNotNull(intent.getStringExtra("PHOTO_URL"), { "Activity parameter 'PHOTO_URL' is missing" }))
如果你按照这6个提示,你可以删除所有!!
来自你的Kotlin代码。您的代码将更安全,更可调试,更清洁。如果您了解更多关于如何处理无效安全的方法,请在评论中告诉我们。