kotlin学习笔记

2020-01-19  本文已影响0人  大泽苦哈哈

kotlin基础知识

基础语法

kotlin中的特殊语法

  1. object
    创建匿名内部类的写法
    kotlin中声明单例的写法
    object修饰的类是单例类
    object Test{
        fun getData(s :String):String{
            return s
        }
    }

    fun main(args: Array<String>) {
        Test.getData("")
    }
  1. String!
    只会在java和kotlin互调的时候出现:编译器认为是一个java类型却用在了kotlin上
  2. Unit

kotlin中的基础说明

  1. kotlin中的Class不是编译为class, 而是KClass
  2. kotlin中没有封装类
    java中的封装类会被转换为基本数据类型
  3. Kotlin类型空值敏感
  4. Kotlin中没有静态变量和静态方法
    object KotlinObject{
        /**
        * 不声明 :  KotlinObject.INSTANCE.sayMessage("s");
        * 声明 : KotlinObject.sayMessage("s");
        */
        @JvmStatic
        fun sayMessage(string: String){
            println(string)
        }
    }
    

java和kotlin中互相调用的注意点

  1. kotlin中接受一个java对象的时候,如果你不能确定这个对象是否为空,一定需要将kotlin的接受对象设为可空类型的,否则会报空指针异常

函数与Lambda闭包

基础

  1. 函数的特性语法
    fun echo(name:String) = prinln("$name")
  1. 嵌套函数
  1. 扩展函数
    kotlin: 可以静态的给一个类扩展它的成员方法和成员变量, 不具备运行时的多态效果
    ----kotlin------
    public fun File.readText(charset: Charset = Charsets.UTF_8): String = readBytes().toString(charset)
    ----Java------
    public class JavaMain {
        public static void main(String[] args) {
            File file = new File("readme.md");
            System.out.println(FilesKt.readText(file, Charsets.UTF_8));
        }
    }
  1. Lambda闭包
    //lambda闭包声明
    var name = {str: String -> println(str)}
    fun main(args: Array<String>) {
        name("name")
    }
    //编译为
    Function1<String, Unit> echo = (Function1)echo.INSTANCE;
  1. 高阶函数
    函数或者lambda的参数又是一个函数或lambda
    fun onlyIf(isDebug: Boolean, block: () -> Unit){
        if(isDebug) block()
    }

    fun main(args: Array<String>) {
        onlyIf(true){
            println("打印日志")
        }
    }
  1. 内联函数

类与对象

  1. 构造函数
  1. 访问修饰符
    private protected public internal
  1. 伴生对象
    class StringUtils {
        companion object{
            fun isEmpty(str: String):Boolean{
                return str == ""
            }
        }
    }
    fun main(args: Array<String>) {
        StringUtils.isEmpty("")
    }
    //Java
    StringUtils.Companion.isEmpty("");
  1. 单例类
    //单例的写法 推荐
    class Single private constructor(){
        companion object{
            fun get() : Single{
                return Holder.instance
            }
        }
        private object Holder{
            val instance = Single()
        }
    }
    fun main(args: Array<String>) {
        val single = Single.get()
    }
  1. 动态代理
    interface Animal{
        fun bark()
    }

    class Dog : Animal{
        override fun bark() {
            println("wang wang")
        }
    }

    class Zoo(animal: Animal): Animal by animal

    fun main(args: Array<String>) {
        Zoo(Dog()).bark()
    }
  1. Kotlin中特有的类
    enum class Command{
        A, B, C, D
    }

    fun exec(command: Command) = when(command){
        Command.A -> {}
        Command.B -> {}
        Command.C -> {}
        Command.D -> {}
    }
    sealed class SupperCommand{
        object A: SupperCommand()
        object B: SupperCommand()
        object C: SupperCommand()
        object D: SupperCommand()
        //扩展子类
        class PACE(var paceL: Int) : SuperCommand()
    }

    fun exec(supperCommand: SupperCommand) = when(supperCommand){
        SupperCommand.A -> println("A")
        SupperCommand.B -> println("B")
        SupperCommand.C -> println("C")
        SupperCommand.D -> println("D")
        is SupperCommand.PACE -> {}
    }

    fun main(args: Array<String>) {
        exec(SupperCommand.A)
    }

kotlin中的高级特性

  1. 解构
    将类拆解并分别赋值
    常用于遍历map
    var map = mapOf<String, String>("key" to "key", "value" to "value")
    for((k,v) in map){
        println("$k ----- $v")
    }
  1. 循环与集合操作符
    var list = arrayListOf<Char>('a','b','c')
    val a = list.map { it - 'a' }.filter { it > 0 }.find{it > 1}
    println(a)
  1. 运算符重载
    . 通过operator关键字
    . 修饰一个方法的时候,表示方法命指代一个运算符
    . operator:将一个函数标记为重载一个操作符或者实现一个约定
    . 一定是定义好的运算符,不能凭空重载运算符,运算符有上线

  2. 作用域函数
    . kotlin内置一系列可以对数据进行变换的函数,与集合操作符号相似,但集合操作符值只能用于集合的操作变换,而作用域函数可以用于所有对象
    . run{...} with(T){...} let{...} apply{...} also{...}

    fun main(args: Array<String>) {
        val user = User("zhangsan")
        //let 和 run 都会返回闭包的执行结果,区别在于let有闭包参数,run没有闭包参数
        var letResult= user.let{"let::${it.javaClass}"}
        //lambda的特性,如果只有一个参数的时候,可以省略不写,用it替代
        var letResult2 = user.let{user : User -> "let::${user.javaClass}"}
        println(letResult)
        val runResult = user.run { "run::${this.javaClass}" }
        println(runResult)

        //also 和 apply都不返回闭包的执行结果,区别在于also有闭包参数,apply没有
        user.also {
            println("also::${it.javaClass}")
        }.apply {
            println("apply::${this.javaClass}")
        }.name = "hello"

        //takeIf 的闭包返回一个判断结果, 为false时,takeIf函数会返回空
        //takeUnless 与 takeIf 刚好相反,闭包的判断结果, 为true时函数会返回空
        user.takeIf { it.name.length > 0 } ?.also { println("姓名为${it.name}") } ?: print("姓名为空")
        user.takeUnless { it.name.length > 0 } ?.also { println("姓名为空") } ?: print("姓名为${user.name}")

        //重复执行当前闭包
        repeat(5){
            println(user.name)
            println(it)
        }

        //with比较特殊,不是以扩展方法的形式存在,而是一个顶级函数
        with(user){
            this.name = "with"
        }

        user.apply {
            this.name = "with"
        }
    }
  1. 中缀表达式
    . 和运算符的重载一样,本质都是一个特殊的函数,通过函数的调用完成
    . 一个函数只有用于两个角色类似的对象时才将其声明为中缀函数, 如果一个方法会改动其接受者,那么不要声明为中缀形式。
    . infix关键字
    fun main(args: Array<String>) {
        println(5 vs 6)
    }
    
    //Int. 表示函数的接收者
    infix fun Int.vs(num: Int): CompareResult =
        if(this - num < 0){
            CompareResult.LESS
        }else if(this - num > 0){
            CompareResult.MORE
        }else{
            CompareResult.EQUAL
        }

    sealed class CompareResult{
        object MORE: CompareResult(){
            override fun toString(): String {
                return "大于"
            }
        }
        object LESS: CompareResult(){
            override fun toString(): String {
                return "小于"
            }
        }
        object EQUAL: CompareResult(){
            override fun toString(): String {
                return "等于"
            }
        }
    }
  1. kotlin中的特殊符号
    . 反引号:解决关键字冲突问题 将一个不合法的字符变为合法的
    不推荐使用
    fun`1234`(){}
    使用场景较小:比如internal 只能用于kotlin中, java当作public
    如果某个类不希望被java访问, 则可以将这个类做一些特殊不合法的字符
    . == 等同于java的equals === 等同于java的 ==
    . typealias
    类似于c和c++中的def 将一个类映射到另一个类上
    可以用在跨平台上,提供兼容性
  2. DSL
    Domain Specific Language
    领域专用语言
    . 提高开发效率 减小沟通成本
    . Lambda 高阶函数 扩展函数 运算符重载 中缀表达式
  3. 总结
    [图片上传失败...(image-bda9ca-1579446616919)]
    [图片上传失败...(image-62dc93-1579446616919)]

语法特性解析

变量、常量与只读

. var和val最本质的区别是val不能有setter,但val 可以通过重写get方法达到改变它的值的效果
. 编译时常量 const val a = 0 const只能修饰object的属性 或 top-level变量 const变量的值必须在编译期间确定下来,所以它的类型只能是String或基本类型
因为对象的值在编译器是不确定的,会随着在运行时分配内存位置不一致,导致对象不是一个固定的对象。

空安全是如何实现的

尝试调用空对象的成员变量或方法会触发空指针异常

  1. 每次引用对象的时候,都去进行空对象判空,在运行期避免对象空指针
  2. 通过静态代码检查,编译插件检查,在编译期避免空指针异常
    kotlin是以上两种方式的结合
内联的特殊情况
  1. 在kotlin中,内部Lambda是不允许中断外部函数执行的
  2. inline的Lambda可以中段外部函数调用
  3. croossinline不允许inline的Lambda中断外部函数执行
  4. noinline拒绝内联 通常用于修饰一个返回函数为内联函数的时候
Kotlin的真泛型与实现方法
  1. kotlin的泛型支持限定泛型的参数类型,支持多个类型, java的泛型会在编译时将泛型参数抹去,变为object
    class Test<T> where T : Callback, T : Runnalble{
        fun add(t: T){
            t.run()
            t.callback()
        }
    }
    //java
    public <T> T fromJson(String json, Class<T> classOfT) throws JsonSyntaxException{...}
    //kotlin
    //inline关键字不能省略 因为必须在编译时知道T的类型 reified表示T是一个真泛型
    inline fun <reified T> Gson.fromJson(json: String): T{
        return fromJson(json, T::class.java)
    }
  1. reified表示T是一个真泛型, 只能修饰函数,不能修饰类,所以类可以通过下面的方法在运行时拿到泛型参数,从而使得类也有真泛型
    //android中实现MVP
    fun main(args: Array<String>) {
        val b = View<Presenter>().presenter
        //等同于
        val a = View.Companion.invoke<Presenter>().presenter
    }

    class View<T>(val clazz: Class<T>){
        val presenter by lazy { clazz.newInstance() }
        //伴生对象会在构造函数调用之前,也就是类被加载到类加载器的时候创建好
        companion object{
            //重载了invoke操作符,同时调用了构造函数,并将当前的泛型类型,传递给了view的构造函数,所以在运行时可以拿到clazz运行变量
            inline operator fun <reified T> invoke() = View(T::class.java)
        }
    }

    class Presenter{
        override fun toString(): String {
            return "Presenter"
        }
    }

协程

Kotlin中的相关注解

  1. @JvmOverloads: 在有默认参数值的方法中使用@JvmOverloads注解,则Kotlin就会暴露多个重载方法。
上一篇 下一篇

猜你喜欢

热点阅读