变量和函数
变量
- var (variable) 可变量
- val (value) 不可变量
- 初始化
val a: Int = 10 val b = 10
函数
- 说明: 函数(function)与方法(method)是同一个概念,Kotlin叫函数,Java叫做方法
- 写法:
- 基本写法
// 有返回值 fun methodName(param1: Int, param2: Int): Int { return 0 } //没有返回值可以省略 Int fun methodName(param1: Int, param2: Int) { //todo }
- 函数中只有一句话时可以简写
//下面两个方法等价 fun largerNumber(num1: Int, num2: Int): Int = max(num1, num2) fun largerNumber(num1: Int, num2: Int) = max(num1, num2)
逻辑控制
if 条件语句
- if 可以有返回值
或fun largerNumber(num1: Int, num2: Int) = if (num1 > num2) { num1 } else { num2 }
fun largerNumber(num1: Int, num2: Int) = if (num1 > num2) num1 else num2
when 条件语句
- 语法
when (任意类型参数) { 匹配值 -> { 执行逻辑 } else -> { 执行逻辑 } }
- when 语句中如果要返回值,必须要有 else , 除非是个密封类对象
- 用法1:
fun getScore(name: String) = when (name) { "Tom" -> 86 "Jim" -> 77 "Jack" -> 95 "Lily" -> 100 else -> 0 }
- 用法2, 不带参数的 when
fun getScore(name: String) = when { name.startsWith("Tom") -> 86 name == "Jim" -> 77 name == "Jack" -> 95 name == "Lily" -> 100 else -> 0 }
- 用法3,类型匹配
fun checkNumber(num: Number) { when (num) { is Int -> println("number is Int") is Double -> println("number is Double") else -> println("number not support") } }
循环语句
-
区间概念
0..10
对于数学中的 [0, 10],只能是升序,
0 until 10
即 [0, 10),只能是升序
10 downto 0
即 [10, 0],只能是降序 -
例子
for (i in 0 until 10) { print(i) //结果:0123456789 } // step 按指定值递增 for (i in 0 until 10 step 2) { print(i) //结果:02468 }
-
遍历集合
val list = listOf("a", "b", "c") for (v in list) { println(v) } val map = mapOf("key1" to 1, "key2" to 2, "key3" to 3) for ((k, v) in map){ println("$k = $v") }
面向对象编程
创建对象不需要 new
类、方法默认都是 public final
继承与构造函数
-
创建 public final 的类, 默认即 public final
class Person { }
-
创建 public 且可被继承的类
open class Person { //方法也要加 open, 否则默认为 public final 修饰 open fun eat() { println("person eat") } }
-
继承
class Student() : Person() { }
-
主构造函数:写在类名上的构造函数;
-
次构造函数 constructor:除主构造函数外的构造函数
-
init 结构体, 用于通过主构造函数创建对象时初始化数据
class Student() : Person() { init { println("Student created") } } // 当没有次构造函数且没有主构造函数时默认为有无参构造函数 class Student : Person() { init { println("Student created") } }
-
注意:所有次构造函数必须直接或间接调用主构造函数
class Student(val grade: String, name: String, age: Int) : Person(name, age) { init { println("Student created") } //通过 constructor 创建次构造函数 constructor() : this("g") { } constructor(grade: String) : this(grade, "", 0) { } } open class Person(val name: String, val age: Int) { }
-
只有次构造函数的情况,Student和Person都不用括号
class Student : Person { init { println("Student created") } //大括号可以省略 constructor() : this("g") constructor(grade: String) : this(grade, "", 0) constructor(grade: String, name: String, age: Int) : super(name, age) { //do sth } } open class Person(val name: String, val age: Int) { }
接口
-
实现接口的写法和继承的写法类似
interface Study { fun readBooks() //接口可以有默认方法 fun doHomework() { println("do homework") } } class Student(name: String, age: Int) : Person(name, age), Study { override fun readBooks() { println("student do readBooks") } }
-
Java 和 Kotlin 函数可见性修饰符对照表
修饰符 Java Kotlin public 所有类可见 所有类可见(默认) private 当前类可见 当前类可见 protected 当前类、子类、同一包路径下的类 当前类、子类 default 同一包路径下的类可见(默认) 无 internal 无 同一模块中的类可见
数据类和单例类
- 数据类, 加 data 关键字,默认会创建 get/set/equal/hashCode/copy/toString 等函数
如果不加 data 关键字,则只会生成 get/set 方法 (val 只会生成 get 方法)data class Cellphone(val brand: String, val price: Double)
- 单例,将 class 改为 object 即可,然后通过
类名.方法名()
调用,类似 Java 的静态方法
kotlin中一般用单例作为工具类
等价于object Singleton { fun singleTest() {} }
public final class Singleton { public static final Singleton INSTANCE; static { INSTANCE = new Singleton(); } public final void singleTest() { } private Singleton() { } }
Lambda 编程
集合
-
list/set 集合
//不可变集合:不能增、删、改 val list = listOf<String>("A", "B", "C") //可变集合 val mutableList = mutableListOf<String>("A", "B", "C") mutableList[2] = "b" mutableList.add("D") val set = setOf<String>("A", "B", "C") val mutableSet = mutableSetOf<String>("A", "B", "C")
-
map 集合
val map = mapOf("A" to 65, "B" to 66, "C" to 67) val mutableMap = mutableMapOf("A" to 64, "B" to 66, "C" to 67) // 增和改的写法相同,下面依次为 增 改 删 mutableMap["D"] = 68 mutableMap["A"] = 65 mutableMap.remove("B") // 两种遍历方式 for (entry in mutableMap) { println("${entry.key} = ${entry.value}") } for ((key, value) in mutableMap) { println("$key = $value") }
集合的函数式 API
-
map 包装效果,将集合中的对象包装为另一个对象后返回新的集合
-
filter 过滤满足条件的对象并返回新的集合
// 将 Char 集合转为 Int 集合并找到集合中 >65 的值并遍历打印 val list = listOf<Char>('A', 'B', 'C') list.map { it.toInt() } .filter { it > 65 } .forEach { println(it) } //66 67
-
any 有一个满足条件则返回 true
-
all 所有都满足条件则返回 true
val any: Boolean = list.any { it == 'A' } //true val all: Boolean = list.all { it == 'A' } //false
-
maxBy 按给定比较参数返回最大值
val max: Char? = list.maxBy { it.toInt() } //'C'
-
Lambda 表达式简化过程演示
val lambda = { c: Char -> c.toInt() } list.maxBy(lambda) list.maxBy({ c: Char -> c.toInt() }) list.maxBy({ c -> c.toInt() }) list.maxBy({ it.toInt() }) list.maxBy{ it.toInt() }
Java 函数式 API 的使用
- 匿名类的写法, 加 object 关键字
Thread(object : Runnable { override fun run() { //do sth } }).start()
- 可简写为
监听点击事件的例子Thread(Runnable { // do sth }).start() // 如果接口中只有一个方法,可以将接口名省略 Thread({ // do sth }).start() // 当 Lambda 表达式是方法的最后一个参数时,可以将表达式移到方法的外边。 Thread() { // do sth }.start() // 同时如果 Lambda 表达式还是方法的唯一参数,还可以将方法的括号省略 Thread { // do sth }.start()
// 完整写法 tv.setOnClickListener(object : View.OnClickListener { override fun onClick(v: View?) { } }) // 省略 object 和 方法名 tv.setOnClickListener(View.OnClickListener { }) // 省略类名 tv.setOnClickListener({ }) // 移到方法括号外边 tv.setOnClickListener() { } // 省略方法括号 tv.setOnClickListener { }
空指针检查
-
?. 判空
var l = str?.lenth
等价于
if (str != null) { return str.length(); } else { return null; }
-
?: 三元运算
val s = str ?: ""
等价于
val s = str == null ? "" : str;
val l = str?.length ?: 0 // 上面句子可分解为 val l = str?.length l = l ?: 0
等价于
val l = str == null ? 0 : str.length;
-
!!. 非空断言,强制编译器不做空指针检查,如
str!!.length
-
let 标准函数,配合 ?. 操作符使用, 判空的同时处理数据
str?.let { println(it.length) println(it) }
Kotlin 小魔术
字符串内嵌表达式
-
变量前加 $ 符,如果是表达式用大括号括起来
val a: String = "abc" val b: String = "cdf" println("a = $a, b = $b, a+b = ${a+b}")
-
如果要打印"$"符,加斜杠
\$
函数参数默认值
小知识点
- 通过类名获取class对象:
Person::class.java
即 Java 的Person.class
- 通过对象获取class对象:
person.javaClass
即 Java 的person.getClass()
- Any 是 Kotlin 中所有类的基类,相当于 Java 中的 Object
标准函数和静态方法
标准函数即是在 Standard.kt 里定义的函数
标准函数 let、with、run、apply
- let
obj.let{ it -> }
, 最后一行作为返回值 - with
with(obj) { 这里面调用的都是‘obj’对象的方法 }
, 最后一行作为返回值 - run
obj.run { 这里面调用的都是‘obj’对象的方法 }
, 最后一行作为返回值 - apply
obj.apply { 这里面调用的都是‘obj’对象的方法 }
, 返回值为‘对象’本身 - repeat
repeat(n) {}
, 重复 Lambda 表达式 n 次
静态方法
关键字
- inner class, 定义内部类
- companion object { }, 定义静态成员/函数
- const val, 常量,只能在companion object、单例类或顶层作用域中使用
- vararg, 英文意思variable argument, 对应 Java 的可变参数列表:
(int... array)
延迟初始化和密封类
延迟初始化 lateinit var
- lateinit 后面只能是 var, 不能用于 val
- 判断是否已经初始化
::adapter.isInitialized
- 例子
private lateinit var adapter: MsgAdapter fun method(){ if(!::adapter.isInitialized){ adapter = MsgAdapter() } }
密封类 sealed class
- 用法:
sealed class 类名
- 作用: when 语句传入密封类对象作为条件时,编译器会自动检查该类所有的子类,并强制要求你将每一个子类所对应的条件全部处理
- 优点: 不需要 else 了; 编译器会自动检测所有条件防止遗漏,更安全了。
- 注意: 密封类及其所有子类只能定义在同一个文件的顶层位置,不能嵌套在其他类中。
- 但是,我没测不出效果
扩展函数和运算符重载
扩展函数
- 建议:
哪个类添加扩展函数,就定义一个同名的 Kotlin 文件
最好将扩展函数定义成顶层方法,这样可以让扩展函数有全局的访问域 - 语法:
fun String.lettersCount(): Int {
var count = 0
for (char in this) {
if (char.isLetter()) {
count++
}
}
return count
}
- 以下是 Kotlin 自带的扩展函数的例子
IO流对象.use {}
,用于自动关闭流
BufferedReader.forEachLine {}
, 用于读取每一行并回调到 Lambda 表达式中
运算符重载
-
语法, 函数前加关键字:operator
//重载加法 operator fun plus(money: Money) { }
-
语法糖表达式与实际调用函数对照
法糖表达式 实际调用函数 中英文解释 + b a.plus(b) 加 - b a.minus(b) 减 * b a.times(b) 乘 / b a.div(b) 除 divide % b a.rem(b) 求余 remainder ++ a.inc() 自增 increase – a.dec() 自减 decrease a a.unaryPlus() 一元加 a a.unaryMinus() 一元减 a a.not() 非 == b a.equals(b) 恒等于 > b, a < b, a >= b, a <= b a.compareTo(b) 大于 …b a.rangeTo(b) 区间 [b] a.get(b) 获取 [b] = c a.set(b, c) 设置 in b b.contains(a) 包含
高阶函数
高阶函数定义
- 语法:
(String, Int) -> Unit
括号内是函数的参数,Unit是返回值,Unit表示没有返回值,相当于 Java 的 void - 函数引用:
::plus
, 将普通函数 plus 变为方法参数val result = num1AndNum2(num1, num2, ::plus)