在 Android 开发中使用常见的 Kotlin 代码实践
本主题重点介绍 Kotlin 语言在 Android 开发过程中最有用的一些方面。同时是我们普遍遵循的清晰硬性规则,而避免给出真人或工具无法简明地遵循的建议。
所有源文件都必须编码为 UTF-8。
来源标注:在 Android 开发中使用常见的 Kotlin 模式 | Android Developers
书接上篇:Android Kotlin知识汇总(一)编程语言
继承
您可以使用 class
关键字在 Kotlin 中声明类。您可以通过在子类与其父类之间使用 :
运算符来指明其继承关系。在以下示例中,LoginFragment
是 Fragment
的子类。
class LoginFragment : Fragment()
参数可Null
以问号 ?
声明某些变量为NULL。而在函数中参数类型以问号 ?
为后缀,则表示可以传递给这些参数的实际参数可以为 null值。
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
}
延迟初始化
在 Kotlin 中,您必须在声明对象时初始化对象的属性。如果要声明后推迟初始化,您可以使用 lateinit
推迟属性初始化。使用 lateinit
时,您应尽快初始化属性。示例如下:
class LoginFragment : Fragment() {
private lateinit var usernameEditText: EditText
private lateinit var loginButton: Button
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
usernameEditText = view.findViewById(R.id.username_edit_text)
loginButton = view.findViewById(R.id.login_button)
}
...
}
注意: 如果您在属性初始化之前对其进行访问,Kotlin 会抛出
UninitializedPropertyAccessException
。
SAM 转换
您可以通过实现 OnClickListener
接口来监听 Android 中的点击事件。因为始终接受 OnClickListener
作为参数,又因其有单一抽象方法 onClick()
。所以此实现在 Kotlin 中可以使用匿名函数来表示。
此过程称为单一抽象方法转换,简称 SAM 转换。SAM 转换可使代码明显变得更简洁。
loginButton.setOnClickListener {
//...
}
伴生对象(重点)
伴生对象有助于连接变量、函数和类定义,而无需引用该类的任何特定实例。
用于定义在概念上与某个类型相关但不与特定对象关联的变量或函数。伴生对象类似于对变量和方法使用 Java 的 static
关键字。
伴生常量,在 companion object
中作为有效常量的公开非 const
属性必须带有 @JvmField
注解,才能作为静态字段公开。
class LoginFragment : Fragment() {
companion object {
private const val TAG = "LoginFragment"
@JvmField val TAG = "LoginFragment"
}
}
伴生函数必须带有 @JvmStatic
注解才能作为静态方法公开。
如果没有该注解,则这些函数只能作为静态 Companion
字段中的实例方法使用。
class KotlinClass {
companion object {
@JvmStatic fun doWork() {}
}
}
属性委托
初始化属性时,您可能会重复 Android 的一些比较常见的模式,例如在 Fragment
中访问 ViewModel
。为避免过多的重复代码,您可以使用 Kotlin 的属性委托语法。
private val viewModel: LoginViewModel by viewModels()
属性委托提供了一种可在您的整个应用中重复使用的通用实现。属性委托使用反射会增加一些性能开销。这种代价换来的是简洁的语法,可让您节省开发时间。
平台Null类型
每当您用 Java 编写代码时,都应使用可为 null 性注释。这些注释对 Java 和 Kotlin 开发者都有帮助。声明成员变量 accessId
带有 @Nullable
注释,这表示它可以持有 null 值。于是,Kotlin 会将 accessId
视为 String?
。如需指明变量绝不能为 null,使用 @NonNull
注释。
public class Account implements Parcelable {
private final @Nullable String accessId;
public final @NonNull String name;
}
默认值的函数过载
函数中的参数具有默认值的函数必须使用 @JvmOverloads
。
class Greeting {
@JvmOverloads
fun sayHello(prefix: String = "Mr.", name: String) {
println("Hello, $prefix $name")
}
}
public class JavaClass {
public static void main(String... args) {
Greeting greeting = new Greeting();
greeting.sayHello("Bob");
}
}
处理可为 null 性
尽量避免使用非 null 断言运算符 !!,其表示
左侧内容视为非 null。如以下示例所示:
val account = Account("name", "type")
val accountName = account.name!!.trim()
//替换
val accountName = account.name?.trim()
如果它左侧表达式的结果为 null,则您的应用会抛出 NullPointerException
。更安全的选择是使用安全调用运算符 ?.
命名
如果源文件只包含一个顶级类,则文件名应为该类的名称(区分大小写)加上 .kt
扩展名。如果源文件包含多个顶级声明,则应选择一个可描述文件内容的名称(采用 PascalCase 大小写形式;如果文件名为复数,亦可采用驼峰命名法)并加上 .kt
扩展名。
// MyClass.kt
class MyClass { }
结构
.kt
文件由下面几部分组成(按顺序列出):
- 版权和/或许可标头(可选)
- 文件级注解
- package 语句
- import 语句
- 顶级声明
上述各部分用一个空白行隔开。
/*
* Copyright 2017 Google, Inc.
*
* ...
*/
大括号
when
分支以及具有不超过一个 else
分支且仅占一行的 if
表达式不需要大括号。
if (string.isEmpty()) return
val result =
if (string.isEmpty()) DEFAULT_VALUE else string
when (value) {
0 -> return
// …
}
除此以外,任何 if
、for
、when
分支、do
和 while
语句及表达式都需要大括号,即使主体为空或仅包含一个语句也是如此。
结构化并发
Kotlin 协程让异步代码像阻塞代码一样易于使用。协程可大幅简化后台任务管理,例如网络调用、本地数据访问等任务的管理。
下一篇,继续介绍 Kotlin协程解决的问题,从而让您能够编写出更清晰、更简洁的应用代码。