重构为Kotlin
文章目录
准备User类
public class User {
private String firstName;
private String lastName;
public User(String firstName, String lastName) {
this.firstName = firstName;
this.lastName = lastName;
}
public String getFirstName() {
return firstName;
}
public void setFirstName(String firstName) {
this.firstName = firstName;
}
public String getLastName() {
return lastName;
}
public void setLastName(String lastName) {
this.lastName = lastName;
}
}
Java文件转换为Kotlin文件
选择XXX.java
文件,右键选择并将其转换为 Kotlin:Code -> Convert Java File to Kotlin File
声明可空性、val、var 和数据类
var
修饰可变变量
val
修饰不可变变量。val
类似于 Java 中的 final
关键字
Kotlin 文件的扩展名为 .kt
Kotlin 与 Java的一个区别在于:Kotlin 会明确指定变量能否接受 null 值。具体而言,其是通过在类型声明后附加"?
"以进行此项指定
数据类
关键字 :data
data class User(var firstName: String?, var lastName: String?)
在将此类标记为 data
类后,编译器便会自动创建 getter 和 setter。此外,其还会派生 equals()
、hashCode()
和 toString()
函数
data 类必须至少有一个主构造参数
多构造参数,例如:
data class User(var firstName: String, var lastName: String) {
constructor(firstName: String) : this(firstName,"Tom") {}
}
您可在官方文档中阅读有关构造函数的更多内容
相等性
Kotlin 分为两类相等性:
- 构成相等使用
==
运算符,并调用equals()
来确定两个实例是否相等。 - 引用相等使用 === 运算符,以检查两个引用是否指向同一对象
val user1 = User("Jane", "Doe")
val user2 = User("Jane", "Doe")
val structurallyEqual = user1 == user2 // true
val referentiallyEqual = user1 === user2 // false
您可在官方文档中阅读有关数据类的更多内容。
默认参数与命名参数
data class User(var firstName: String?, var lastName: String? = null)
// usage
val jane = User ("Jane") // same as User("Jane", null)
val joe = User ("John", "Doe")
默认参数 lastName
的默认值为 null
,也可以这样写
val john = User (firstName = "John", lastName = "Doe")
假如 firstName
将 null
用作其默认值,而 lastName
并不如此。在此情况下,由于默认参数位于未设默认值的参数之前,因此您必须使用命名参数来调用此函数,具体如下:
data class User(var firstName: String? = null, var lastName: String?)
// usage
val jane = User (lastName = "Doe") // same as User(null, "Doe")
val john = User ("John", "Doe")
若您的函数具有多个参数,请考虑使用命名参数
对象初始化、伴生对象和单一实例
public class Repository {
private static Repository INSTANCE = null;
private List<User> users = null;
public static Repository getInstance() {
if (INSTANCE == null) {
synchronized (Repository.class) {
if (INSTANCE == null) {
INSTANCE = new Repository();
}
}
}
return INSTANCE;
}
public Repository() {
User user1 = new User("Jane", "");
User user2 = new User("Jane", null);
User user3 = new User("Jane", "Doe");
users = new ArrayList<>();
users.add(user1);
users.add(user2);
users.add(user3);
}
public List<User> getUsers() {
return users;
}
public List<String> getFormattedUserName() {
List<String> userNames = new ArrayList<>(users.size());
for (User user :
users) {
String name;
if (user.getLastName() != null) {
if (user.getFirstName() != null) {
name = user.getFirstName() + " " + user.getLastName();
} else {
name = user.getLastName();
}
} else if (user.getFirstName() != null) {
name = user.getFirstName();
} else {
name = "Unknown";
}
userNames.add(name);
}
return userNames;
}
}
把Repository
类转换为 Kotlin
class Repository {
private var users: MutableList<User>? = null
fun getUsers(): List<User>? {
return users
}
val formattedUserName: List<String?>
get() {
val userNames: MutableList<String?> =
ArrayList(users!!.size)
for ((firstName, lastName) in users!!) {
var name: String
name = if (lastName != null) {
if (firstName != null) {
firstName + " " + lastName
} else {
lastName
}
} else if (firstName != null) {
firstName
} else {
"Unknown"
}
userNames.add(name)
}
return userNames
}
companion object {
private var INSTANCE: Repository? = null
val instance: Repository?
get() {
if (INSTANCE == null) {
synchronized(Repository::class.java) {
if (INSTANCE == null) {
INSTANCE = Repository()
}
}
}
return INSTANCE
}
}
init {
val user1 = User("Jane", "")
val user2 = User("Jane", null)
val user3 = User("Jane", "Doe")
users = ArrayList()
users!!.add(user1)
users!!.add(user2)
users!!.add(user3)
}
}
我们来看一下自动转换器执行了哪些操作:
-
添加了
init
代码块 -
static
字段现已加入companion object
代码块中 -
users
列表可为 null,因为该对象在声明时并未实例化 -
getFormattedUserNames()
方法现已成为一个属性 -
在对用户列表执行循环时,其语法与 Java 不同
等效规则:
Kotlin Java init块 构造函数 companion object 静态属性和静态方法 (static)
init块
在 Kotlin 中,主构造函数无法包含任何代码,因此初始化代码会置于 init
块中
class Repository private constructor() {
...
init {
val user1 = User("Jane", "")
val user2 = User("John", null)
val user3 = User("Anne", "Doe")
users = ArrayList()
users!!.add(user1)
users.add(user2)
users.add(user3)
}
}
init
代码大都用于处理属性的初始化。这项操作也可在声明属性时完成。例如,在 Kotlin 版本的 Repository
类中,我们可以看到,users 属性已在声明时进行初始化
private val users: MutableList<User>? = null
您可在官方文档中阅读有关初始化程序块的更多内容
Kotlin 的"静态"属性与"静态"方法
在 Java 中,我们会在字段或函数中使用 static
关键字,以指出此等字段或函数属于某个类,但不属于该类的某个实例。因此,我们在 Repository
类中创建了 INSTANCE
静态字段。在 Kotlin 中,companion object
代码块与此等效。您还可在此处声明静态字段和静态函数。转换器已创建 INSTANCE
字段并将其移至此处
处理单一实例
由于只需要Repository
类的一个实例,因此我们在 Java 中使用了单一实例模式
在 Kotlin 中,通过将 class
关键字替换为 object
,可以在编译器级别强制使用此模式
现在,我们可以移除私有构造函数和伴生对象(companion object
)
object Repository {
private var users: MutableList<User>? = null
fun getUsers(): List<User>? {
return users
}
val formattedUserName: List<String?>
get() {
val userNames: MutableList<String?> =
ArrayList(users!!.size)
for ((firstName, lastName) in users!!) {
var name: String?
name = if (lastName != null) {
if (firstName != null) {
firstName + " " + lastName
} else {
lastName
}
} else if (firstName != null) {
firstName
} else {
"Unknown"
}
userNames.add(name)
}
return userNames
}
init {
val user1 = User("Jane", "")
val user2 = User("Jane", null)
val user3 = User("Jane", "Doe")
users = ArrayList()
users!!.add(user1)
users!!.add(user2)
users!!.add(user3)
}
}
使用 object
类时,我们直接在对象上调用函数和属性,如下所示:
val users = Repository.getUsers()
您可在官方文档中阅读有关对象和伴生对象的更多内容
解构
Kotlin 允许使用名为解构声明的语法,将对象解构为多个变量。我们可以创建多个变量,并能独立使用这些变量
例如,数据类支持解构,因此自动转换器能够将 for
循环中的 User
对象解构。如此一来,我们便可直接处理 firstName
和 lastName
值,具体如下:
for ((firstName, lastName) in users) {
val name: String?
if (lastName != null) {
if (firstName != null) {
name = "$firstName $lastName"
}
...
您可在官方文档中阅读有关解构声明的更多内容
处理可空性
将 Repository
类转换为 Kotlin 时,自动转换器已将用户列表设为可为 null,这是由于我们在声明时并未将其初始化为对象。在 users
对象的所有使用情境中,我们都使用了非 null 断言运算符 !!
。该运算符可将所有变量转换为非 null 类型,并在值为 null 时抛出异常。使用 !!
时,存在运行时抛出异常的风险
建议您使用下列其中一种方法来处理可空性:
- 执行 null 检查 (
if (users != null) {...}
) - 使用 Elvis 运算符
?:
- 使用某些 Kotlin 标准函数
您可在官方文档中阅读有关空安全的更多内容
创建集合类实例时,您可以利用 Kotlin 所提供的多个帮助程序函数,让代码更易阅读而且更为灵活。本例中,我们将 MutableList
用于 users
,具体如下:
private val users: MutableList<User>? = null
为简单起见,我们可以使用 mutableListOf()
函数,提供列表元素类型,从 init
代码块中移除 ArrayList
构造函数调用,然后移除 users
属性的显式类型声明,具体如下:
private val users = mutableListOf<User>()
作出此项更改后,users
属性现已变为非 null,我们此时亦可移除所有不必要的 !!
运算符实例
运算符:
!! 非 null 断言运算符 ,该运算符可将所有变量转换为非 null 类型,并在值为 null 时抛出异常。使用 !!
时,存在运行时抛出异常的风险
?: Elvis 运算符 若左侧表达式不为 null,则 Elvis 运算符将返回该表达式,否则便会返回右侧表达式
若 user.firstName
不为 null,以下代码便会返回此值。若 user.firstName
为 null,该表达式将返回右侧值 "Unknown"
,具体如下:
if (lastName != null) {
...
} else {
name = firstName ?: "Unknown"
}
您可在官方文档中阅读有关 Elvis 运算符的更多内容
字符串模板和 if 表达式
借助字符串模板,Kotlin 将能简化 String
的处理工作。字符串模板允许在字符串声明内引用变量
使用 $
符号直接在字符串内引用变量名称,并可将表达式置于 { }
之间
// Java
name = user.getFirstName() + " " + user.getLastName();
// Kotlin
name = "${user.firstName} ${user.lastName}"
在 Kotlin 中,if
、when
、for
和 while
均为表达式,它们有返回值。
我们将if 语句的最后一行将被用于赋值。如下所示,我们可以更清晰地看到,此代码块的唯一目的便是初始化 name 值:
name = if (firstName != null) { // do something
firstName
}
// name = firstName
从 if 语句中移出赋值的做法很容易遭到滥用。请确保您的 if 代码块仅具有一个角色,且不会产生其他附带后果
接下来,页面会弹出警告,提示我们可以将 name
声明与赋值合并。同样,我们继续遵照该警告进行操作。由于可以推导出名称变量的类型,因此我们可以移除显式类型声明。现在,formattedUserNames
将如下所示:
val formattedUserNames: List<String>
get() {
val userNames = ArrayList<String>(users.size)
for ((firstName, lastName) in users) {
val name = if (lastName != null) {
if (firstName != null) {
"$firstName $lastName"
} else {
lastName ?: "Unknown"
}
} else {
firstName ?: "Unknown"
}
userNames.add(name)
}
return userNames
}
您可在官方文档中阅读有关 if
、when
、for
和 while
的更多内容