Kotlin基础 学习笔记
kotlin是一门基于jvm的编程语言。执行kotlin编译的代码,需要额外的kotlin运行时依赖库。
[infobox title="可空类型"][/infobox]
kotlin默认所有变量不可为空,可空类型必须显示声明,调用可空类型参数前必须做判空处理。这么做的好处在于强制开发者处理所有的空值情况,大大降低了空指针的情况,但对于初学者而言,可能会很不适应。
?.
安全调用操作符 非空情况下才会调用 否则返回null
?:
elvis操作符 处理空值情况下的赋值
var txt: String? = null // 参数类型后面的?表示这是可空类型 txt = txt?.plus("two") // 如果txt不为空 则合并字符串 println(txt) txt = txt ?: "one" // 如果为空 则赋值 one println(txt)
as?
安全转换操作符 如果转换失败则返回null
var p1 : Person? = p as? Person
[infobox title="Pair类"][/infobox]
Pair类就是用于存储了单条键值对的容器
一般可以直接使用 to
这个中缀函数创建 等同于调用构造函数
在构建map或者遍历map的时候,有很大机会使用到。
val pair = 1 to "2" println(pair.first) // 1 println(pair.second) // 2
[infobox title="变量的get set方法"]get和set都属于OOP思想的体现,隐藏了对象的内部结构,也可以实现对输入输出内容的预处理。[/infobox]
class User(val _name: String) { var name = _name get() { println("hello world") // field 为幕后字段 表示存储实际值的字段 return "hello $field" } set(value) { field = "_$value" } }
[infobox title="数据类"][/infobox]
如果你想定义一个实体,则可以使用数据类,此类会自动实现equals hashcode toString get set等方法,需要注意的是,这些方法仅覆盖了构造函数中声明的变量。而不会体现出成员连梁。实现数据类仅需在class 上增加data关键字
data class User(val name: String, val age: Int) fun test() { val user = User("kotlin", 1) println(user.name) }
[infobox title="const val 与 var"][/infobox]
这三个都是变量的修饰符
const 表示编译时常量
val var都属于变量 区别在于val变量只能赋值一次,相当于final类型。
[infobox title="lazy 懒加载"]lazy可以将变量转化为委托,在第一次调用时才初始化变量[/infobox]
private val message :Listby lazy { load() } private fun load() = mutableListOf("1","2") @Test fun test() { println(message) }
[infobox title="const val 与 var"][/infobox]
这三个都是变量的修饰符
const 表示编译时常量
val var都属于变量 区别在于val变量只能赋值一次,相当于final类型。
[infobox title="lateinit 延迟初始化"]当一个变量为非空类型但又没办法立即初始化的时候可以使用这个关键字,该变量的所有调用之处都会插桩一个if判空。一般不推荐使用,使用这个关键词会导致代码的执行不可控。但在spring框架中使用@Resource之类的注解时会很有用[/infobox]
// kotlin 源码 private lateinit var message :String @Test fun test() { println(message) } // 编译后的java代码 private String message; @Test public final void test() { String str = this.message; if (str == null) { Intrinsics.throwUninitializedPropertyAccessException("message"); } System.out.println(str); }
[infobox title="object 单例"]一个普通的class如何实现单例? 直接把class关键字改成object即可[/infobox]
// 编译前 object User { var sex: String = "women" } // 编译后 所有的东西都是static 且只能有一个私有的构造函数 局限性也比较明显 public final class User { @NotNull public static finalUser INSTANCE = new User(); @NotNull private static String sex = "women"; private User() { } @NotNull public final String getSex() { return sex; } public final void setSex(@NotNull String str) { Intrinsics.checkNotNullParameter(str, ""); sex = str; } }
[infobox title="函数式编程 fold"]fold方法作为Iterable接口的扩展函数存在,当然数组也存在这个方法,功能上和java中的reduce差不多。初始值的类型和遍历元素的类型可以不同。[/infobox]
val c = intArrayOf(1,2,3) c.fold(0) { a, b -> // fold的参数值为迭代的初始值 a为上一次迭代的结果值或者初始值 b为本次迭代的对象 a + b // 返回值作为本次迭代的结果值 }.apply { println(this) } // print 6 // apply后的代码块的上下文为当前对象
[infobox title="函数式编程 reduce"]和上面的fold一致,区别在于没有初始值,将第一个迭代对象作为初始值,在这里可能会有一个问题:初始值不会被reduce函数处理。比如遍历一个int数组,所有成员*2后累加,那么索引0的参数不会*2[/infobox]
val c = intArrayOf(1,2,3) c.reduce() { a, b -> a + b }.apply { println(this) }
[infobox title="尾递归"]尾递归是一种特殊的代码优化语法糖,将常见的方法递归调用转为 while(true)的普通代码,主要为了解决调用次数过多导致栈溢出的问题,直接在递归方法上使用tailrec关键词。[/infobox]
fun test() { tail(9999) } tailrec fun tail(n: Int) { when (n) { 1 -> println(n) else -> { println(n) tail(n - 1) //1.最后一步必须是调用自身 2.不能用在try catch finally的代码块里 否则就没办法编译成 while true 啦 } } } // 编译后 public final void test() { tail(9999); } public final void tail(int n) { // 现在已经是扁平的代码了 不存在调用栈爆炸的问题 while (true) { switch (n) { case 1: System.out.println(n); return; default: System.out.println(n); n--; } } }