推薦使用 Kotlin 關鍵字 Reified
reified
:使抽象的東西更加具體或真實,非常推薦 Android 開發使用這個關鍵字。本文介紹 3 點特別的使用方式如下:
1. 不再需要傳引數 clazz
大部分的文章講解reified
的使用,都有提到這個點,比如我們定義實現一個擴充套件函式啟動 Activity,一般都需要傳Class<T>
引數:
// Function private fun <T : Activity> Activity.startActivity(context: Context, clazz: Class<T>) { startActivity(Intent(context, clazz)) } // Caller startActivity(context, NewActivity::class.java)
reified 方式
使用reified
,通過新增型別傳遞簡化泛型引數
// Function inline fun <reified T : Activity> Activity.startActivity(context: Context) { startActivity(Intent(context, T::class.java)) } // Caller startActivity<NewActivity>(context)
2. 不安全的轉換
Kotlin 中, 使用安全轉換操作符as?
,它可以在失敗時返回 null。實現如下函式,我們認為會安全地獲取資料或返回 null
// Function fun <T> Bundle.getDataOrNull(): T? { return getSerializable(DATA_KEY) as? T } // Caller val bundle: Bundle? = Bundle() bundle?.putSerializable(DATA_KEY, "Testing") val strData: String? = bundle?.getDataOrNull() val intData: Int? = bundle?.getDataOrNull() // Crash
然而,這個函式會出現 crash,如果獲得的資料不是它期望的型別。因此為了安全獲取資料,修改之前的函式如下:
// Function fun <T> Bundle.getDataOrNull(clazz: Class<T>): T? { val data = getSerializable(DATA_KEY) return if (clazz.isInstance(data)) { data as T } else { null } } // Caller val bundle: Bundle? = Bundle() bundle?.putSerializable(DATA_KEY, "Testing") val strData: String? = bundle?.getDataOrNull(String::class.java) val intData: Int? = bundle?.getDataOrNull(String::class.java) // Null
這種寫法不太友好,不僅在實現函式的方式上,而且還需要傳遞額外的clazz
引數。
reified 方式
使用reified
,簡化泛型引數和保證as?
型別轉換安全性
// Function private inline fun <reified T> Bundle.getDataOrNull(): T? { return getSerializable(DATA_KEY) as? T } // Caller val bundle: Bundle? = Bundle() bundle?.putSerializable(DATA_KEY, "Testing") val strData: String? = bundle?.getDataOrNull() val intData: Int? = bundle?.getDataOrNull() // Null
3. 不同的返回型別函式過載
實現一個函式計算 DP 到畫素,並返回一個 Int 或 Float。這種情況就會想到函式過載,如下所示:
fun Resources.dpToPx(value: Int): Float { return TypedValue.applyDimension( TypedValue.COMPLEX_UNIT_DIP, value.toFloat(), displayMetrics) } fun Resources.dpToPx(value: Int): Int { val floatValue: Float = dpToPx(value) return floatValue.toInt() }
但是,這將導致編譯時出錯。原因是,函式過載方式只能根據引數計數和型別不同,而不能根據返回型別。
reified 方式
使用reified
,可以實現不同的返回型別函式過載
inline fun <reified T> Resources.dpToPx(value: Int): T { val result = TypedValue.applyDimension( TypedValue.COMPLEX_UNIT_DIP, value.toFloat(), displayMetrics) return when (T::class) { Float::class -> result as T Int::class -> result.toInt() as T else -> throw IllegalStateException("Type not supported") } } // Caller val intValue: Int = resource.dpToPx(64) val floatValue: Float = resource.dpToPx(64)
從上面的3個例子中,很明顯reified
使 Kotlin 用起來更加友好。如果還有其他場景使用reified
的方法,歡迎分享。