reified:使抽象的东西更加具体或真实,非常推荐 Android 开发使用这个关键字。本文介绍 3 点特别的使用方式如下:
大部分的文章讲解 reified 的使用,都有提到这个点,比如我们定义实现一个扩展函数启动 Activity,一般都需要传 Class 参数:
- // Function
- private fun
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
(context)
Kotlin 中, 使用安全转换操作符 as?,它可以在失败时返回 null。实现如下函数,我们认为会安全地获取数据或返回 null
- // Function
- fun
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
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
实现一个函数计算 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 的方法,欢迎分享。