1. 准备工作
在此在线课程的 Codelab 中,您将构建一个 Dice Roller Android 应用。当用户“掷骰子”时,系统会生成一个随机结果。生成结果时会考虑骰子的面数。例如,6 面的骰子,只能生成 1 到 6 的值。
下面是最终应用呈现的样子。
为帮助您专注于了解此应用的全新编程概念,您将使用基于浏览器的 Kotlin 编程工具来创建核心应用功能。该程序会将您的结果输出到控制台。之后您将在 Android Studio 中实现界面。
在此第一个 Codelab 中,您将创建一个 Kotlin 程序,以模拟掷骰子并输出一个随机数字,就像实际掷骰子的情形一样。
前提条件
- 已了解如何在 https://developer.android.com/training/kotlinplayground 中打开、修改和运行代码
- 能够创建并运行使用变量和函数的 Kotlin 程序,并将结果输出到控制台。
- 能够使用采用
${variable}
表示法的字符串模板设置文本中数字的格式。
学习内容
- 如何编写程序来生成随机数字以模拟掷骰子。
- 如何使用变量和方法创建
Dice
类,以构建代码。 - 如何创建类的对象实例、修改其变量以及调用其方法。
构建内容
- 使用基于浏览器的 Kotlin 编程工具构建可执行随机掷骰子操作的 Kotlin 程序。
所需条件
- 一台已连接到互联网的计算机
2. 掷出随机数字
游戏通常具有随机性。您可以赢得随机奖励,或在棋盘游戏中移动随机步数。在日常生活中,您可以使用随机数字和字母来生成更安全的密码!
您可以编写一个程序来模拟掷骰子,而无需真的掷骰子。每次掷骰子时,结果可以是可能值范围内的任何数字。幸运的是,您无需为此类程序自行构建随机数字生成器。大多数编程语言(包括 Kotlin)都内置有用于生成随机数字的方法。在此任务中,您将使用 Kotlin 代码来生成随机数字。
设置起始代码
- 在浏览器中,打开网站 https://developer.android.com/training/kotlinplayground。
- 删除代码编辑器中的所有现有代码,并将其替换为以下代码。这是您在之前的 Codelab 中使用的
main()
函数。
fun main() {
}
使用随机函数
如要掷骰子,您需要一种方法来表示所有有效的掷骰结果值。对于常规的 6 面骰子,可接受的掷骰结果值包括:1、2、3、4、5 和 6。
在前面的课程中,您学习了数据类型,比如 Int
表示整数,String
表示文字。IntRange
是另一种数据类型,表示从起始值到结束值的整数范围。IntRange
是适合用于表示掷骰可能产生的值的数据类型。
- 在
main()
函数中,将一个变量定义为名为diceRange
的val
。将其分配给 1 到 6 的IntRange
,用于表示 6 面骰子可以掷出的整数值范围。
val diceRange = 1..6
可以看出,1..6
是一个 Kotlin 范围,因为它包含起始数字、两个点以及一个结束数字(中间没有空格)。下面再列举两个整数范围的其他例子,2..5
表示数字 2 到 5,100..200
表示数字 100 到 200。
与调用 println()
可指示系统输出给定文本类似,您可以使用 random()
函数生成并返回给定范围内的随机数字。同之前一样,您可以将结果存储在变量中。
- 在
main()
中,将一个变量定义为名为randomNumber
的val
。 - 为
randomNumber
设置对diceRange
范围调用random()
的结果值,如下所示。
val randomNumber = diceRange.random()
请注意,在变量和函数调用之间使用一个句点(即点),即可对 diceRange
调用 random()
。您可以将其解读为“从 diceRange
中生成随机数字”。然后结果会存储在 randomNumber
变量中。
- 为了显示随机生成的数字,请使用字符串格式表示法(也称为“字符串模板”)
${randomNumber}
进行输出,如下所示。
println("Random number: ${randomNumber}")
完成后的代码应如下所示。
fun main() {
val diceRange = 1..6
val randomNumber = diceRange.random()
println("Random number: ${randomNumber}")
}
- 运行几次代码。每次,您都应看到如下输出内容,其中会显示不同的随机数字。
Random number: 4
3. 创建 Dice 类
当您掷骰子时,它们是您手中的真实物体。而您刚刚编写的代码尽管可以正常运行,但是很难将其与真实的骰子关联起来。编写程序时,如果能让程序更贴合其表示的事物,就会更容易让人理解。所以,如果能用程序创建一个可以投掷的骰子,那就太棒了!
所有骰子的工作原理基本都相同。它们具有相同的属性(如骰面)、相同的行为(例如可以滚动)。在 Kotlin 中,您可以创建骰子的程序蓝图,说明骰子有骰面,并可以掷出一个随机数字。此蓝图称为“类”。
然后,您可以在该类中创建称为“对象实例”的真实骰子对象。例如,您可以创建 12 面的骰子或 4 面的骰子。
定义 Dice 类
在以下步骤中,您将定义一个名为 Dice
的新类来表示可滚动的骰子。
- 为了重新开始,请清除
main()
函数中的代码,确保最终代码如下所示。
fun main() {
}
- 在此
main()
函数下,添加一个空行,然后添加代码以创建Dice
类。如下所示,先输入关键字class
,后跟类名称,再跟左大括号和右大括号。在大括号内留出空间,以便放入类的代码。
class Dice {
}
在类定义中,可以使用变量为类指定一个或多个属性。真实的骰子可以具有多个面、一种颜色或重量。在此任务中,您将着重处理骰子的面数属性。
- 在
Dice
类中,添加一个名为sides
的var
,用于表示骰子将具有的面数。将sides
设置为 6。
class Dice {
var sides = 6
}
大功告成。现在,您有了一个表示骰子的非常简单的类。
创建 Dice 类的实例
有了这个 Dice
类,您就有了表示骰子的蓝图。为了在程序中呈现出一个真实的骰子,您需要创建一个 Dice
对象实例。(如果您需要有三个骰子,就要创建三个对象实例。)
- 为了创建
Dice
的对象实例,请在main()
函数中创建一个名为myFirstDice
的val
,并将其初始化为Dice
类的实例。请注意类名称后面的括号,其表示您在通过该类创建一个新的对象实例。
fun main() {
val myFirstDice = Dice()
}
现在,您已经有了依据蓝图构建的 myFirstDice
对象,接下来可以访问其属性了。Dice
的唯一属性是其 sides
。您使用“点分表示法”访问属性。因此,如需访问 myFirstDice
的 sides
属性,请调用 myFirstDice.sides
,这读作“myFirstDice
点 sides
”。
- 在
myFirstDice
声明的下方,添加println()
语句以输出myFirstDice.
的sides
数量。
println(myFirstDice.sides)
您的代码应如下所示。
fun main() {
val myFirstDice = Dice()
println(myFirstDice.sides)
}
class Dice {
var sides = 6
}
- 运行程序,它应输出在
Dice
类中定义的sides
的数量。
6
现在,您有了一个 Dice
类和一个具有 6 个 sides
的真实骰子 myFirstDice
。
我们来掷骰子吧!
掷骰子
您之前曾使用函数执行过输出蛋糕层的操作。掷骰子操作也可以用函数实现。由于所有骰子都可以滚动,因此您可以在 Dice
类中为其添加一个函数。在类中定义的函数也称为方法。
- 在
Dice
类的sides
变量下方,插入一个空行,然后创建一个用于掷骰子的新函数。首先输入 Kotlin 关键字fun
,再输入方法的名称,然后是括号()
,然后再是左大括号和右大括号{}
。可以在大括号内留一个空行,以便输入更多代码,如下所示。您的类应如下所示。
class Dice {
var sides = 6
fun roll() {
}
}
当您掷一个六面骰子时,它会产生一个从 1 到 6 的随机数字。
- 在
roll()
方法中,创建一个val randomNumber
。在1..6
范围内为其分配一个随机数字。使用点分表示法对该范围调用random()
。
val randomNumber = (1..6).random()
- 生成随机数字后,将其输出到控制台。完成后的
roll()
方法应如以下代码所示。
fun roll() {
val randomNumber = (1..6).random()
println(randomNumber)
}
- 为了真实地滚动
myFirstDice
,请在main()
中对myFirstDice
调用roll()
方法。您使用“点分表示法”调用方法。因此,为了对myFirstDice
调用roll()
方法,请输入myFirstDice.roll()
,这读作“myFirstDice
dotroll()
”。
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)
}
}
- 运行代码!您应会看到随机掷骰子的结果小于骰子面数。多运行几次代码,会发现面数保持不变,掷骰子的结果值发生变化。
6 4
祝贺您!您已使用 sides
变量和 roll()
函数定义了 Dice
类。在 main()
函数中,您创建了一个新的 Dice
对象实例,然后对该实例调用了 roll()
方法,以生成随机数字。
4. 返回您掷骰子的结果值
您目前是在 roll()
函数中输出 randomNumber
的值,运行一切正常!但有时,将函数的结果返回给调用该函数的对象会更有用。例如,您可以将 roll()
方法的结果分配给某个变量,然后将玩家移动相应的步数!我们来看看具体操作方法。
- 在
main()
中,修改显示myFirstDice.roll()
的代码行。创建名为diceRoll
的val
。将其设置为与roll()
方法返回的值相等。
val diceRoll = myFirstDice.roll()
目前还没有任何效果,因为 roll()
尚未返回任何内容。为了使上述代码按预期运行,roll()
必须返回一些内容。
在之前的 Codelab 中,您了解到需要为函数的输入参数指定数据类型。同样,您也必须为函数返回的数据指定数据类型。
- 更改
roll()
函数以指定将返回的数据类型。在本例中,随机数字为Int
,因此返回类型为Int
。指定返回类型的语法如下:在函数名称后面,在括号后添加冒号、空格,然后为函数返回类型添加Int
关键字。函数定义应如下代码所示。
fun roll(): Int {
- 运行该代码。您将在问题视图中看到一个错误。错误内容是:
A ‘return' expression required in a function with a block body (‘{...}')
您将函数定义改为了返回 Int
,但系统提示说您的代码实际上并未返回 Int
。“块主体”或“函数主体”是指函数大括号内的代码。您可以修复此错误,方法是在函数主体末尾使用 return
语句从函数返回值。
- 在
roll()
中,移除println()
语句,并将其替换为randomNumber
的return
语句。您的roll()
函数应如以下代码所示。
fun roll(): Int {
val randomNumber = (1..6).random()
return randomNumber
}
- 在
main()
中,移除有关骰子面数的输出语句。 - 添加一个语句,用信息性句子输出
sides
和diceRoll
的值。您完成的main()
函数应类似于以下代码。
fun main() {
val myFirstDice = Dice()
val diceRoll = myFirstDice.roll()
println("Your ${myFirstDice.sides} sided dice rolled ${diceRoll}!")
}
- 运行代码,输出应如下所示。
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
}
}
5. 更改骰子的面数
并非所有骰子都有 6 个面!骰子形状各异,大小不一:有 4 面的、8 面的,还有 120 面的!
- 在
Dice
类的roll()
方法中,将硬编码的1..6
更改为使用sides
,这样,范围和掷出的随机数字将始终适用于面数。
val randomNumber = (1..sides).random()
- 在
main()
函数中,在输出掷骰结果的后面,将myFirstDice
的sides
设置为 20。
myFirstDice.sides = 20
- 复制下面的现有输出语句并将其粘贴在更改面数的位置下方。
- 将
diceRoll
的输出结果替换为对myFirstDice
调用roll()
方法的输出结果。
println("Your ${myFirstDice.sides} sided dice rolled ${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
}
}
- 运行程序,您应该会看到一条有关 6 面骰子的消息,以及另一条有关 20 面骰子的消息。
Your 6 sided dice rolled 3! Your 20 sided dice rolled 15!
6. 自定义骰子
类的概念是代表一个事物,通常是现实世界中的事物。在本例中,Dice
类代表实际的骰子。在现实世界中,骰子的面数是不会变的。如果您需要不同的面数,就需要使用不同的骰子。在编程上,这意味着您应该创建一个具有所需面数的新骰子对象实例,而不是更改现有 Dice
对象实例的面数属性。
在此任务中,您将修改 Dice
类,以便在创建新实例时指定面数。您将更改 Dice
类定义,以便可以提供面数。这类似于函数接受输入参数的方式。
- 修改
Dice
类定义,以接受名为numSides
的整数。类中的代码不变。
class Dice(val numSides: Int) {
// Code inside does not change.
}
- 在
Dice
类中,删除sides
变量,因为您现在可以使用numSides
。 - 此外,修改范围以使用
numSides
。
您的 Dice
类应如下所示。
class Dice (val numSides: Int) {
fun roll(): Int {
val randomNumber = (1..numSides).random()
return randomNumber
}
}
如果运行这段代码,您将看到大量错误,因为需要更新 main()
才能使用对 Dice
类的更改。
- 在
main()
中,为了创建具有 6 个面的myFirstDice
,您现在必须提供面数作为Dice
类的参数,如下所示。
val myFirstDice = Dice(6)
- 在输出语句中,将
sides
更改为numSides
。 - 然后在下方,删除将
sides
更改为 20 的代码,因为该变量已不存在。 - 同时删除其下方的
println
语句。
您的 main()
函数应类似于以下代码,如果运行该代码段,应该不会再出现错误了。
fun main() {
val myFirstDice = Dice(6)
val diceRoll = myFirstDice.roll()
println("Your ${myFirstDice.numSides} sided dice rolled ${diceRoll}!")
}
- 输出第一个掷骰结果后,添加代码以创建并输出第二个名为
mySecondDice
的Dice
对象,其具有 20 个面。
val mySecondDice = Dice(20)
- 添加用于掷骰并输出返回值的输出语句。
println("Your ${mySecondDice.numSides} sided dice rolled ${mySecondDice.roll()}!")
- 完成后的
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
}
}
- 运行已完成的程序,输出内容应如下所示。
Your 6 sided dice rolled 5! Your 20 sided dice rolled 7!
7. 采用规范的编码做法
编写代码时,应尽量简洁。您可以去掉 randomNumber
变量并直接返回随机数字。
- 更改
return
语句以直接返回随机数字。
fun roll(): Int {
return (1..numSides).random()
}
在第二个输出语句中,您将用于获取随机数的调用放入字符串模板。您可以按照在第一个输出语句中采用的相同方法来去掉 diceRoll
变量。
- 在字符串模板中调用
myFirstDice.roll()
并删除diceRoll
变量。您的main()
代码的前两行现在如下所示。
val myFirstDice = Dice(6)
println("Your ${myFirstDice.numSides} sided dice rolled ${myFirstDice.roll()}!")
- 运行代码,输出内容应该没有变化。
以下是您重构后的最终代码。
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()
}
}
8. 解决方案代码
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()
}
}
9. 总结
- 对
IntRange
调用random()
函数以生成随机数字:(1..6).random()
- 类就像是对象的蓝图。它们可以具有作为变量和函数实现的属性和行为。
- 类的实例代表对象,通常是实物,如骰子。可以调用针对对象的操作并更改其属性。
- 创建实例时,可以向类提供值。例如:创建
class Dice(val numSides: Int)
,然后通过Dice(6)
创建实例。 - 函数可以返回一些内容。可以在函数定义中指定要返回的数据类型,并在函数正文中使用
return
语句来返回内容。例如:fun example(): Int { return 5 }
10. 了解详情
11. 自行练习
进行以下练习:
- 为
Dice
类指定另一种颜色属性,并创建多个具有不同面数和颜色的实例! - 创建
Coin
类,使其能够翻转,创建该类的实例,并抛掷多个硬币!您将如何将random()
函数与范围结合使用来实现硬币的抛掷?