美文网首页
Kotlin进阶学习笔记

Kotlin进阶学习笔记

作者: 志威梦 | 来源:发表于2021-01-26 20:24 被阅读0次

    Kotlin进阶学习笔记

    从源码分析学习Kotlin,知其然、知其所以然。

    通用基础语法学习Kotlin官网快速语法学习笔记

    1. Why Kotlin 之官方说辞
    • 简洁Concise:data classlambda快速单例类object

    • 安全Safe:Nullable类型区分,自动推断

      //区分nullable
        var str:String//不同于String?
        str=null//编译则报错,因为String类型非空,任何可空类型需要?符号,比如String?
        //避免NPE
        val name:String?=null//name是可空的
        println(name.length())//编译报错,原因name可能为null,会引起name.length()报空指针异常
        //自动类型推断
        val str:Any//外部参数
        if(str is String){
            //此处在str is String作用域内,就不需要强转类型了
            str.length()
        }
      
    • 互操作性Interoperable

      官宣与Java的现有库可以100%的兼容使用

    2. 源码分析简洁/安全/互通性
    1. 语法简洁

      类型定义,函数声明,POJO创建,labmda的随处可见,快速单例类等都是kotlin语法简洁的有力证明。

      • POJO的对比

        public class Student{
          private String name;
          private int age;
          private String desc;
          
          public void setName(String name){
            this.name = name;
          }
          //省略...
        }
        
        //kotlin中的data class专用于POJO的创建生成,并内部实现了getter/setter,toString,equals,hashCode等必要函数
        data class Student(val name:String,var age:Int,val desc:String)
        

        注意⚠️:可通过AS的Tools-->kotlin-->show kotlin Bytecode在IDE中显示当前kotlin文件对应的字节码,点击Decompile即可生成对应的java文件,从而对比kotlin于java的异同。

      • 单例类的快速创建

        object Tools{
          fun abc(){
            //...
          }
        }
        
        //对应java文件大致如此,之所以有final,因为在kotlin中默认定义的类、函数都是final不可继承的。
        public final class Tools{
          
          @NotNull
          public static final Tools INSTANCE;
          
          public final void abc(){
            //....
          }
          static{
            Tools var0 = new Tools();
            INSTANCE = var0;
          }
        }
        

        通过AS操作对比生成的java文件,我们可以看得出,如上简单的object单例类,对应于java中就是static京台单例类的写法。

    2. 安全性

      • Kotlin中最大的一点在于很好的避免大多数的空指针异常(NPE)的发生,原因在于其从语法层级就区分了 nullablenotnull的类型,如字符串类型String则在kotlin中就表示声明该类型不能赋值null,若要用null的赋值,则必须是String?方可。同理其他类型如是。
      • 在与Java互通时候,就需要Java端的字段与函数标记nullable或notnull注解才能让kotlin正确的接收类型。

    操作示例图

    show kotlin bytecode
    1. 互通

      虽然号称和Java的100%兼容互通,在项目开发中仍然要有一些细节点注意

      • Kotlin调用Java

        1. getter(无参)setter(单个单数)java方法可映射为kotlin中对象属性。Booleangetter理解为isXXX

          public class AA{
              private String name;
              private boolean adult;
              
              public void setName(String name){
                  this.name = name;
              }
              public String getName(){
                  return name;
              }
              public void setAdult(boolean adult){
                  this.adult = adult;
              }
              public boolean isAdult(){
                  return adult;
              }
          }
          
          fun test(){
              val aa = AA()
              //kotlin中调用java,对应getter和setter可映射为kotlin中的属性
              aa.name = "Julia"
              aa.isAdult = true
          }
          
        2. 特殊字符,如isobjectis等在java中无所谓,但是在kotlin中是关键字,调用时候需要转义

          //如java中定义一个函数名public void is(String name){}
          //kotlin调用的时候,is要转义
          aa.`is`("name")
          
        3. 空安全,java中任何引用都可能null,所以kotlin调用java就比较尴尬。

          1. 编译器可能无法判断类型可空与否。在接收java类型时,可根据情况最好注解为可null

          2. 参照java中注解@Nullable@NotNull,则在kotlin中调用可明确类型空否。

            public void setName(@NotNull String name){
                //....
            }
            @Nullable
            public String getName(){
                return name;
            }
            //在kotlin调用如上函数,就比较明晰,如果没有注解的话,就要灵活判断
            
        4. Java中泛型在Kotlin中的使用,转换

          1. Foo<? extends Bar>——>Foo<out Bar!>!
          2. Foo<? super Bar> ——> Foo<in Bar!>!
          3. Java中原始类型如List转化为List<*>!,即List<out Any?>!
          4. 同样Kotlin的泛型也存在类型擦除问题。泛型详细讲解,之后单独文章分析
        5. Java中数组是可型变的,Kotlin中数组不可型变。Kotlin中基础类型隐藏了拆装箱的细节,对应于原生java的数组提供了特殊类来映射。IntArrayDoubleArray等,但这些都不是数组,对应会编译为Java的原生数组。

          //Java数组方法
          public class JavaArrayTest{
              public void removeIndices(int[] indices){
                  //...
              }
          }
          //kotlin中调用java的数组参数的方法
          fun testJavaArray(){
              val javaObj = JavaArrayTest()
              val arr = intArrayOf(1,2,2,3)//生成kotlin语法的数组
              javaObj.removeIndices(arr)//调用java的方法,传入数组参数
              
              //更新赋值,遍历,均不会引入额外开销
              arr[2] = arr[1]*3//将2号位的值更新为1号值得3倍,这里也不会有get/set的调用
             for(x in arr){//并不会创建迭代器
                 print(x)
             }
             for(i in arr.indices){//也不会有迭代器创建,不会有额外开销,in的校验也不会有额外开销。
                 arr[i]+=3
             }
          }
          

          因为kolint的语法特定类,在编译为JVM的字节码时候,会化为原生的形式。

          Java的数组可型变与Kotlin的数组不可型变,对比图

    arr
    java arr
    1. Java可变参数,Kotlin中使用*符号展开对应数组,不可传null
            //Java中可变参数使用...表示
            public void removeIndices(int... indices){
                //...
            }
            //kotlin中调用就需要是对应可展开的数组类型
            val aar = intArrayOf(1,2,2,3)
            removeIndices(*arr)
    
    1. Kotlin中无法使用中缀语法调用Java方法

    2. Kotlin中可能异常的函数,并不会要求你一定try...catch,这就有点尴尬,你嗲用Java或者kotlin的其他可能异常的函数时候,就要自己注意catch

    3. Java类的静态成员会在Kotlin中表示为伴生对象companion object

    4. Java反射与Kotlin反射类似,后续文章单独分析

    5. SAM(simple abstract method conversions)转换。

    6. Kotlin调用JNI,类似于Java,需要在Kotlin中声明一个函数,在C/C++中有实现的,并使用external修饰

            external fun aaa(a:Int):Double//这个函数,在Kotlin中声明,需要在C/C++中实现。
            //也可以是属性的形式,这样就是生成两个external的函数,setter/getter
            var mName:String
               external get
               external set
    
    • Java中调用Kotlin

      1. Kotlin中顶级函数和字段,在Java中调用

        //文件名叫做 test.kt
        package org.zhiwei
        
        class ABC(){}
        //top level 的函数和字段
        fun getName(){
            //...
        }
        const val bbbb = "bbbbb"//静态常量
        var cccccc= ""
        val dddddd = ""
        

        通过编译,会生成org.zhiwei.testKt类,成为内部方法和属性。

        //java中调用则
        org.zhiwei.testKt.getName();//函数
        org.zhiwei.testKt.bbbb;//静态常量  
        org.zhiwei.testKt.setCccccc("");//可变量
        

        默认生成fileNameKt,可通过@JvmName指定生成的类名/函数名

        @file:JvmName("TestTools")//则在java中调用就用TestTools.来调用即可。
        

        注:不同的kt文件中toplevel可以指定生成相同的Java类,需要添加@file:JvmMultifileClass注解

        //oldUtils.kt
        @file:JvmName("Tools")
        @file:JvmMultifileClass
        
        package org.zhiwei
        
        fun getTime(){
            //...
        }
        //newUtils.kt
        @file:JvmName("Tools")
        @file:JvmMultifileClass
        
        package org.zhiwei
        
        fun getDate(){
            //...
        }
        //如上两个kt文件中不同的top level的函数定义,都指定生成java文件Tools类,
        
        //java中调用,就是如下
        Tools.getTime();
        Tools.getDate();
        
      2. Kotlin中非私有/非open/override/非const/无默认值/非被委托的属性,通过@JvmField修饰,可在Java中调用。

        //kotlin
        class User(id:String){
            @JvmField val ID = id
        }
        //java中 
        public String getId(User user){
            return user.ID;
        }
        
      3. Kotlin中静态字段通常会是私有的。可通过@JvmField/lateinit/const来修饰,使得Java中公开调用到。

        //kotlin
        class User(){
            companion object{
                const val SEX = '男'
                @JvmField
                val name = "张三"
                lateinit var mobile:String
            }
        }
        //java中,注意,不需要UserKt.的形式
        User.SEX;
        User.name;
        User.mobile;
        
      4. kotlin中静态方法对应于Java中,@JvmStatic注解适用于字段和函数(jdk1.8/kotlin 1.3)

        //top level中的静态函数都是java对应的。而在具体的类中,可以写在object或者伴生对象中
        class CCC{
            companion object{
                //在kotlin中属于静态函数的调用方式,但是java调用就需要伴生对象形式
                fun callInKotlinStatic(){}
                //支持如上方式,但同时在java中也是静态调用方式
                @JvmStatic fun callInJavaStatic(){}
            }
        }
        //java中
        public void test(){
            CCC.callInJavaStatic();
            CCC.Companion.callInJavaStatic();
            CCC.Companion.callInKotlinStatic();//只有这一个,没有直接CCC.callInKotlinStatic()
            
        }
        
      5. Kotlin中接口的函数定义,可以有默认实现(jdk1.8+)

        interface IUser{
            fun run(){
                //...默认实现
            }
        }
        //java实现接口的时候,可以不复写run函数
        
      6. 权限符号publicprivateinternalprotectedJava中异同

        1. private、public权限一致;private在kt文件级别,则等同于包内可见。
        2. Java中可以访问包内的其他protected的类,kotlin不行
        3. internal的属性字段,对应java的public,而internal 的类的成员,与java中作用关系无对应。
      7. KClass调用可用getKotlinClass(XXX.class)

      8. Jvm的签名冲突处理,@JvmName处理

        //kotlin中定义两个同名函数
        fun List<String>.filterN():List<String>
        fun List<Int>.filterN():List<Int>
        //属性的setter和getter也可以单独注解
        val x:Int
        @JvmName("getX_prop")
        get() = 888
        //或者
        @get:JvmName("x")
        @set:JvmName("changeX")
        var x:Int = 33
        
      9. @JvmOverloads函数重载,用于将kotlin中存在默认参数的函数,提供出多种重载函数给Java调用。可用于构造函数和静态函数,不能修饰抽象函数和接口的方法。

        //这个函数存在java中就会有依次的集中重载组合函数
        @JvmOverloads
        fun haha(name:String,age:Int=18,sex:Int=1,address:String="beijing")
        
      10. 异常,kotiln不检查异常,所以想要让调用方知道会异常就添加@Throws

        @Throws(IOException::class)
        fun write2File(){
            //...
            throw IOException()
        }
        
      11. Java中的类型参数可能接收null,并且kotlin不知道,比较尴尬。

      12. Nothing类型,在Java中没有对应。Nothing不能为null

    以上为Kotlin进阶学习中着意知识点;接下来会分析Kotlin的类型推断原理、泛型、反射以及其他高阶特性。

    相关文章

      网友评论

          本文标题:Kotlin进阶学习笔记

          本文链接:https://www.haomeiwen.com/subject/irjnzktx.html