摘要:将一个函数标记为 inline ,你告诉编译器将函数代码直接复制到调用点,再加上 reified 关键字,类型信息会在调用点保留下来,从而在运行时可以访问。
如果你是一名安卓开发者,你很可能喜爱 Kotlin 语言。它简洁、安全,改变了我们以往使用 Java 的开发方式。
最新的跨平台 Compose,已经完全基于 Kotlin 开发了。
那么,如何像一个 Kotlin 老手写代码呢?
或者当你去面试,跟面试官说,我是个十年老手!
下面,我会展示一些 Kotlin 用法,让你的代码更简洁、更易读、更高效,让你看起来像一个写了十年 Kotlin 代码的老手。
还记得 java 的类型擦除吗?
因为类型擦除的关系,在实际运行的时候,你是无法知道一个泛型的确切类型。
fun isType(obj: Any) = obj is TKotlin 中带有实化类型参数的内联函数巧妙地解决了这个问题。
将一个函数标记为 inline ,你告诉编译器将函数代码直接复制到调用点,再加上 reified 关键字,类型信息会在调用点保留下来,从而在运行时可以访问。
上述代码稍作修改:
inline fun isType(obj:Any) = obj is Tprintln(isType("Kotlin"))println(isType(2))我们甚至可以使用泛型去创建实例:
inline fun newOne: T { return T::class.java.newInstance}val a:String = newOne这对于 Json 序列化等场景而言,非常方便:
inline fun Gson.fromJson(json: String): T = this.fromJson(json, T::class.java)val user = gson.fromJson("""{"name": "Prakash"}""")我们都喜欢扩展函数。能够向现有类添加新函数而无需继承它们,这是 Kotlin 的强大功能之一。
例如 Kotlin 官方提供的:
str.isnullOrBlank该方法可以判断字符串是否为 null 、为空字符,或者仅由空白字符组成。
但你知道吗,除了扩展函数以外,还有扩展属性。
扩展属性允许你给任何类“添加”新的只读属性。它们的行为与普通属性无异,但其值是通过一个 getter 方法来计算的。
val String.firstChar: Char get = this[0]println("Kotlin".firstChar)这对于创建简洁实用的逻辑非常理想。相比于像 String.getFirstChar 这样的函数调用,使用更具可读性的属性访问 String.firstChar 让你的代码更具语义性,尤其是在为无法修改的第三方类或框架类添加简单计算值时更是如此 。
这在 Compose 中比较常见,Compose 中可以这样使用尺寸:
3.dp5.sp我只能说,还有谁!
你肯定用过 let 、apply 、run 、with 和 also 。但很容易将它们视为可以互换的。它们真正的强大之处在于依据其特定意图来使用它们。每一个函数都旨在以尽可能简洁的方式处理一种常见的编码模式。
以 apply 为例。它的作用是配置一个对象。它在一个对象上运行一段代码块,并返回对象本身。这对于设置复杂对象或构建器来说非常合适。
让我们将它与 also 结合使用,also 用于在不改变对象的情况下执行其它操作(比如日志记录或调试)。
data class Config(var host: String = "", var port: Int = 0)val config = Config.apply { host = "localhost" port = 8080 }.also { println("Config created: $it")使用恰当的作用域函数能让你的代码意图一目了然。
这就像是为每项小任务都准备了专门的工具。
如果在 IO 操作中使用相关作用域函数,代码会非常方便:
File("").inputStream.use { ios ->val os = File("").outputStream.use { os ->var len = 0 val bytes = byteArray(1024) val byteBuffer = (File("").writer) while (ios.read(bytes).also { len = it } > -1) { os.write(bytes, 0, len) } } }这一点可能会让你感觉像是在重写语言本身,而这正是它强大的原因。
运算符重载允许你为自己的自定义类型定义诸如 + 、- 、* 甚至数组访问 等标准运算符的含义。
你无需编写 a.add(b) ,只需写 a + b 即可。这使得诸如向量数学运算、矩阵运算或自定义数据结构相关的代码变得极其直观且易于阅读。
data class Vec2(val x: Int, val y: Int) { operator fun plus(other: Vec2) = Vec2(x + other.x, y + other.y) operator fun times(times: Int): Vec2 { return Vec2(x * times, y * times) }}val a = Vec2(1, 2) val b = Vec2(3, 4)val c = a +println(c) println(c * 4)如果经过深思熟虑后使用,这能够产生极具表现力的 API 和 DSL,使用起来会感觉非常自然。
有时你有一段逻辑,你想写一个辅助方法,而该辅助方法其实只在一个函数内部使用。在 Java 中,你可能会把它设为类的私有方法。但这仍然会用一个用途非常有限的函数使类的作用域变得杂乱。因为通常情况下,能提出方法的,多半是在其他地方能够复用的,而如果只在一个函数内部复用,提一个类的私有方法,多少有点大材小用了。
Kotlin 允许你在其他函数内部声明函数。
这是一个实现封装的出色工具。辅助函数只存在于需要它的地方,不会意外地从其他地方被调用。它还可以访问外部函数的局部变量,这能进一步简化操作。
fun validateAndSaveUser(user: User) {fun isValidEmail(email: String): Boolean { return email.isNotBlank && email.contains("@") }if (isValidEmail(user.email)) { } }我通常在 Compose 中这么用
@Composablefun HomePage( entry: NavBackStackEntry, vm: HomeViewModel = viewModel, scope: CoroutineScope = rememberCoroutineScope,) { fun gotoHomeScreen { }fun previewWork(ui: WorksUIItem) { }fun goSettingPage { }BackHandler { gotoHomeScreen } HomeTitleBar( index = homePager.currentPage, onSettingPage = ::goSettingPage, onPreview = ::previewWork )}这是一个简单的特性,它可以帮助你将复杂的函数分解成更小的、可管理的且完全封装的部分,而不会污染类的命名空间。
这是一种一次性将对象解包为多个变量的简洁方法。它开箱即用地适用于数据类、二元组、三元组,甚至映射项。
不用这样写:
data class User(val name: String, val age: Int)val user = getUser val name = user.name val age = user.age你可以这样写:
val (name, age) = getUserKotlin 中自带的 Pair ,就支持这种写法:
val pair = Pair("Alice", 30)val (name, age) = pairprintln("Name: $name, Age: $age")Kotlin 的中缀表达式是一种特殊的函数调用方式,它允许你用更接近自然语言或数学表达式的语法来调用函数,使代码更清晰易读。
当你想创建一个 Pair 的时候,你可以使用两种写法:
val annaBook = Pair("Anna's diary", 30.49f)val book = "Anna's diary" to 30.49f当然,我们也可以让自己编写的类支持中缀表达式:
class Config { var host: String = "" var port: Int = 0 var password: String = ""}infix fun Config.host(value: String) { this.host = value }infix fun Config.port(value: Int) { this.port = value }infix fun Config.password(value: String) { this.password = value }fun main { val cfg = Config.also { it host "127.0.0.1" it port 8987 }cfg password "Pe*45U3n^bIha" }如果你在 MVI 架构模式中,对 Redux 方法应用了中缀表达式方法,那么你就会写出表达力极强的代码:
跳出常规用法,充分利用 Kotlin 的强大特性,善用这些强大工具,你的代码不仅实用,更将优雅而富有表现力,宛如 Kotlin 老手的手笔。
来源:墨码行者