0%

Kotlin 学习笔记 - 2

前言

本篇继续上一篇 Kotlin 的技术笔记,继续记录一些学习过程中觉得有意思或者比较重要的知识点。

本篇的主要内容来自于笔者在 JetBrains Academy 中所学到的部分。

https://hyperskill.org/ 为 JetBrains Academy 的网站,亦可以通过 IDEA 的 EduTools 来参加学习。

其余有许多优秀的参考资料已经列于上一篇 Kotlin 的博文,如在本篇中有重复使用将额外列出。

本篇博文假设读者具有基本的 Java 编程知识。

参考资料

https://www.cnblogs.com/Jetictors/p/9237108.html 一个很不错的讲解 Kotlin 的博文系列

https://blog.csdn.net/lckj686/article/details/80448471 Kotlin 中 复合符号( ‘?.’ ‘?:’ ‘!!’ ‘as?’ ‘?’ )

正文

Enum

其实之前在好多语言中也见到过 Enum 这个语言特性,但是一直不会用,而且感觉也没有用到,所以一直模模糊糊的状态。那么 Enum 到底使用与做什么的呢?

当你需要管理一组 常数 时,使用 Enum 可以方便高效的管理。

Enum 基础

1
2
3
enum class Rainbow {
RED, ORANGE, YELLOW, GREEN, BLUE, INDIGO, VIOLET
}

在 Kotlin 命名规范中, Enum 其中变量可以使用 全部大写+下划线分割 或者 大驼峰来命名变量。

1
2
3
4
5
6
7
8
9
enum class Rainbow(val color: String) {
RED("Red"),
ORANGE("Orange"),
YELLOW("Yellow"),
GREEN("Green"),
BLUE("Blue"),
INDIGO("Indigo"),
VIOLET("Violet")
}

可以使用类的构造函数来初始化每一个实例。需要使用时,可以按照下面的方法使用。

1
val color = Rainbow.RED.color

除此之外,由于是一个类,还可以编写其方法。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
enum class Rainbow(val color: String, val rgb: String) {
RED("Red", "#FF0000"),
ORANGE("Orange", "#FF7F00"),
YELLOW("Yellow", "#FFFF00"),
GREEN("Green", "#00FF00"),
BLUE("Blue", "#0000FF"),
INDIGO("Indigo", "#4B0082"),
VIOLET("Violet", "#8B00FF");

fun printFullInfo() {
println("Color - $color, rgb - $rgb")
}
}

val rgb = Rainbow.RED
rgb.printFullInfo() // 输出 Color - Red, rgb - #FF0000

Enum 进阶

这里已经有一组内置的 Enum 的方法可以使用。

name 可以让你获得 Enum 实例的名称。

1
2
val color: Rainbow = Rainbow.RED
println(color.name) // RED

ordinal 可以让你获得实例的顺序。

1
2
val color: Rainbow = Rainbow.GREEN
println(color.ordinal) // 3

values() 可以返回包括 Enum 所有实例的一个数组。

比如我们可以利用这个函数编写一个用于校验某个颜色是否属于彩虹的颜色。

1
2
3
4
5
6
7
8
9
fun isRainbow(color: String) : Boolean {
for (enum in Rainbow.values()) {
if (color.toUpperCase() == enum.name) return true
}
return false
}

// 测试部分
println(isRainbow("black")) // false

valueOf() 通过传入 String 类型的名字获得对应的示例。(大小写敏感)如果找不到,就会 throw 一个 IllegalArgumentException

1
println(Rainbow.valueOf("RED")) // RED

可以使用 companion object 关键字来对 Enum 进行静态的功能扩展。下面编写一个函数来通过 Rgb 找具体 Enum 实例。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
enum class Rainbow(val color: String, val rgb: String) {
RED("Red", "#FF0000"),
ORANGE("Orange", "#FF7F00"),
YELLOW("Yellow", "#FFFF00"),
GREEN("Green", "#00FF00"),
BLUE("Blue", "#0000FF"),
INDIGO("Indigo", "#4B0082"),
VIOLET("Violet", "#8B00FF"),
NULL("", "");

companion object {
fun findByRgb(rgb: String): Rainbow {
for (enum in Rainbow.values()) {
if (rgb == enum.rgb) return enum
}
return NULL
}
}

fun printFullInfo() {
println("Color - $color, rgb - $rgb")
}
}

// test
println(Rainbow.findByRgb("#FF0001")) // NULL

Property Accessors

这里高大上的题目其实就是常说的 Getter 和 Setter 了。在 Kotlin 中,会自动生成 Getter 和 Setter 无需手动编写。如果需要做一些自定义,可以参照下面的格式。

1
2
3
var <propertyName>[: <PropertyType>] [= <property_initializer>]
[<getter>]
[<setter>]

举个栗子。比如对于 Client 类,我们不希望年龄被设置为负数,则可以通过下面的方式。

1
2
3
4
5
6
7
8
9
class Client(val name: String) {
var age: Int = 0
set(value) {
field = if (value < 0) 0
else {
value
}
}
}

至于为什么这里用 field 这个关键字,可以自行上网查询解析,我的理解简单来说就是可能会出现递归。

PS : 如果需要使用 backing field ,属性必须被初始化。

当然下面的就是一个不使用 filed 的例子,这里永远不会 get 到 length。

1
2
3
4
class Client(val name: String) {
val length: Int
get() = name.length
}

同时,初始化也可以有一些骚操作。

1
2
3
4
class Field(val length: Int, val width: Int) {
val system: String = "meters"
var totalArea: String = (length * width).toString() + " square $system"
}

可变列表排序

对于 MutableList,sort 可以大致分2种,一种是 sort ,这种会直接对 List 本身进行修改,返回 Unit;一种是 sorted ,不对 List 进行修改,将排序后的 List 返回。另外默认排序从小到大。

1
2
3
4
5
6
7
8
9
10
11
12
class City(val name: String, var degrees: Int)

fun main() {
val firstCity = City("Dubai", 30)
val secondCity = City("Moscow", 5)
val thirdCity = City("Hanoi", 20)

var cityList = mutableListOf(firstCity,secondCity,thirdCity)
cityList.sortBy{it.degrees} // 这里是直接对变量 cityList 进行修改,返回值是 Unit。
}


进阶字符串操作

上一篇博文中写到了一些基本的字符串操作,这里是一些进阶操作。

substring方法返回一个子字符串。前闭后开。

1
2
3
4
val greeting = "Hello"
println(greeting.substring(0, 3)) // "Hel"
println(greeting.substring(1, 3)) // "el"
println(greeting.substring(2)) // "llo"

substringBefore返回自第一个参数(又叫分界符)之前的字符串,如果没有找到分节符,就返回整个字符串,如果设定了第二个参数,就返回第二个参数。

1
2
3
4
// greeting = "Hello"
println(greeting.substringAfter('l')) // "lo"
println(greeting.substringBefore('o')) // "Hell"
println(greeting.substringBefore('z')) // "Hello"

substringBeforeLast 有类似的逻辑,但是从后往前找分界符。

1
2
3
// greeting = "Hello"
println(greeting.substringAfterLast('l')) // "o"
println(greeting.substringBeforeLast('l')) // "Hel"

replace 会将字符串中所有出现的第一个参数换成第二个。

1
2
3
val example = "Good morning..."
println(example.replace("morning", "bye")) // "Good bye..."
println(example.replace('.', '!')) // "Good morning!!!"

replaceFirst适用于仅仅替换第一次出现的情况。

1
2
val example = "one one two three"
println(example.replaceFirst("one", "two")) // "two one two three"

repeat 用于重复某段字符串。

1
println("Hello".repeat(4)) // HelloHelloHelloHello

toLowerCasetoUpperCase用于调节大小写。

1
2
val example = "UPPERCASE String"
println(example.toLowerCase()) // uppercase string

MutableList 使用

MutableList 即可变列表,其实跟 Python 默认的 List 很像,如果是普通的 List , 是不可以进行增删改的。

要创建 MutableList , 可以使用两种方法。

1
2
3
var mutableListA = mutableListOf("1","2","3") // 第一种方法
var testArray = arrayOf("1","2","3")
var mutableListB = testArray.toMutableList() // 第二种方法

也就是说可以直接通过 mutableListOf 来创建一个新的 MutableList, 还可以通过 toMutableList() 来将一个 Array 转化为 MutableList。但是值得注意的是,如果手上有一个 Array ,直接用 mutableListOf 的话,其实会将 Array 作为 MutableList 的一个元素,如果想让 Array 中的元素作为 MutableList 中的元素,要使用 toMutableList.

Array

Array 就是我们常说的数组。可以用 intArrayOf 或者类似的函数生成一个 Array。使用 joinToString函数将其转化为可打印的字符串。

1
2
3
4
5
6
7
8
val numbers = intArrayOf(1, 2, 3, 4, 5) // It stores 5 elements of the Int type
println(numbers.joinToString()) // 1, 2, 3, 4, 5

val characters = charArrayOf('K', 't', 'l') // It stores 3 elements of the Char type
println(characters.joinToString()) // K, t, l

val doubles = doubleArrayOf(1.25, 0.17, 0.4) // It stores 3 elements of the Double type
println(doubles.joinToString()) // 1.15, 0.17, 0.4

此外还可以用构造函数直接生成空的 Array。

1
2
3
4
5
val numbers = IntArray(5) // an array for 5 integer numbers
println(numbers.joinToString())

val doubles = DoubleArray(7) // an array for 7 doubles
println(doubles.joinToString())

Kotlin 还可以使用 first,last 方法来获取第一个和最后一个。

1
2
3
4
5
6
7
8
val strings = arrayOf("abc", "cdf", "efg")

val last = strings[strings.size - 1] // "efg"
val prelast = strings[strings.size - 2] // "cdf"

println(strings.first()) // "abc"
println(strings.last()) // "efg"
println(strings.lastIndex) // 2

使用 contentEquals() 函数比较两个 array。

1
2
3
4
5
6
val numbers1 = intArrayOf(1, 2, 3, 4)
val numbers2 = intArrayOf(1, 2, 3, 4)
val numbers3 = intArrayOf(1, 2, 3)

println(numbers1.contentEquals(numbers2)) // true
println(numbers1.contentEquals(numbers3)) // false

For 与 range、array

当需要使用倒序循环时,使用 downTo , 而不是 4..1 。

1
2
3
for (i in 4 downTo 1) {
println(i)
}

如果需要前闭后开(range默认两边都是闭),使用 until 关键字。

1
2
3
for (i in 1 until 4) {
println(i)
}

使用 step 指定步长。

1
2
3
for (i in 1..7 step 2) {
println(i)
}

Array 可以直接用 in 来遍历。但是也可以用 indices 来遍历。

1
2
3
4
5
6
7
fun main(args: Array<String>) {
val daysOfWeek = arrayOf("Sun", "Mon", "Tues", "Wed", "Thur", "Fri", "Sat")

for (index in daysOfWeek.indices){
println("$index: ${daysOfWeek[index]}")
}
}

String 处理

String 和 charArray 可以通过下面的方法互相转换。

1
2
3
4
5
6
7
val chars = charArrayOf('A', 'B', 'C', 'D', 'E', 'F')

val stringFromChars = String(chars) // "ABCDEF"

val charsFromString = stringFromChars.toCharArray() // { 'A', 'B', 'C', 'D', 'E', 'F' }

val theSameString = String(charsFromString) // "ABCDEF"

也可以将 String 转为 Array。

1
2
val text = "Hello world"
val parts: Array<String> = text.split(" ").toTypedArray() // {"Hello", "world"}

上面使用的 toTypedArray() 是将 List 转为 Array 的函数。

其他

在 Kotlin 中,方法 Method 通常被叫做成员函数 Member Function。

使用下面的代码新建一个Scanner。

1
2
import java.util.Scanner
val scanner = Scanner(System.`in`)

Kotlin 的异常使用 throw。

1
2
3
4
5
6
7
8
9
10
11
12
13
fun calculateSpentMoney(total: Int, itemPrice: Int): Int {
if (total < 0) {
throw Exception("Total can't be negative")
}
if (itemPrice < 0) {
throw Exception("Item price can't be negative")
}
if (itemPrice == 0) {
return 0
}
val amountToBuy = total / itemPrice
return amountToBuy * itemPrice
}