3.2.6 类

原文链接: https://kotlinlang.org/docs/kotlin-tour-classes.html

3.2.6 类

Kotlin 通过类和对象支持面向对象编程。对象适合在程序中存储数据。类让你能够为某个对象声明一组特征。当你从类创建对象时,可以节省时间和精力,因为不必每次都重新声明这些特征。

要声明一个类,请使用 class 关键字:

1
class Customer

属性

类的对象的特征可以声明为属性。你可以通过以下方式为类声明属性:

  • 在类名之后的圆括号 () 中。
1
class Contact(val id: Int, var email: String)
  • 在由花括号 {} 定义的类体中。
1
2
3
class Contact(val id: Int, var email: String) {
    val category: String = ""
}

我们建议把属性声明为只读(val),除非在创建类实例之后确实需要修改它们。

你可以在圆括号中声明不带 val 或 var 的属性,但这些属性在创建实例之后无法访问。

注意: * 圆括号 () 中包含的内容称为类头。* 声明类属性时可以使用尾随逗号。

与函数参数一样,类属性也可以有默认值:

1
2
3
class Contact(val id: Int, var email: String = "example@gmail.com") {
    val category: String = "work"
}

创建实例

要从类创建对象,你需要使用构造器声明一个类的实例。

默认情况下,Kotlin 会自动创建一个带有类头中所声明参数的构造器。

例如:

1
2
3
4
5
class Contact(val id: Int, var email: String)

fun main() {
    val contact = Contact(1, "mary@gmail.com")
}

在这个示例中:

  • Contact 是一个类。
  • contact 是 Contact 类的一个实例。
  • id 和 email 是属性。
  • 使用默认构造器并传入 id 和 email 来创建 contact。

Kotlin 的类可以有多个构造器,也包括你自己定义的构造器。要了解更多关于声明多个构造器的内容,请参阅构造器。

访问属性

要访问实例的属性,请在实例名之后写出属性名,并用句点 . 连接:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
class Contact(val id: Int, var email: String)

fun main() {
    val contact = Contact(1, "mary@gmail.com")

    // Prints the value of the property: email
    println(contact.email)
    // mary@gmail.com

    // Updates the value of the property: email
    contact.email = "jane@gmail.com"

    // Prints the new value of the property: email
    println(contact.email)
    // jane@gmail.com
}

提示: 要把属性值拼接进字符串,你可以使用字符串模板($)。例如:kotlin println("Their email address is: ${contact.email}")

成员函数

除了把属性声明为对象的特征之外,你还可以用成员函数定义对象的行为。

在 Kotlin 中,成员函数必须声明在类体内部。要在实例上调用成员函数,请在实例名之后写出函数名,并用句点 . 连接。例如:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
class Contact(val id: Int, var email: String) {
    fun printId() {
        println(id)
    }
}

fun main() {
    val contact = Contact(1, "mary@gmail.com")
    // Calls member function printId()
    contact.printId()
    // 1
}

数据类

Kotlin 提供了数据类,它们特别适合存储数据。数据类具有与普通类相同的功能,但会自动附带一些额外的成员函数。这些成员函数让你可以轻松地把实例打印成易读的输出、比较类的实例、复制实例等等。由于这些函数是自动提供的,你不必为每个类花时间编写相同的样板代码。

要声明数据类,请使用 data 关键字:

1
data class User(val name: String, val id: Int)

Kotlin 编译器在生成成员函数时,只会使用主构造器内部定义的属性。如果你在数据类类体中声明属性,它们不会被包含在生成函数的输出中。

数据类最有用的预定义成员函数是:

| 函数 | 说明 | | toString() | 打印类实例及其属性的易读字符串。 | | equals() 或 == | 比较类的实例。 | | copy() | 通过复制另一个实例来创建类实例,可以选择性地修改部分属性。 |

关于如何使用每个函数,请参阅以下小节:

要打印类实例的易读字符串,你可以显式调用 toString() 函数,也可以使用会自动调用 toString() 的打印函数(println() 和 print()):

1
2
3
4
5
6
7
8
9
data class User(val name: String, val id: Int)

fun main() {
    val user = User("Alex", 1)

    // Automatically uses toString() function so that output is easy to read
    println(user)
    // User(name=Alex, id=1)
}

这在调试或生成日志时特别有用。

比较实例

要比较数据类实例,请使用相等运算符 ==:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
data class User(val name: String, val id: Int)

fun main() {
    val user = User("Alex", 1)
    val secondUser = User("Alex", 1)
    val thirdUser = User("Max", 2)

    // Compares user to second user
    println("user == secondUser: ${user == secondUser}")
    // user == secondUser: true

    // Compares user to third user
    println("user == thirdUser: ${user == thirdUser}")
    // user == thirdUser: false
}

复制实例

要创建数据类实例的完全副本,请在实例上调用 copy() 函数。

要创建数据类实例的副本并修改某些属性,请在实例上调用 copy() 函数,并把要替换的属性值作为函数参数传入。

例如:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
data class User(val name: String, val id: Int)

fun main() {
    val user = User("Alex", 1)

    // Creates an exact copy of user
    println(user.copy())
    // User(name=Alex, id=1)

    // Creates a copy of user with name: "Max"
    println(user.copy("Max"))
    // User(name=Max, id=1)

    // Creates a copy of user with id: 3
    println(user.copy(id = 3))
    // User(name=Alex, id=3)
}

创建实例的副本比修改原实例更安全,因为任何依赖原实例的代码都不会受到副本以及你对副本所做操作的影响。

关于数据类的更多信息,请参阅数据类。

本次导览的最后一章讲解 Kotlin 的空安全。

练习

习题 1

定义一个数据类 Employee,它有两个属性:一个表示姓名,另一个表示薪水。请确保薪水属性是可变的,否则年底你就拿不到加薪了!main 函数演示了如何使用这个数据类。

1
2
3
4
5
6
7
8
// Write your code here

fun main() {
    val emp = Employee("Mary", 20)
    println(emp)
    emp.salary += 10
    println(emp)
}
1
2
3
4
5
6
7
8
data class Employee(val name: String, var salary: Int)

fun main() {
    val emp = Employee("Mary", 20)
    println(emp)
    emp.salary += 10
    println(emp)
}

示例解答

习题 2

声明这段代码编译所需的其余数据类。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
data class Person(val name: Name, val address: Address, val ownsAPet: Boolean = true)
// Write your code here
// data class Name(...)

fun main() {
    val person = Person(
        Name("John", "Smith"),
        Address("123 Fake Street", City("Springfield", "US")),
        ownsAPet = false
    )
}
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
data class Person(val name: Name, val address: Address, val ownsAPet: Boolean = true)
data class Name(val first: String, val last: String)
data class Address(val street: String, val city: City)
data class City(val name: String, val countryCode: String)

fun main() {
    val person = Person(
        Name("John", "Smith"),
        Address("123 Fake Street", City("Springfield", "US")),
        ownsAPet = false
    )
}

示例解答

习题 3

为了测试你的代码,你需要一个能生成随机员工的生成器。请定义一个 RandomEmployeeGenerator 类,在类体中给出一个固定的候选姓名列表,并在类头中配置最低和最高薪水。在类体中定义 generateEmployee() 函数。同样,main 函数演示了如何使用这个类。

提示: 在这个练习中,你要导入一个包,以便使用 Random.nextInt() 函数。关于导入包的更多信息,请参阅包与导入。

提示 1

列表有一个名为 .random() 的扩展函数,它会返回列表中的一个随机项目。

提示 2

Random.nextInt(from = ..., until = ...) 会在指定的范围之内给你一个随机的 Int 数字。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
import kotlin.random.Random

data class Employee(val name: String, var salary: Int)

// Write your code here

fun main() {
    val empGen = RandomEmployeeGenerator(10, 30)
    println(empGen.generateEmployee())
    println(empGen.generateEmployee())
    println(empGen.generateEmployee())
    empGen.minSalary = 50
    empGen.maxSalary = 100
    println(empGen.generateEmployee())
}
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
import kotlin.random.Random

data class Employee(val name: String, var salary: Int)

class RandomEmployeeGenerator(var minSalary: Int, var maxSalary: Int) {
    val names = listOf("John", "Mary", "Ann", "Paul", "Jack", "Elizabeth")
    fun generateEmployee() =
        Employee(names.random(),
            Random.nextInt(from = minSalary, until = maxSalary))
}

fun main() {
    val empGen = RandomEmployeeGenerator(10, 30)
    println(empGen.generateEmployee())
    println(empGen.generateEmployee())
    println(empGen.generateEmployee())
    empGen.minSalary = 50
    empGen.maxSalary = 100
    println(empGen.generateEmployee())
}

示例解答

另请参阅

上一步

下一步