Kotlin 教程(一)

一、Hello World

fun main() {
    println("Hello world")
}

二、基础语法

2.1 变量声明

var a1: Int = 0 // 可变参数
var a2 = 0 // Kotlin 有出色的类型推断机制,变量类型可以省略
val b = 1 // 不可变参数

2.2 函数声明

// 函数名 test,携带参数 a,返回类型 String
fun test1(a: Int): String {
    return a.toString()
}

// 如果函数体只有一行,可用“=”表示,返回类型可省略
fun test2(a: Int) = a.toString()

2.3 条件语句

if 条件

// if 语句拥有返回值,返回值就是每个条件中最后一行代码的返回值
fun max(a: Int, b: Int) = if (a > b) a else b

when 条件

// when语句非常灵活,可以传入一个任意类型的参数,也可以什么都不传
fun getSeason(month: Int) = when (month) {
    // 判断是否在范围内
    in 3..5 -> "Spring"
    in 6..8 -> "Summer"
    in 9..11 -> "Autumn"
    // 多个条件用","连接
    12, in 1..2 -> "Winter"
    else -> throw IllegalArgumentException("The month value is out of range")
}

2.4 循环语句

// in Range,输出 1~10
for (i in 1..10) {
    println(i)
}
// until表示左开右闭,这在遍历列表下标时非常好用。比如这里指[0,10),输出 0~9
for (i in 0 until 10) {
    println(i)
}
// downTo 逆序,输出 10~1
for (i in 10 downTo 1) {
    println(i)
}
// step 设置步长,输出 1 3 5 7 9
for (i in 1..10 step 2) {
    println(i)
}
// while 循环,输出 1~10
var i = 0
while (i++ < 10) {
    println(i)
}

2.5 构造函数

// 默认是一个无参构造函数
class Person

// 携带参数的构造函数
class Person(name: String, age: Int, gender: String)

// 参数前带有 val 或者 var 时,会在类中生成一个相应的字段。否则参数只能在构造函数中使用
class Person(val name: String, var age: Int, gender: String)

// 每个参数都可以设置默认值,没有设定默认值的参数必须在构建时传入
class Person(name: String, age: Int = 0, gender: String = "secret")

// 设定了默认值的构造函数如果加上 @JvmOverloads 注解,在 Java 类中使用时,就会生成对应的构造方法。
// Kotlin 中 @JvmXxxx 这一类注解都是为了兼容 Java 使用的,纯 Kotlin 开发不需要这些注解
class Person @JvmOverloads constructor(name: String, age: Int = 0, gender: String = "secret")

// 使用 constructor 自定义构造函数
class Person {
    constructor(name: String, age: Int, gender: String)
    constructor(name: String, gender: String)
}

2.6 可见性修饰符

修饰符 Java Kotlin
public 所有类可见 所有类可见(默认)
private 当前类可见 当前类可见
protected 当前类、子类、同一包路径下的类可见 当前类、子类可见
default 同一包路径下的类可见(默认)
internal 同一模块中的类可见

三、特性

3.1 数据类

// 自动生成 equals、hashCode、toString 方法
data class Person(val name: String, val age: Int)

// 输出 Person(name=name, age=12)
println(Person("name", 12))
// 输出 true
println(Person("name", 12) == Person("name", 12))

3.2 单例

// 使用 object 关键字就表示这是一个单例类
object Singleton

3.3 集合

// 初始化一个列表 [1, 2, 3]
val list = listOf(1, 2, 3)
// forEach 逐个遍历列表, 输出 1 2 3
list.forEach {
    println(it)
}
// map 将列表转换成 ["1","2","3"]
val strList = list.map { it.toString() }
// 排序 [1, 2, 3]
val sortedList = list.sorted()
// 按某个值排序 [1, 2, 3]
val sortedList = list.sortedBy { it }
// 过滤出符合条件的列表 [3]
val filterList = list.filter { it > 2 }
// 是否存在某个值满足条件 true
val exist1 = list.any { it == 1 }
// 是否所有值都满足条件 false
val allAbove1 = list.all { it > 1 }

// 可变列表
val mutableList = mutableListOf(1, 2, 3)
// 其他列表类型 Map、HashMap、Set等,操作类似
val map = mapOf(1 to "one", 2 to "two", 3 to "three")
val hashMap = hashMapOf(1 to "one", 2 to "two", 3 to "three")
val set = setOf(1, 2, 3)

3.4 空检查

// 变量类型后加 ? 表示这个变量为可空类型,不加的话表示不可空类型,不可空类型不能设置成 null
val string1: String? = "Android"
val string2: String? = null
// "?." 表示不为空时才执行后面的代码,输出 ANDROID
println(string1?.toUpperCase())
// string2 为空,所以 toUpperCase 不执行,输出 null
println(string2?.toUpperCase())
// "?:" 表示为空时才取后面的值,string1 不为空,输出 Android
println(string1 ?: "it's null")
// string2 为空,输出 it's null
println(string2 ?: "it's null")

// "!!" 表示断定它不为空,不进行空检查,不建议使用,可能导致空指针
println(string1!!.toUpperCase())

3.5 字符串内嵌

val string1: String? = "Android"
val string2: String? = null
// 使用 "$" 内嵌变量,使用 "${}" 内嵌表达式
// 输出 string1 = Android, is string2 null ? true
println("string1 = $string1, is string2 null ? ${string2 == null}")

3.6 标准函数

let

val string1: String? = "Android"
// 将变量携带到代码块中执行一段代码,代码块最后一行作为返回值。
// b = "ANDROID"
val b = a.let {
    println(it)
    it.toUpperCase()
}

run

val a = "Android"
// 将变量携带到代码块中执行一段代码,并且代码块中此变量作为上下文,也就是说代码块里面可以直接调用 a 类中的函数。代码块最后一行作为返回值。
// b = "ANDROID"
val b = a.run {
    println(this)
    toUpperCase()
}

with

val a = "Android"
// with 和 run 效果一模一样,只不过是调用方式的区别:run 是直接用参数调用 run 方法,而 with 可以直接使用,参数传到 with 里
// b = "ANDROID"
val b = with(a) {
    println(this)
    toUpperCase()
}

apply

val a = mutableListOf(1)
// 和 with、run 类似,将变量携带到代码块中执行一段代码,并且代码块中此变量作为上下文,不同的是 apply 将调用对象本身作为返回值
// b = [1, 2, 3]
val b = a.apply {
    add(2)
    add(3)
}

repeat

// repeat 表示重复 n 次,输出 0~9
repeat(10) {
    println(it)
}

takeIf

val a = 2
// 如果 a 满足条件,则取 a,否则取 null,这里 b 的类型为 Int?
// b 的值为 2
val b = a.takeIf { a > 1 }

takeUnless

val a = 2
// 与 takeIf 相反
// c 的值为 null
val c = a.takeUnless { a > 1 }

3.7 静态方法

// 上文中讲到的单例类中的方法都是静态方法
object Singleton {
    fun test() {
        println("it's a static method")
    }
}

class Person {
    // 每个类都可以定义一个伴生对象,伴生对象中的方法都是静态方法
    companion object {
        fun test() {
            println("it's a static method")
        }
    }
}

// 不在任何一个类中的方法被称为顶层方法,顶层方法都是静态方法
fun test() {
    println("it's a static method")
}


object Singleton {
    // 放在类中的静态方法添加 @JvmStatic 注解后,在 Java 代码中就可以像静态方法那样调用了
    @JvmStatic
    fun test() {
        println("it's a static method")
    }
}

3.8 延迟初始化

// 使用 lateinit 关键字表示待会再初始化此变量,这个关键字在定义非空类型时非常有用。
// 否则我们不得不将全局变量申明为可空类型并为其赋值为 null,导致使用时的不方便
lateinit var a: String

// 判断延迟初始化变量是否已经被初始化
::a.isInitialized

3.9 密封类

不使用密封类存在的问题

// 定义了一个 Result 接口
interface Result
// 定义了两个子类实现了 Result 接口
class Success : Result
class Failure : Result

fun test(result: Result) = when (result) {
    is Success -> 1
    is Failure -> 2
    // 我们知道这里的 result 只可能是 Success 或者 Failure,但 when 语句需要返回值时,必须保证每一种对应的情况都有一个返回值。所以我们不得不编写这个多余的 else 条件,才能保证编译通过
    else -> throw Exception("impossible")
}

使用密封类后

// sealed 关键字表示这是一个密封类
sealed class Result

class Success : Result()
class Failure : Result()

// when 语句中传入密封类时,Kotlin 会自动检测密封类有哪些子类,只要每个子类都处理了,就不再需要 else 语句了
fun test(result: Result) = when (result) {
    is Success -> 1
    is Failure -> 2
}

3.10 扩展函数

扩展函数使得我们很方便的为一个类扩展新的方法

// 定义扩展函数很简单,使用"类名.新方法名"即可。这里我们定义的方法用来为字符串后缀一个 "666"
fun String.add666(): String {
    return "${this}666"
}

fun main() {
    val a = "Android"
    val b = a.add666()
    // 输出 Android666
    println(b)
}

3.11 运算符重载

class Person(val name: String) {
    // 使用 operator 关键字重载运算符,plus 方法表示重载 "+" 运算
    operator fun plus(person: Person): List<String> {
        return listOf(this.name, person.name)
    }
}

fun main() {
    val a = Person("小明")
    val b = Person("大明")
    // 重载后,就可以用 "+" 运算符来调用类中的 plus 方法
    val c = a + b
    // 输出 [小明, 大明]
    println(c)
}

可重载的运算符以及对应的方法

运算符 对应方法
a + b a.plus(b)
a - b a.minus(b)
a * b a.times(b)
a / b a.div(b)
a % b a.rem(b)
a++ a.inc()
a– a.dec()
+a a.unaryPlus()
-a a.unaryMinus()
!a a.not()
a == b a.equals(b)
a > b a.compareTo(b)
a < b a.compareTo(b)
a >= b a.compareTo(b)
a <= b a.compareTo(b)
a…b a.rangeTo(b)
a[b] a.get(b)
a[b] = c a.set(b, c)
a in b b.contains(a)

其中,a.compareTo(b) 必须返回 Int,返回正数表示>,0表示=,负数表示<。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章