Kotlin创建和使用变量
数据类型


val关键字 - 预计变量值不会变化时使用。var关键字 - 预计变量值会发生变化时使用。
Kotlin创建和使用函数

传入形参

1 | |
函数语法

具名实参

默认实参

文本元素
1 | |
文本布局
选择方式:选择两个 Text 可组合项,然后点击灯泡。依次选择 Surround with widget > Surround with Row

图片元素
添加图片
- 在 Android Studio 中,依次点击 View > Tool Windows > Resource Manager,或点击 Project 窗口旁边的 Resource Manager 标签页。


注意:Resource Manager 是一个工具窗口,可供您在应用中导入、创建、管理和使用资源。
- 依次点击 + (Add resources to the module) > Import Drawables。

- 在文件浏览器中,选择已下载的图片文件,然后点击 Open。
此操作会打开 Import drawables 对话框。

- Android Studio 会向您显示该图片的预览。从 QUALIFIER TYPE 下拉列表中选择 Density。后面会介绍为何要执行此操作。

- 从 VALUE 列表中选择 No Density。

Android 设备具有不同的屏幕尺寸(手机、平板电脑和电视等),而且这些屏幕也具有不同的像素尺寸。也就是说,有可能一部设备的屏幕为每平方英寸 160 个像素,而另一部设备的屏幕在相同的空间内可以容纳 480 个像素。如果不考虑像素密度的这些变化,系统可能会按比例缩放图片,这可能会导致图片模糊或占用大量内存空间,或者图片大小不当。
如果所调整的图片超出了 Android 系统可处理的图片大小,系统会抛出内存不足错误。对于照片和背景图片(如当前图片 androidparty.png),应将其放在 drawable-nodpi 文件夹中,这样会停止调整大小行为。
如需详细了解像素密度,请参阅支持不同的像素密度。
- 点击下一步。
- Android Studio 会显示将在其中放置图片的文件夹结构。请注意
drawable-nodpi文件夹。 - 点击 **Import(C)**。

访问资源

- 在
GreetingImage()函数中,声明val属性并将其命名为image。 - 通过传入
androidparty资源来调用painterResource()函数。将返回值分配给image变量。
1 | |
示例代码
1 | |
缩放图片内容
多种 ContentScale 类型
对齐和排列文本
在 MainActivity.kt 文件中,滚动到 GreetingText() 函数。此列中的 verticalArrangement 属性设置为 Arrangement.Center。因此,文本内容将在屏幕上居中。
内边距
界面元素会用自身包裹住其内容。为避免包裹地过紧,您可以在每一侧指定内边距大小。
![]() |
![]() |
|---|---|
内边距将作为修饰符使用,这意味着您可以将其应用于任何可组合项。对于可组合项的每一侧,padding 修饰符都接受一个可选实参,该实参定义了内边距的大小。

1 | |
字符串提取到资源文件
- 点击屏幕左侧的灯泡。
- 选择 Extract string resource。

Android Studio 将打开 Extract Resource 对话框。在此对话框中,您可以自定义字符串资源的名称以及有关如何存储该资源的一些详细信息。Resource name 字段用于输入字符串的名称。Resource value 字段用于输入字符串的实际内容。
- 在 Extract Resource 对话框中,将 Resource name 更改为
happy_birthday_text。
字符串资源应使用小写名称,并且多个单词之间应使用下划线分隔。将其他设置保留为默认值。

- 点击确定。
- 请注意代码的变化。
硬编码字符串现已替换为对 getString() 函数的调用。
1 | |
Kotlin条件
if语句
if结构

if分支

when语句

使用英文逗号来处理多个条件

1 | |
使用in处理一系列条件

1 | |
使用is检查数据类型

1 | |
使用if/else或when作为表达式

如果正文仅包含一个返回值或表达式,您可以移除大括号,使代码更简洁。

示例代码
1 | |
Kotlin null变量
使用方法
1 | |
如需在 Kotlin 中声明可为 null 的变量,您需要在相应类型的末尾添加 ? 运算符。例如,String? 类型可以存储字符串或 null,而 String 类型只能存储字符串。如需声明某个可为 null 的变量,您需要明确添加可为 null 类型。如果没有可为 null 类型,Kotlin 编译器会推断该变量属于不可为 null 类型。

使用?.安全调用运算符
如需使用 ?. 安全调用运算符访问方法或属性,请在变量名称后面添加 ? 符号,并使用 . 表示法访问方法或属性。
?. 安全调用运算符可让您更安全地访问可为 null 的变量,因为 Kotlin 编译器会阻止变量成员为访问 null 引用而进行的任何尝试,并针对访问的成员返回 null。
如需安全地访问可为 null 的 favoriteActor 变量的属性,请按以下步骤操作:
- 在
println()语句中,将.运算符替换为?.安全调用运算符:
1 | |
使用!!非null断言运算符

您需要在可为 null 的变量后面添加 !! 非 null 断言运算符,之后再跟 . 运算符,最后添加不含任何空格的方法或属性。
顾名思义,如果您使用 !! 非 null 断言运算符,即表示您断言变量的值不是 null,无论变量是否为该值都是如此。
与 ?. 安全调用运算符不同,当可为 null 的变量确实为 null 时,使用 !! 非 null 断言运算符可能会导致系统抛出 NullPointerException 错误。因此,只有在变量始终为不可为 null 或设置了适当的异常处理时,才应使用该断言运算符。如果异常未得到处理,便会导致运行时错误。您将在本课程后面的单元中了解异常处理。
如需使用 !! 非 null 断言运算符访问 favoriteActor 变量的属性,请按以下步骤操作:
- 为
favoriteActor变量重新赋予喜爱演员的名称,然后在println()语句中将?.安全调用运算符替换为!!非 null 断言运算符:
1 | |
使用?:Elvis运算符
?: Elvis 运算符可以与 ?. 安全调用运算符搭配使用。如果搭配使用 ?: Elvis 运算符,您便可以在 ?. 安全调用运算符返回 null 时添加默认值。这与 if/else 表达式类似,但更为常用。
如果该变量不为 null,则执行 ?: Elvis 运算符之前的表达式;如果变量为 null,则执行 ?: Elvis 运算符之后的表达式。

Kotlin类与对象
定义类
类定义以 class 关键字开头,后面依次跟名称和一对大括号。左大括号之前的语法部分也称为类标头。在大括号之间,您可以指定类的属性和函数。您很快就会学到属性和函数。类定义的语法如以下示意图所示:

以下是建议遵循的类命名惯例:
- 可以选择任何想要的类名称,但不要将 Kotlin 关键字用作类名称,例如
fun关键字。 - 类名称采用 PascalCase 大小写形式编写,因此每个单词都以大写字母开头,且各个单词之间没有空格。以“SmartDevice”为例,每个单词的第一个字母都大写,且单词之间没有空格。
类由以下三大部分组成:
- 属性:用于指定类对象属性的变量。
- 方法:包含类的行为和操作的函数。
- 构造函数:一种特殊的成员函数,用于在定义类的整个程序中创建类的实例。
创建类的示例
若要使用某个对象,您需要创建该对象,并将其赋给变量,方法与定义变量的方式类似。您可以使用 val 关键字来创建不可变变量,使用 var 关键字来创建可变变量。val 或 var 关键字后依次跟变量名称、= 赋值运算符和类对象的实例化。语法如下图所示:

定义类方法
在类中定义函数的语法与您之前学习的语法相同。唯一的区别在于,该函数是放在类主体中。在类主体中定义函数时,该函数称为成员函数或方法,用于表示类的行为。在本 Codelab 的剩余部分中,出现在类主体内的函数一律称为方法。
在 SmartDevice 类中定义 turnOn() 和 turnOff() 方法:
- 在
SmartDevice类的主体中,定义主体为空的turnOn()方法:
1 | |
- 在
turnOn()方法的主体中,添加println()语句,然后向其传递"Smartdeviceisturnedon."字符串:
1 | |
若要在类的外部调用类方法,请以类对象开头,后面依次跟 . 运算符、函数名称和一对圆括号。可视情况在圆括号中包含方法所需的实参。语法如下图所示:

对该对象调用 turnOn() 和 turnOff() 方法:
- 在
main()函数中smartTvDevice变量后面的代码行上,调用turnOn()方法:
1 | |
定义类属性
- 在
name属性后面的代码行上,定义category属性并为其赋予"Entertainment"字符串,然后定义deviceStatus属性并为其赋予"online"字符串:
1 | |
- 在
smartTvDevice变量后面的代码行上,调用println()函数,然后向其传递"Devicenameis:${smartTvDevice.name}"字符串:
1 | |
- 运行代码。
输出如下所示:
1 | |
属性中的getter和setter函数
属性的用途比变量更广泛。例如,假设您创建了一个类结构来表示智能电视。您会执行的常见操作之一是调高和调低音量。如需在编程中表示此操作,您可以创建一个名为 speakerVolume 的属性,其中包含电视音箱当前设置的音量,但音量值有范围限制。可设置的音量下限为 0,上限为 100。若要确保 speakerVolume 属性始终不超过 100 或低于 0,您可以编写 setter 函数。在更新属性值时,您需要检查该值是否处于 0 到 100 的范围内。再举一例,假设您必须确保名称始终大写。您可以实现 getter 函数,将 name 属性转换为大写。
在深入了解如何实现这些属性之前,您需要了解用于声明这些属性的完整语法。定义可变属性的完整语法是以变量定义开头,后跟可选的 get() 和 set() 函数。语法如下图所示:

例如,若要确保赋给 speakerVolume 属性的值介于 0 到 100 之间,您可以实现 setter 函数,如以下代码段所示:
1 | |
定义构造函数
定义形参化构造函数
在 SmartDevice 类中,name 和 category 属性不可变。您需要确保 SmartDevice 类的所有实例都会初始化 name 和 category 属性。在当前实现中,name 和 category 属性的值都采用硬编码。也就是说,所有智能设备都是以 "Android TV" 字符串命名,并使用 "Entertainment" 字符串进行分类。
若要保持不变性,同时避免使用硬编码值,请使用形参化构造函数进行初始化:
- 在
SmartDevice类中,将name和category属性移至构造函数中,且不赋予默认值:
1 | |
现在,该构造函数可接受形参来设置其属性,因此,为此类实例化对象的方式也会随之更改。实例化对象的完整语法如下图所示:

主要构造函数
您可以使用主要构造函数来初始化类标头中的属性。传递给构造函数的实参会赋给属性。定义主要构造函数的语法是以类名称开头,后面依次跟 constructor 关键字和一对圆括号。圆括号中包含主要构造函数的形参。如果有多个形参,请用英文逗号分隔形参定义。定义主要构造函数的完整语法如下图所示:

辅助构造函数
辅助构造函数包含在类的主体中,其语法包括以下三个部分:
- 辅助构造函数声明:辅助构造函数定义以
constructor关键字开头,后跟圆括号。可视情况在圆括号中包含辅助构造函数所需的形参。 - 主要构造函数初始化:初始化以冒号开头,后面依次跟
this关键字和一对圆括号。可视情况在圆括号中包含主要构造函数所需的形参。 - 辅助构造函数主体:在主要构造函数的初始化后跟一对大括号,其中包含辅助构造函数的主体。
语法如下图所示:

实现类之间的关系
在 Kotlin 中,所有类默认都是最终类,也就是说您无法扩展这些类,因此必须定义类之间的关系。
定义 SmartDevice 父类及其子类之间的关系:
- 在
SmartDevice父类中的class关键字前面添加open关键字,使其具有扩展性:
1 | |
open 关键字会告知编译器此类可供扩展,因此其他类现在可对其进行扩展。

- 创建会扩展
SmartDevice父类的SmartTvDevice子类:
1 | |
IS-A关系(继承)
如果在 SmartDevice 父类和 SmartTvDevice 子类之间指定 IS-A 关系,即表示 SmartDevice 父类可以执行的操作,SmartTvDevice 子类也可执行。这种关系是单向的,因此可以说每个智能电视“都是”智能设备,但不能说每个智能设备“都是”智能电视。IS-A 关系的代码表示形式如以下代码段所示:
1 | |
HAS-A关系(组合)
HAS-A 关系是指定两个类之间的关系的另一种方式。例如,您可能要使用住宅中的智能电视。在这种情况下,智能电视和住宅之间存在某种关系。住宅中包含智能设备,即住宅“拥有”智能设备。两个类之间的 HAS-A 关系也称为“组合”。
使用 HAS-A 关系定义 SmartHome 类:
- 在
SmartLightDevice类和main()函数之间,定义SmartHome类:
1 | |
- 在
SmartHome类构造函数中,使用val关键字创建SmartTvDevice类型的smartTvDevice属性:
1 | |
- 在
SmartHome类的主体中,定义会对smartTvDevice属性调用turnOn()方法的turnOnTv()方法:
1 | |
- 在
turnOnTv()方法之后的代码行上,定义会对smartTvDevice属性调用turnOff()方法的turnOffTv()方法:
1 | |
替换子类中的父类方法
替换 SmartDevice 类中的 turnOn() 和 turnOff() 方法:
在 SmartDevice 父类主体中,找到每个方法的 fun 关键字,并在前面添加 open 关键字:
1 | |
在 SmartLightDevice 类的主体中,定义主体为空的 turnOn() 方法:
1 | |
在 turnOn() 方法的主体中,将 deviceStatus 属性设为字符串“on”,将 brightnessLevel 属性设为值“2”并添加 println() 语句,然后向它传递一个 "$name turned on. The brightness level is $brightnessLevel." 字符串:
1 | |
在 SmartLightDevice 子类中,找到 turnOn() 方法的 fun 关键字,并在前面添加 override 关键字
override 关键字会告知 Kotlin 运行时去执行子类所定义方法中包含的代码
在
main()函数中,使用var关键字定义SmartDevice类型的smartDevice变量,该变量会实例化接受"AndroidTV"实参和"Entertainment"实参的SmartTvDevice对象在
smartDevice变量后面的代码行上,对smartDevice对象调用turnOn()方法
1 | |
使用 super 关键字在子类中重复使用父类代码
若要从子类调用父类中被替换的方法,需要使用super关键字
- 在
SmartTvDevice和SmartLightDevice子类中,使用super关键字从SmartDevice类中调用方法:
1 | |
替换子类中的父类属性
在 SmartDevice 父类中 deviceStatus 属性后面的代码行上,使用 open 和 val 关键字定义 deviceType 属性,并将其设置为 "unknown" 字符串:
1 | |
在 SmartTvDevice 类中,使用 override 和 val 关键字定义 deviceType 属性,并将其设置为 "Smart TV" 字符串:
1 | |
可见性修饰符
Kotlin 提供了以下四种可见性修饰符:
public:默认的可见性修饰符。可让系统在任何位置访问声明。对于您想在类外部使用的属性和方法,请标记为 public。private:可让系统在相同类或源文件中访问声明。
某些属性和方法可能仅在类的内部使用,而且您不一定想让其他类使用。您可以使用 private 可见性修饰符标记这些属性和方法,以确保其他类不会意外访问它们。
protected:可让系统在子类中访问声明。对于您想在定义它们的类及其子类中使用的属性和方法,请使用protected可见性修饰符进行标记。internal:可让系统在相同模块中访问声明。internal 修饰符与 private 类似,但您可以从类的外部访问内部属性和方法,只要是在相同模块中进行访问即可。
为属性指定可见性修饰符
为属性指定可见性修饰符的语法是以 private、protected 或 internal 修饰符开头,后跟定义属性的语法。语法如下图所示:

也可将可见性修饰符设置为setter函数,并将修饰符放在set之前
protected:值应能通过类对象在类的外部读取,只有该类及其子类可以更新或写入这个值,您需要对属性的 set() 函数使用 protected 修饰符。
对 deviceStatus 属性的 set() 函数使用 protected 修饰符:
- 在
SmartDevice父类的deviceStatus属性中,将protected修饰符添加到set()函数中:
1 | |
为方法指定可见性修饰符
为方法指定可见性修饰符的语法是以 private、protected 或 internal 修饰符开头,后跟定义方法的语法。语法如下图所示:

1 | |
为构造函数指定可见性修饰符
为构造函数指定可见性修饰符的语法与定义主要构造函数的语法类似,但存在以下两点差异:
- 修饰符是在类名称之后、
constructor关键字之前的位置指定。 - 为主要构造函数指定修饰符时,即使函数内没有任何形参,也必须保留
constructor关键字和圆括号。
语法如下图所示:

例如,您可以查看以下代码段,了解如何在 SmartDevice 构造函数中添加 protected 修饰符:
1 | |
为类指定可见性修饰符
为类指定可见性修饰符的语法是以 private、protected 或 internal 修饰符开头,后跟定义类的语法。语法如下图所示:

例如,您可以查看以下代码段,了解如何为 SmartDevice 类指定 internal 修饰符:
1 | |
理想情况下,您应努力严控属性和方法的可见性,因此请尽可能通过 private 修饰符来声明属性和方法。如果您无法确保它们私有,请使用 protected 修饰符;如果您无法确保它们受到保护,请使用 internal 修饰符;如果您无法确保它们仅在内部使用,请使用 public 修饰符。
| 修饰符 | 可在相同类中访问 | 可在子类中访问 | 可在相同模块中访问 | 可在模块之外访问 |
|---|---|---|---|---|
private |
✔ | 𝗫 | 𝗫 | 𝗫 |
protected |
✔ | ✔ | 𝗫 | 𝗫 |
internal |
✔ | ✔ | ✔ | 𝗫 |
public |
✔ | ✔ | ✔ | ✔ |
定义属性委托
创建属性委托的语法是以变量声明开头,后面一次跟by关键字以及用于为处理getter和setter函数的委托对象。语法如下图所示:

为 var 类型创建委托:
- 在
main()函数之前,创建会实现ReadWriteProperty<Any?,Int>接口的RangeRegulator类:
1 | |
- 在
RangeRegulator类的主体中,替换getValue()和setValue()方法:
1 | |

