Kotlin 中的类和对象实例

在此衔接课程的 Codelab 中,您将构建一个 Dice Roller Android 应用。当用户“掷骰子”时,系统会生成一个随机结果。生成结果时会考虑骰子的面数。例如,6 面的骰子,只能生成 1 到 6 的值。

下面是最终的应用呈现的样子。

为帮助您专注于了解此应用的全新编程概念,您将使用基于浏览器的 Kotlin 编程工具来创建核心应用功能。该程序会将您的结果输出到控制台。之后您将在 Android Studio 中实现界面。

在此第一个 Codelab 中,您将创建一个 Kotlin 程序,以模拟掷骰子并输出一个随机数字,就像实际掷骰子的情形一样。

前提条件

  • 已通过 https://developer.android.com/training/kotlinplayground 了解如何打开、修改和运行代码
  • 能够创建并运行使用变量和函数的 Kotlin 程序,并将结果输出到控制台。
  • 能够使用采用 ${variable} 表示法的字符串模板设置文本中数字的格式。

学习内容

  • 如何编写程序来生成随机数字以模拟掷骰子。
  • 如何使用变量和方法创建 Dice 类,以构建代码。
  • 如何创建类的对象实例、修改其变量以及调用其方法。

您将构建的内容

  • 使用基于浏览器的 Kotlin 编程工具构建可执行随机掷骰子操作的 Kotlin 程序。

所需工具

  • 一台已连接到互联网的计算机

游戏通常具有随机性。您可以赢得随机奖励,或在棋盘游戏中移动随机步数。在日常生活中,您可以使用随机数字和字母来生成更安全的密码!

您可以编写一个程序来模拟掷骰子,而无需真的掷骰子。每次掷骰子时,结果可以是可能值范围内的任何数字。幸运的是,您无需为此类程序自行构建随机数字生成器。大多数编程语言(包括 Kotlin)都内置有用于生成随机数字的方法。在此任务中,您将使用 Kotlin 代码来生成随机数字。

设置起始代码

  1. 在浏览器中,打开网站 https://developer.android.com/training/kotlinplayground
  2. 删除代码编辑器中的所有现有代码,并将其替换为以下代码。这是您在之前的 Codelab 中使用的 main() 函数。
fun main() {

}

使用随机函数

如要掷骰子,您需要一种方法来表示所有有效的掷骰结果值。对于常规的 6 面骰子,可接受的掷骰结果值包括:1、2、3、4、5 和 6。

在前面的课程中,您学习了数据类型,比如 Int 表示整数,String 表示文字。IntRange 是另一种数据类型,表示从起始值到结束值的整数范围。IntRange 是适合用于表示掷骰可能产生的值的数据类型。

  1. main() 函数中,将一个变量定义为名为 diceRangeval。将其分配给 1 到 6 的 IntRange,用于表示 6 面骰子可以掷出的整数值范围。
val diceRange = 1..6

可以看出,1..6 是一个 Kotlin 范围,因为它包含起始数字、两个点以及一个结束数字(中间没有空格)。下面再列举两个整数范围的其他例子,2..5 表示数字 2 到 5,100..200 表示数字 100 到 200。

与调用 println() 可指示系统输出给定文本类似,您可以使用 random() 函数生成并返回给定范围内的随机数字。同之前一样,您可以将结果存储在变量中。

  1. main() 中,将一个变量定义为名为 randomNumberval
  2. randomNumber 设置对 diceRange 范围调用 random() 的结果值,如下所示。
 val randomNumber = diceRange.random()

请注意,在变量和函数调用之间使用一个句点(即点),即可对 diceRange 调用 random()。您可以将其解读为“从 diceRange 中生成随机数字”。然后结果会存储在 randomNumber 变量中。

  1. 为了显示随机生成的数字,请使用字符串格式表示法(也称为“字符串模板”)${randomNumber} 进行输出,如下所示。
println("Random number: ${randomNumber}")

完成后的代码应如下所示。

fun main() {
    val diceRange = 1..6
    val randomNumber = diceRange.random()
    println("Random number: ${randomNumber}")
}
  1. 运行几次代码。每次,您都应看到如下输出内容,其中会显示不同的随机数字。
Random number: 4

当您掷骰子时,它们是您手中的真实物体。而您刚刚编写的代码尽管可以正常运行,但是很难将其与真实的骰子关联起来。编写程序时,如果能让程序更贴合其表示的事物,就会更容易让人理解。所以,如果能用程序创建一个可以投掷的骰子,那就太棒了!

所有骰子的工作原理基本都相同。它们具有相同的属性(如骰面)、相同的行为(例如可以滚动)。在 Kotlin 中,您可以创建骰子的程序蓝图,说明骰子有骰面,并可以掷出一个随机数字。此蓝图称为“类”。

然后,您可以在该类中创建称为“对象实例”的真实骰子对象。例如,您可以创建 12 面的骰子或 4 面的骰子。

定义 Dice 类

在以下步骤中,您将定义一个名为 Dice 的新类来表示可滚动的骰子。

  1. 为了重新开始,请清除 main() 函数中的代码,确保最终代码如下所示。
fun main() {

}
  1. 在此 main() 函数下,添加一个空行,然后添加代码以创建 Dice 类。如下所示,先输入关键字 class,后跟类名称,再跟左大括号和右大括号。在大括号内留出空间,以便放入类的代码。
class Dice {

}

在类定义中,可以使用变量为类指定一个或多个属性。真实的骰子可以具有多个面、一种颜色或重量。在此任务中,您将着重处理骰子的面数属性。

  1. Dice 类中,添加一个名为 sidesvar,用于表示骰子将具有的面数。将 sides 设置为 6。
class Dice {
    var sides = 6
}

大功告成。现在,您有了一个表示骰子的非常简单的类。

创建 Dice 类的实例

有了这个 Dice 类,您就有了表示骰子的蓝图。为了在程序中呈现出一个真实的骰子,您需要创建一个 Dice 对象实例。(如果您需要有三个骰子,就要创建三个对象实例。)

  1. 为了创建 Dice 的对象实例,请在 main() 函数中创建一个名为 myFirstDiceval,并将其初始化为 Dice 类的实例。请注意类名称后面的括号,其表示您在从该类创建新的对象实例。
fun main() {
    val myFirstDice = Dice()
}

现在,您已经有了依据蓝图构建的 myFirstDice 对象,接下来可以访问其属性了。Dice 的唯一属性是其 sides。您可以使用“点分表示法”访问属性。因此,如需访问 myFirstDicesides 属性,请调用 myFirstDice.sides,这读作“myFirstDicesides”。

  1. myFirstDice 声明的下方,添加 println() 语句以输出 myFirstDice.sides 数量。
println(myFirstDice.sides)

代码应如下所示。

fun main() {
    val myFirstDice = Dice()
    println(myFirstDice.sides)
}

class Dice {
    var sides = 6
}
  1. 运行程序,它应输出在 Dice 类中定义的 sides 的数量。
6

现在,您有了一个 Dice 类和一个具有 6 个 sides 的真实骰子 myFirstDice

我们来掷骰子吧!

掷骰子

您之前曾使用函数执行过输出蛋糕层的操作。掷骰子操作也可以用函数实现。由于所有骰子都可以滚动,因此您可以在 Dice 类中为其添加一个函数。在类中定义的函数也称为方法。

  1. Dice 类的 sides 变量下方,插入一个空行,然后创建一个用于掷骰子的新函数。首先输入 Kotlin 关键字 fun,再输入方法的名称,然后是括号 (),然后再是左大括号和右大括号 {}。可以在大括号内留一个空行,以便输入更多代码,如下所示。类应如下所示。
class Dice {
    var sides = 6

    fun roll() {

    }
}

当您掷一个六面骰子时,它会产生一个从 1 到 6 的随机数字。

  1. roll() 方法中,创建一个 val randomNumber。在 1..6 范围内为其分配一个随机数字。使用点分表示法对该范围调用 random()
val randomNumber = (1..6).random()
  1. 生成随机数字后,将其输出到控制台。完成后的 roll() 方法应如以下代码所示。
fun roll() {
     val randomNumber = (1..6).random()
     println(randomNumber)
}
  1. 为了真实地滚动 myFirstDice,请在 main() 中对 myFirstDice 调用 roll() 方法。您使用“点分表示法”调用方法。因此,为了对 myFirstDice 调用 roll() 方法,请输入 myFirstDice.roll(),这读作“myFirstDice dot roll()”。
myFirstDice.roll()

完成后的代码应如下所示。

fun main() {
    val myFirstDice = Dice()
    println(myFirstDice.sides)
    myFirstDice.roll()
}

class Dice {
    var sides = 6

    fun roll() {
        val randomNumber = (1..6).random()
        println(randomNumber)
    }
}
  1. 运行代码!您应会看到随机掷骰子的结果小于骰子面数。多运行几次代码,会发现面数保持不变,掷骰子的结果值发生变化。
6
4

祝贺您!您已使用 sides 变量和 roll() 函数定义了 Dice 类。在 main() 函数中,您创建了一个新的 Dice 对象实例,然后对该实例调用了 roll() 方法,以生成随机数字。

您目前是在 roll() 函数中输出 randomNumber 的值,运行一切正常!但有时,将函数的结果返回给调用该函数的对象会更有用。例如,您可以将 roll() 方法的结果分配给某个变量,然后将玩家移动相应的步数!我们来看看具体操作方法。

  1. main() 中,修改显示 myFirstDice.roll() 的代码行。创建名为 diceRollval。将其设置为与 roll() 方法返回的值相等。
val diceRoll = myFirstDice.roll()

目前还没有任何效果,因为 roll() 尚未返回任何内容。为了使上述代码按预期运行,roll() 必须返回一些内容。

在之前的 Codelab 中,您了解到需要为函数的输入参数指定数据类型。同样,您也必须为函数返回的数据指定数据类型。

  1. 更改 roll() 函数以指定将返回的数据类型。在本例中,随机数字为 Int,因此返回类型为 Int。指定返回类型的语法如下:在函数名称后面,在括号后添加冒号、空格,然后为函数返回类型添加 Int 关键字。函数定义应如下代码所示。
fun roll(): Int {
  1. 运行该代码。您将在问题视图中看到一个错误。错误内容是:
A ‘return'  expression is required in a function with a block body. 

您将函数定义改为了返回 Int,但系统提示说

您的代码实际上并未返回 Int。“块正文”或“函数正文”是指函数大括号内的代码。您可以修复此错误,方法是在函数正文末尾使用 return 语句从函数返回值。

  1. roll() 中,移除 println() 语句,并将其替换为 randomNumberreturn 语句。您的 roll() 函数应如以下代码所示。
fun roll(): Int {
     val randomNumber = (1..6).random()
     return randomNumber
}
  1. main() 中,移除有关骰子面数的输出语句。
  2. 添加一个语句,用信息性句子输出 sidesdiceRoll 的值。您完成的 main() 函数应类似于以下代码。
fun main() {
    val myFirstDice = Dice()
    val diceRoll = myFirstDice.roll()
    println("Your ${myFirstDice.sides} sided dice rolled ${diceRoll}!")
}
  1. 运行代码,输出应如下所示。
Your 6 sided dice rolled 4!

以下是到目前为止所编写的所有代码。

fun main() {
    val myFirstDice = Dice()
    val diceRoll = myFirstDice.roll()
    println("Your ${myFirstDice.sides} sided dice rolled ${diceRoll}!")
}

class Dice {
    var sides = 6

    fun roll(): Int {
        val randomNumber = (1..6).random()
        return randomNumber
    }
}

并非所有骰子都有 6 个面!骰子形状各异,大小不一:有 4 面的、8 面的,还有 120 面的!

  1. Dice 类的 roll() 方法中,将硬编码的 1..6 更改为使用 sides,这样,范围和掷出的随机数字将始终适用于面数。
val randomNumber = (1..sides).random()
  1. main() 函数中,在输出掷骰结果的后面,将 myFirstDicesides 设置为 20。
myFirstDice.sides = 20
  1. 复制下面的现有输出语句并将其粘贴在更改面数的位置下方。
  2. diceRoll 的输出结果替换为对 myFirstDice 调用 roll() 方法的输出结果。
println("Your ${myFirstDice.sides} sided dice has rolled a ${myFirstDice.roll()}!")

您的程序应如下所示。

fun main() {

    val myFirstDice = Dice()
    val diceRoll = myFirstDice.roll()
    println("Your ${myFirstDice.sides} sided dice rolled ${diceRoll}!")

    myFirstDice.sides = 20
    println("Your ${myFirstDice.sides} sided dice rolled ${myFirstDice.roll()}!")
}

class Dice {
    var sides = 6

    fun roll(): Int {
        val randomNumber = (1..sides).random()
        return randomNumber
    }
}
  1. 运行程序,您应该会看到一条有关 6 面骰子的消息,以及另一条有关 20 面骰子的消息。
Your 6 sided dice rolled 3!
Your 20 sided dice rolled 15!

类的概念是代表一个事物,通常是现实世界中的事物。在本例中,Dice 类代表实际的骰子。在现实世界中,骰子的面数是不会变的。如果您需要不同的面数,就需要使用不同的骰子。在编程上,这意味着您应该创建一个具有所需面数的新骰子对象实例,而不是更改现有 Dice 对象实例的面数属性。

在此任务中,您将修改 Dice 类,以便在创建新实例时指定面数。您将更改 Dice 类定义,以便可以提供面数。这类似于函数接受输入参数的方式。

  1. 修改 Dice 类定义,以接受名为 numSides 的整数。类中的代码不变。
class Dice(val numSides: Int) {
   // Code inside does not change.
}
  1. Dice 类中,删除 sides 变量,因为您现在可以使用 numSides
  2. 此外,修改范围以使用 numSides

您的 Dice 类应如下所示。

class Dice (val numSides: Int) {

    fun roll(): Int {
        val randomNumber = (1..numSides).random()
        return randomNumber
    }
}

如果运行这段代码,您将看到大量错误,因为需要更新 main() 才能使用对 Dice 类的更改。

  1. main() 中,为了创建具有 6 个面的 myFirstDice,您现在必须提供面数作为 Dice 类的参数,如下所示。
    val myFirstDice = Dice(6)
  1. 在输出语句中,将 sides 更改为 numSides
  2. 然后在下方,删除将 sides 更改为 20 的代码,因为该变量已不存在。
  3. 同时删除其下方的 println 语句。

您的 main() 函数应类似于以下代码,如果运行该代码段,应该不会再出现错误了。

fun main() {
    val myFirstDice = Dice(6)
    val diceRoll = myFirstDice.roll()
    println("Your ${myFirstDice.numSides} sided dice rolled ${diceRoll}!")
}
  1. 输出第一个掷骰结果后,添加代码以创建并输出第二个名为 mySecondDiceDice 对象,其具有 20 个面。
val mySecondDice = Dice(20)
  1. 添加用于滚动并输出返回值的输出语句。
println("Your ${mySecondDice.numSides} sided dice rolled  ${mySecondDice.roll()}!")
  1. 完成后的 main() 函数应如下所示。
fun main() {
    val myFirstDice = Dice(6)
    val diceRoll = myFirstDice.roll()
    println("Your ${myFirstDice.numSides} sided dice rolled ${diceRoll}!")

    val mySecondDice = Dice(20)
    println("Your ${mySecondDice.numSides} sided dice rolled ${mySecondDice.roll()}!")
}

class Dice (val numSides: Int) {

    fun roll(): Int {
        val randomNumber = (1..numSides).random()
        return randomNumber
    }
}
  1. 运行已完成的程序,输出内容应如下所示。
Your 6 sided dice rolled 5!
Your 20 sided dice rolled 7!

编写代码时,应尽量简洁。您可以去掉 randomNumber 变量并直接返回随机数字。

  1. 更改 return 语句以直接返回随机数字。
fun roll(): Int {
    return (1..numSides).random()
}

在第二个输出语句中,您将用于获取随机数的调用放入字符串模板。您可以按照在第一个输出语句中采用的相同方法来去掉 diceRoll 变量。

  1. 在字符串模板中调用 myFirstDice.roll() 并删除 diceRoll 变量。您的 main() 代码的前两行现在如下所示。
val myFirstDice = Dice(6)
println("Your ${myFirstDice.numSides} sided dice rolled ${myFirstDice.roll()}!")
  1. 运行代码,输出内容应该没有变化。

以下是您重构后的最终代码。

fun main() {
    val myFirstDice = Dice(6)
    println("Your ${myFirstDice.numSides} sided dice rolled ${myFirstDice.roll()}!")

    val mySecondDice = Dice(20)
    println("Your ${mySecondDice.numSides} sided dice rolled ${mySecondDice.roll()}!")
}

class Dice (val numSides: Int) {

    fun roll(): Int {
        return (1..numSides).random()
    }
}
fun main() {
    val myFirstDice = Dice(6)
    val diceRoll = myFirstDice.roll()
    println("Your ${myFirstDice.numSides} sided dice rolled ${diceRoll}!")

    val mySecondDice = Dice(20)
    println("Your ${mySecondDice.numSides} sided dice rolled ${mySecondDice.roll()}!")
}

class Dice (val numSides: Int) {

    fun roll(): Int {
        return (1..numSides).random()
    }
}
  • IntRange 调用 random() 函数以生成随机数字:(1..6).random()
  • 类就像是对象的蓝图。它们可以具有作为变量和函数实现的属性和行为。
  • 类的实例代表对象,通常是实物,如骰子。可以调用针对对象的操作并更改其属性。
  • 创建实例时,可以向类提供值。例如:创建 class Dice(val numSides: Int),然后通过 Dice(6) 创建实例。
  • 函数可以返回一些内容。可以在函数定义中指定要返回的数据类型,并在函数正文中使用 return 语句来返回内容。例如:fun example(): Int { return 5 }

请进行以下练习

  • Dice 类指定另一种颜色属性,并创建多个具有不同面数和颜色的实例!
  • 创建 Coin 类,使其能够翻转,创建该类的实例,并抛掷多个硬币!您将如何将 random() 函数与范围结合使用来实现硬币的抛掷?