第十一天笔记
Kotlin 讲义总结
1. 包的定义与导入
1) 包的声明
- 包的声明在代码文件的开头,格式为
package 包名
。 - 包名是工程根目录下的子目录,如
com.jetbrains
。 - 在根目录下创建
com.jetbrains
包,并在此包下创建Demo1.kt
文件,文件第一行声明包名:package com.jetbrains
。
2) 包的导入
- 包的导入格式为
import 资源全名
。 - 在
Demo1.kt
文件中添加demo1
函数,函数全名为com.jetbrains.demo1
。 - 在
Main.kt
文件中导入并使用com.jetbrains.demo1
:
import com.jetbrains.demo1
fun main(args: Array<String>) {
demo1()
}
- Kotlin 默认导入一些包,无需手动导入:
kotlin.*
kotlin.annotation.*
kotlin.collections.*
kotlin.comparisons.*
kotlin.io.*
kotlin.ranges.*
kotlin.sequences.*
kotlin.text.*
2. 程序入口
- 每个 Kotlin 程序都有一个执行入口,在每个 Kotlin 文件中,
main
函数是执行入口:
fun main() {
println("Hello world!")
}
main
函数可以接收可变数量的字符串参数:
fun main(args: Array<String>) {
println(args.contentToString())
}
- 在命令行中运行程序并传递参数:
$ kotlin MainKt a b c
- 在 IDEA 中直接运行时可以在输出目录下找到编译后的
MainKt.class
文件,并手动执行。
3. 标准输入输出
标准输出
print
打印内容到标准输出,不换行。println
打印内容到标准输出并换行。
print("Hello, ")
print("World!")
println("Hello, World!")
println(123)
println(3.14)
标准输入
readln()
读取一行输入,返回String
类型。
val value = readln()
- 使用
toInt()
等方法将输入转换为其他基本数据类型:
val intValue = readln().toInt()
val longValue = readln().toLong()
val doubleValue = readln().toDouble()
- 如果使用 Kotlin 1.6 之前的版本,需使用
readLine()!!
:
val value = readLine()!!
- 使用
split
方法读取多个输入数据,例如空格分割:
val (a, b) = readln().split(" ")
4. 函数
函数定义
- 使用关键字
fun
定义函数:
fun sum(a: Int, b: Int): Int {
return a + b
}
- Kotlin 支持函数的表达式形式,返回值可以自动推断:
fun sum(a: Int, b: Int) = a + b
返回 Unit
类型
- 如果函数不返回任何有意义的值,可以用
Unit
关键字:
fun printSum(a: Int, b: Int): Unit {
println("$a 与 $b 的和为: ${a + b}")
}
Unit
类型可以省略:
fun printSum(a: Int, b: Int) {
println("$a 与 $b 的和为: ${a + b}")
}
参数默认值
- 函数参数可以有默认值:
fun sum(a: Int, b: Int = 0): Int {
return a + b
}
4. 函数
函数调用
-
可以在其他函数中调用函数,例如在
main
函数中调用sum
函数:fun main() { val res = sum(1, 2) println(res) }
-
区别不同函数可以使用函数名和参数列表来确定:
fun sum(a: Int): Int { return a } fun sum(a: Int, b: Int): Int { return a + b } fun printSum(a: Int, b: Int): Unit { println(a + b) }
-
函数调用示例:
fun main() { val a = sum(1) println(a) val b = sum(1, 2) println(b) printSum(1, 2) }
-
函数的调用会涉及到入栈、出栈操作,遵循先入后出的原则。
5. 变量
使用 val
关键字定义只读变量
-
只读变量赋值后不可更改,但可以延迟初始化:
fun main() { val a: Int = 1 // 立即赋值 val b = 2 // 自动推断类型 val c: Int // 提供初始值前,先声明类型 c = 3 // 延后初始化 println("a = $a, b = $b, c = $c") }
使用 var
关键字定义可变变量
-
可变变量可以重新赋值:
fun main() { var a = 1 a = 2 println("a = $a") }
-
val
等价于 Java 的private final
变量,var
等价于private
变量。 -
官方推荐优先使用
val
定义变量,避免副作用。
6. 基本数据类型
数值类型 (Number)
- Kotlin 的数值类型分为两种:整数类型 (Integer types) 和浮点类型 (Floating-point types)。
类型 | 大小 | 最小值 | 最大值 |
---|---|---|---|
Byte | 8bits(1Byte) | -128 | 127 |
Short | 16bits(2Byte) | -32768 | 32767 |
Int | 32bits(4Byte) | -2,147,483,648 (-2^31) | 2,147,483,647 (2^31-1) |
Long | 64bits(8Byte) | -9,223,372,036,854,775,808 (-2^63) | 9,223,372,036,854,775,807 (2^63-1) |
浮点类型
Kotlin 提供两种浮点类型:Float
和 Double
。
类型 | 大小 | 有效位 | 指数位 | 小数位数 |
---|---|---|---|---|
Float | 32bits(4Byte) | 24 | 8 | 6-7 |
Double | 64bits(8Byte) | 53 | 11 | 15-16 |
这些类型都继承自 Number
类。
整数类型
在 Kotlin 中,整数类型的变量初始化时,如果显式指定数据类型,编译器会自动推断:
- 如果值在
Int
类型范围内,默认为Int
类型。 - 如果超过
Int
类型范围,自动转换为Long
类型。
例如:
val int = 1 // Int类型
val long = 2147483648 // Long类型
println(long is Long) // true
可以通过添加后缀 L
显式指定 Long
类型:
val long = 2147483648L // Long类型
默认情况下,变量类型为 Int
,可以通过显式声明指定其他类型:
val byte: Byte = 1 // Byte类型
如果变量值超出类型限制,编译器会报错。
无符号整型
Kotlin 提供了无符号整数类型:
类型 | 数据范围 |
---|---|
UByte | 0-255 |
UShort | 0-65535 |
UInt | 0-2^32-1 |
ULong | 0-2^64-1 |
浮点型变量
初始化浮点型变量时,未显式指定类型默认为 Double
。可以通过添加后缀 F
指定为 Float
:
val double = 1.0 // Double类型
val float = 1.0f // Float类型
如果显式指定浮点类型却赋值为整数,编译器会报错。
JVM 中的数字表示
在 Kotlin 中,==
用于比较数值,===
用于比较对象引用。
对于可空的数字引用:
val a = 128
val b: Int? = a
val c: Int? = a
println(b == c) // true
println(b === c) // false
当值在 -128 到 127 范围内时,对象引用相同:
val a = 127
val b: Int? = a
val c: Int? = a
println(b === c) // true
类型转换
Kotlin 提供类型转换函数如 toInt()
, toDouble()
等,如 sqrt()
函数需要传入浮点型:
val num = 16.toDouble()
println(sqrt(num))
2. 布尔类型
布尔类型的值只有 true
和 false
两种,布尔值可以为空。
val a: Boolean = true
val b = false
val c: Boolean? = null
println(a && b) // false
println(a || b) // true
3. 字符类型
与 Java 不同,Kotlin 中字符类型不属于数值类型,不能与数字进行操作。
val a: Char = 'a'
val b = 'b'
val c = '\n'
print(a)
print(c)
print(b)
可以通过 code
属性获取字符的 ASCII 编码。
val a = 'a'
println(a.code) // 97
4. 字符串类型
字符串类型用 ""
表示。
val s: String = "I'm a coder"
println(s)
可以用 in
关键字判断字符是否出现在字符串中,也可以通过该关键字遍历字符串。
val c = 'I'
val s: String = "I'm a coder"
println(c in s) // true
for (c in s) {
println(c)
}
可以通过 uppercase()
方法将字符串全转换为大写。
println("abcd".uppercase()) // ABCD
可以通过 +
来连接字符串。
val a = "abc"
val b = a + "def"
println(b)
可以通过 """ """
来使用原始字符串,它不会处理转义符,可以包含换行符和任意字符,可以通过 trimMargin()
去掉前导空格。
val s = """
#include <iostream>
using namespace std;
int main() {
cout << "Hello, World!"
}
""".trimMargin()
println(s)
字符串模板:
val i = 1
println("i = $i") // i = 1
val s = "abc"
println("s.length = ${s.length}") // s.length = 3
5. 数组类型
在 Kotlin 中,数组由 Array
类表示。
数组的定义可以通过 arrayOf()
或者 Array
构造函数方式。
val a: Array<String> = arrayOf("a", "b", "c")
val b: Array<Int> = arrayOf(1, 2, 3)
可以通过 forEach()
方法遍历数组。
a.forEach { println(it) }
数组类有 size
属性,可以通过它得到数组的长度。
6. 任意类型
Kotlin 中的 Any
类是所有非空类型的超类,与 Java 中的 Object
类相似。
7. 类型的检查和强制转换
-
类型检查:使用
is
关键字进行类型检查。val s = "Hello" println(s is String) // true
-
不安全类型转换:使用
as
关键字进行强制转换,如果转换失败会抛出异常。val y = null val x: String = y as String // 抛出异常
-
安全类型转换:使用
as?
关键字进行安全类型转换,转换失败时返回null
而不会抛出异常。val y = null val x: String? = y as? String println(x) // null
8. 条件判断、循环语句
if 判断条件
-
基本形式:
val a = 1 val b = 2 var max = a if (a > b) { max = a } else { max = b }
-
表达式形式:
val a = 1 val b = 2 val max = if (a > b) a else b
-
块中的表达式:
val a = 1 val b = 2 val max = if (a > b) { println(a) a } else { println(b) b }
-
与
in
关键字搭配使用:if (1 in 1..10) { println("Yes") } else { println("No") }
when 表达式
-
基本用法:类似于
switch
,但功能更强大。val x: Int = 1 when (x) { 1 -> println(1) 2 -> println(2) else -> println("No condition matched") }
-
多值匹配:
when (x) { 0, 1 -> println("x is 0 or 1") else -> println("x is neither 0 nor 1") }
when 表达式
-
简单匹配:
val x: Int = 1 when (x) { 1, 2 -> println("1 or 2") else -> println("No conditions matched") }
-
传入函数,匹配函数的返回值:
fun getNum(x: Int) = x * 2 fun main() { when (getNum(2)) { 2 -> println(2) 4 -> println(4) } }
-
使用
is
和in
关键字:fun getNum(x: Int) = x * 2 fun main() { when (getNum(2)) { in 1..2 -> println("1..2") } }
循环语句
-
for 循环:
for (i in 1..10) { println(i) // 1 到 10 } for (i in 0..6 step 2) { println(i) // 0, 2, 4, 6 } for (i in 6 downTo 0 step 2) { println(i) // 6, 4, 2, 0 }
-
遍历 Array 对象:
val array: Array<Int> = arrayOf(1, 2, 3, 4) for (i in array) { println(i) }
-
通过索引遍历:
val array: Array<Int> = arrayOf(1, 2, 3, 4) for (i in array.indices) { println(array[i]) }
-
使用 withIndex:
val array: Array<Int> = arrayOf(1, 2, 3, 4) for ((index, value) in array.withIndex()) { println("index: $index, value: $value") }
-
使用
break
和continue
:for (i in array.withIndex()) { if (i == 2) break // 终止循环 if (i == 1) continue // 跳过本次循环 }
while 循环
-
基本用法:
var x = 3 while (x > 0) { println(x) x-- } var y = 3 do { println(y) y-- } while (y > 0)
面向对象
特殊语法糖
class DetailActivity : AppCompatActivity() {
companion object {
const val EXTRA_POSITION = "position"
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_detail)
val pos = intent.getIntExtra(EXTRA_POSITION, -1)
val textView = findViewById<TextView>(R.id.text)
val imageView = findViewById<ImageView>(R.id.image)
val likeView = findViewById<View>(R.id.like)
when (val item = ItemDataManager.items[pos]) {
is Item.TextItem -> {
textView.text = item.text
textView.visibility = View.VISIBLE
imageView.visibility = View.GONE
likeView.run {
isSelected = item.like
setOnClickListener {
item.like = !item.like
isSelected = item.like
}
}
}
is Item.ImageItem -> {
imageView.setImageResource(item.imageResId)
imageView.visibility = View.VISIBLE
textView.visibility = View.GONE
likeView.run {
isSelected = item.like
setOnClickListener {
item.like = !item.like
isSelected = item.like
}
}
}
}
}
}
class MyAdapter(
private val items: List<Item>,
private val onClick: (Int) -> Unit
) : RecyclerView.Adapter<RecyclerView.ViewHolder>() {
class TextViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
val textView: TextView? = itemView.findViewById(R.id.text)
val likeView: View? = itemView.findViewById(R.id.like)
}
class ImageViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
val imageView: ImageView? = itemView.findViewById(R.id.image)
val likeView: View? = itemView.findViewById(R.id.like)
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder {
return when (viewType) {
TEXT_TYPE -> {
val view =
LayoutInflater.from(parent.context).inflate(R.layout.item_text, parent, false)
TextViewHolder(view)
}
IMAGE_TYPE -> {
val view =
LayoutInflater.from(parent.context).inflate(R.layout.item_image, parent, false)
ImageViewHolder(view)
}
else -> {
val view =
LayoutInflater.from(parent.context).inflate(R.layout.item_text, parent, false)
TextViewHolder(view)
}
}
}
override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) {
holder.itemView.setOnClickListener {
onClick(position)
}
when (val item = items[position]) {
is Item.TextItem -> {
val textHolder = holder as TextViewHolder
textHolder.textView?.text = item.text
textHolder.likeView?.run {
isSelected = item.like
setOnClickListener {
item.like = !item.like
isSelected = item.like
}
}
}
is Item.ImageItem -> {
val imageHolder = holder as ImageViewHolder
imageHolder.imageView?.setImageResource(item.imageResId)
imageHolder.likeView?.run {
isSelected = item.like
setOnClickListener {
item.like = !item.like
isSelected = item.like
}
}
}
}
}
override fun getItemCount() = items.size
override fun getItemViewType(position: Int) = when (items[position]) {
is Item.TextItem -> TEXT_TYPE
is Item.ImageItem -> IMAGE_TYPE
}
companion object {
private const val TEXT_TYPE = 1
private const val IMAGE_TYPE = 2
}
}