记录N年前学习kotlin的基础知识点

107 阅读11分钟
### kotlin基础知识
1. 约定:
    1. kotlin鼓励使用不可变的,而不是可变的;
    2. Kotlin标准库使用更加精简的语法,为Java库函数提供了众多的包装器
    3. 可以省略行尾的分好“  ;’’
    4. 在Kotlin中 ,if是一个带有结果值的表达式,而在Java中 if只是声明;表达式 有值,而声明 没有值;同样的还有when
    5. 声明与表达式
        1. 表达式有值,它可以用作另一个表达式的一部分,
        2. 声明没有值,一个声明总是闭合块中的一个顶层元素,而没有自己的值
        3. 在Java中所有的控制结构都属于声明(statement),而在Kotlin中,除了循环之外的大多数控制结构都是表达式,比如when
        4. 另一方面:赋值在Java中是表达式,但是在kotlin中却是声明
    6. Kotlin是一种静态语言,在编译期间 kotlin就可以判断每个变量以及表达式的类型;对于表达式主体函数,编译器能够分析用作主函数主体的表达式,并使用表达式的类型作为函数的返回类型。【类型推断】
    7. 在Kotlin中,应该尽量使用val声明所有变量,仅在必要的时候将变量声明为var
    8. 使用不可变引用,不可变对象和函数没有副作用,反而会使得代码更接近函数式风格
    9. 在kotlin中,代码中的最后一句表达式的结果作为返回值,这个规则在所有可以使用代码并且需要返回结果的地方都是有效的;
    10. kotlin中所有的类都有一个共同的超类Any (equals(),hashCode(),toString(),没有其他任何成员)
    11. 默认情况下,kotlin中所有的class类都是final的。如果需要可继承,则使用open关键字声明
    12. kotlin中没有静态方法,但是可以用object对象,或者伴随对象,通过类直接访问其类成员
    13. kotlin中外部类不能访问内部类私有成员



2. 函数:
    1. 语法:fun funName(argName : argType) : ReturnType { };这里的大括号包含函数体
    2. 语法2fun funName(argName : argType) : ReturnType = 表达式 
    3. 函数可以声明在文件顶层,即该模块的顶级文件中,使得整个模块内可被访问;
    4. 函数可以不放到类中
    5. 数组是一个类
    6. 注意 只有表达式函数体,函数才允许忽略函数返回值
    7. 函数声明,参数,泛型,默认参数值
    8. 注意:参数默认值与Java
        1. Java中没有默认值的概念,当你在Java中调用带有参数默认值的kotlin函数时,你必须要显示的指定所有参数的值。如果你经常需要从Java中调用一个函数并想让Java调用者易于使用,你可以用@JvmOverloads 对他进行标注。他将指示编译器以从最后一个参数开始,逐个忽略每个参数的方式生成重载的Java函数
    9. 扩展函数:fun String.lastChar() : Char = this.get(this.length - 1);其中String为接收器类型,this为接收器对象
        1. 其中this可以省略,在扩展函数中,你可以直接访问你扩展的类的函数与属性,但是不允许你打破封装,即不能访问私有或受保护的属性成员
        2. 扩展函数只是静态函数的一个高效语法糖,
        3. 方法覆盖在kotlin对平常的成员函数是有效的,但是你不能覆盖一个扩展函数。扩展函数并不是类的一部分,它们是在类的外部。尽管你可以为某个基类和它的子类定义同样的名字和参数类型的扩展函数,但是被调用的函数依然只依赖于变量被声明的类型有关!!!而不依赖于变量值的运行时类型;
        4. 一个类中有成员函数和扩展函数有着相同的签名,成员函数总是优先!· 
3. 变量:
    1. 关键字:
        1. val 不可变的引用,其内容在初始化后,不可以变化,且只能初始化一次;对应Java中的final变量
        2. var 可变的的引用,其内容可以变化,类型不能发生改变!!!;对应Java中的非final变量,(因为编译器仅仅在初始化的时候确定变量类型)
    2. 语法:val args : ArgsType = argsValue,其中ArgsType可以省略,当且仅当 argsValue有可知类型
4. 类和对象:
    1. 语法:[public 默认] class Person(val name:String , var isMarried : Boolean) :Human() , OnClickListener  
    2. val声明的属性是 只读的;//编译器生成一个字段,一个getter函数
    3. var声明的属性是:可读可写 //编译器生成 一个字段,一个getter函数,一个setter函数
    4. 当person.name可以直接调用属性,实际上是访问的getter函数;你自己也可以改写getter函数
    5. 自定义getter
        1. 相当于覆盖java中的getter
    6. 知识概括
        - 类和继承
            - 类名+类头(参数,主/副构造函数)+类体;如果没有类体,可以省略花括号{};
                - 主构造函数跟在类头,无修饰符可以省略constructor关键字,默认是public
                - 主构造函数不能包括任何代码,初始化代码可以放到初始化块init{}中,初始化块init中可以使用主构造函数的参数,
                - 主构造函数的参数如果有val/var,则为类成员变量
                - 次构造函数,每个次构造函数需要委托给主构造函数(可以直接或间接通过其他次构造函数)
                - 若无构造函数,则默认生成一个无参主构造函数。
            - 创建类的实例:
                - 没有new关键字,直接调用构造函数即可
            - 包含
                - 构造函数,初始化块init{}
                - 函数fun
                - 属性
                - 嵌套类,内部类
                - 对象声明
            - 继承:    
                - 使用:来继承基类,
                - 如果该类有一个主构造函数,其基类可以(并且必须)用基类的主构造函数参数就地初始化
                - 如果没有主构造函数,那么次构造函数必须要用super关键字初始化其基类型(或者间接委托)
                - 一个未开放的类(即final类),其成员不可open修饰;
                - 一个override的成员 本身是open的,可以在子类中继续被覆盖;如果想禁止覆盖,则final修饰
                - 属性覆盖类似成员方法覆盖
                    - 超类中声明的属性,在子类中重新声明 则必须override,并且类型兼容
                    - var属性可以覆盖一个val属性,反之不行;val属性本质上声明了一个getter方法。
            
        - 属性和字段
            - 只读val(不可变),读写var(可变)
            - 幕后字段 field只能用在属性的访问器中
                - 如果你的需求不符合这套“隐式的幕后字段”方案,则可以使用幕后属性
            - 编译器常量const
                - 位于顶层,或者是object的一个成员
                - 用String或原生类型 值初始化
                - 没有自定义getter
            - 惰性初始化
                - lateinit 修饰符;只能出现在类体中声明的var变量,并且仅当该属性没有自定义getter,setter,必须是非空,不能是原生类型
                - 初始化前访问惰性属性,会抛出异常。
        - 接口
            - 既可以有抽象方法声明,可以有方法实现
            - 可以定义属性,要么是抽象的,要么是提供getter,且不能有幕后字段
            - super<指定接口、指定父类>解决覆盖问题
        - 可见性修饰符
            - 类,对象,接口,构造函数,方法,属性都有可见性修饰符 
                - private,私有
                - protected, 子类亦可访问
                - internal,模块内
                - public(默认是public)
            - 函数,属性,类,对象,接口都可以在顶层声明
        - 扩展
            - 扩展函数 fun String.xxx(args){...}
                - 为了在接收者类型表达式中使用泛型,需要在函数名前面声明泛型参数 
                ```
                    fun <T> MutableList<T>.swap(args){...}
                ```
                - 扩展是静态解析的。并不能在类中插入该段代码,仅仅是通过该类型的变量用点表达式去调用这个新函数
                - 在函数绑定这块,扩展函数始终跟随声明时接收者类型;
                - 如果存在相同的成员函数和扩展函数,则成员函数优先于扩展函数。
            - 扩展属性(类似扩展函数)
                - 没有初始化器,意味着不能做初始值赋值;只能有显示的getter/setter定义
            - 伴随对象也可以有函数扩展和属性扩展
        - 泛型
            - 型变
                - 声明处型变:关键字out in
                - 类型投影
            - java中的泛型:略 
                - extends限定类型上界,super
        - 分类
            - 抽象类
            - 数据类
                - 关键字data
                - 主构造函数必须有参数,且需要val/var修饰
                - 不能是abstractopensealed,或者内部的
                - 如果数据类需要一个无参构造函数,则需要在主构造函数的参数都赋值默认值
                - 复制
                - 解构声明
            - 密封类
                - :用来表示受限的类继承结构。当一个值为有限几种的类型时。比如枚举(与枚举不同的是,每个枚举常量只存在一个实例,而sealed类可以存在多个实例)
                - 可以继承
                - 好处是在使用when的时候,不必多写else分支
            - 嵌套类
                - 嵌套在其他类中 类似java中的静态内部类 val demo = Outer.Inner();
            - 内部类:
                - 关键字inner,能够访问外部类的成员,内部类对外部类持有一个引用
            - 匿名内部类:使用对象表达式创建的匿名内部类实例 ;
            - 枚举类
                - 基本用法,关键字enum 参考java
                - 枚举常量
        - 对象表达式与对象声明
            - 对象表达式:声明立即实例化,
            - 对象声明:
                - object XXX:YYY(),Z {}...};
                - 第一次访问到时再进行初始化(即延迟初始化lateinit)
                - 不是表达式,就像声明变量一样
                - 可以继承父类
                - 不能在局部作用域(即嵌套在函数内部),但是可以嵌套在其他对象声明中,后者是非内部类中
            - 伴随对象:就是在类XXX内部的对象声明,;可以通过XXX.访问
                 - 在类解析时就被初始化
        - 委托
        - 委托属性
5. 属性:
6. 包:关键字package
    1. 导入:import,和java类似;与java不太一样的地方是:Kotlin允许你使用import导入任何类型的声明,类,函数,属性
    2. 文件:kotlin文件,可以把多个类放在同一个文件里,并且可以为文件选择任意的名字
7. 控制结构:
    1. when:类似于java的switch,但是更强大,
        1. 允许任意的对象作为分支条件;
        2. 分支条件可以合并,由“,”分割
        3. 匹配机制:根据参数进行匹配,直到分支条件符合为止,而不是Java中的switch按照顺序匹配所有的分支;
        4. 如果没有匹配成功,则必须要有else分支
        5. 使用不带参数的when:实际上是借助了外部参数变量,然后—列出需要的条件(布尔表达式)
    2. 枚举:enum class Color{ XXX,YYY,….} 注意:如果在枚举类中定义了任何函数方法,则需要用“;”把枚举常量与函数定义分割开来
    3. if1. 表达式,以if分支中最后一句表达式作为返回值
        2. 如果if分支中只有一个表达式,则{ } 可以省略
8. 循环结构:迭代iterator 与java中极其相似
    1. while:同java
    2. do...while:同java
    3. for:与java中不同,kotlin中提供了ranges这样的概念  
        1. 操作符: 
            1.  ..   表示闭区间[ ];不仅仅对数值有效,对字符也是适用的
            2. downTo 降序
            3. step 有步进制
            4. util 表示左闭右开区间[ );
            5. 其实 downTo step util为函数
        2. in:可以用于遍历,也可以用于检查是否属于某个范围;相反的使用:  !in
        3. 范围并没有局限在数值,字符;如果你有任何支持实例比较算法的类(比如通过实现Comparable接口),你就可以创建一个这种类型的范围对象
9. 智能类型转换:在通过is进行类型判断的时候,已经确定变量类型,在使用的时候,可以不用再次显示转换
10. 异常:
    1. 在kotlin中throw 同样是一个表达式
    2. try catch finally
11. 顶层函数 和 属性
    1. 原理:kotlin编译器为顶层函数和属性,生成一个和kotlin文件一样名称的类文件,其中包含了静态函数(即顶层函数)
    2. 改变生成的类名:@file:JvmName(“Hello”),把它放置在kotlin文件的开头,位于包名之前的地方

12. 协程