研究学习Kotlin的一些方法

Kotlin是一门让人感到很舒服的语言,相比Java来说,它更加简洁,省去了琐琐碎碎的语法工作,同时了提供了类似Lambda,String template,Null Safe Operator等特性。让开发者用起来得心应手。

成都创新互联服务项目包括富顺网站建设、富顺网站制作、富顺网页制作以及富顺网络营销策划等。多年来,我们专注于互联网行业,利用自身积累的技术优势、行业经验、深度合作伙伴关系等,向广大中小型企业、政府机构等提供互联网行业的解决方案,富顺网站推广取得了明显的社会效益与经济效益。目前,我们服务的客户以成都为中心已经辐射到富顺省份的部分城市,未来相信会继续扩大服务区域并继续获得客户的支持与信任!

普通的Java/Android程序员通常只需要很短的时间就能快速使用Kotlin。综合Kotlin的诸多优点,加上Flipboard美国团队自2015年已引入Kotlin,Flipboard中国团队也已经开始采用Kotlin来作为Android主要开发语言。

虽然Kotlin使用简单快捷,然而由于自己的深入研究的习惯导致每接触到Kotlin的新功能,就马不停蹄的研究它的本质,这里总结一下关于如何研究Kotlin的一些方法来快速研究掌握Kotlin。

到底研究什么

比如Kotlin中提供了一种类型叫做Object,使用它我们可以快速实现单例模式的应用。代码特别的简单

 
 
 
 
  1. object AppSettings { 
  2. }

那么问题来了,kotlin这个object类型的类是如何实现的呢,Null安全操作符的实现原理,Lambda表达式是基于内部类还是真正的Lambda,这些问题就是我们要研究的对象。

怎么研究

  • Kotlin和Java都是运行在JVM上,但是实际上JVM并不认识Java和Kotlin,因为它只和bytecode(即class文件)打交道。
  • 因而通过研究bytecode,我们是可以了解Kotlin的一些深入原理的
  • 由于同一份bytecode反编译成java和kotlin文件是等价的,所以将kotlin编译后的class文件反编译成Java,也是具有参考和研究价值的。

实践方法有哪些

  • 利用Kotlin插件
  • 利用kotlinc,javap等工具

一些实践

Null Safe Operator实现原理

在Java中,我们经常会遇到空指针的问题,Kotlin特意增加了一个空指针安全操作符?。使用起来如下

 
 
 
 
  1. fun testNullSafeOperator(string: String?) {
  2.     System.out.println(string?.toCharArray()?.getOrNull(10)?.hashCode())
  3. }

当我们进行这样的调用时

 
 
 
 
  1. testNullSafeOperator(null)
  2. testNullSafeOperator("12345678901")
  3. testNullSafeOperator("123")

得到的输出结果为

 
 
 
 
  1. null
  2. 49
  3. null

从结果可见,并没有像Java那样抛出NullPointerException,而是遇到空指针则不继续执行了。

那么Kotlin的这个空指针安全操作符是如何工作的呢,我们可以借助IntelliJ IDE的Kotlin插件来辅助我们研究,步骤如下

  • 使用IntelliJ IDE打开一个待研究的Kotlin文件(需确保Kotlin插件已安装)
  • 按照下图依次点击至Show Kotlin Bytecode

  • 上面的步骤操作后,会得到这样的bytecode
 
 
 
 
  1. // access flags 0x19
  2.   public final static testNullSafeOperator(Ljava/lang/String;)V
  3.     @Lorg/jetbrains/annotations/Nullable;() // invisible, parameter 0
  4.    L0
  5.     LINENUMBER 11 L0
  6.     GETSTATIC java/lang/System.out : Ljava/io/PrintStream;
  7.     ALOAD 0
  8.     DUP
  9.     IFNULL L1   //对string字符串判空
  10.     INVOKESTATIC kotlin/text/StringsKt.toCharArray (Ljava/lang/String;)[C
  11.     DUP
  12.     IFNULL L1  //对CharArray判空
  13.     BIPUSH 10
  14.     INVOKESTATIC kotlin/collections/ArraysKt.getOrNull ([CI)Ljava/lang/Character;
  15.     DUP
  16.     IFNULL L1  //对Char判空
  17.     INVOKEVIRTUAL java/lang/Object.hashCode ()I
  18.     INVOKESTATIC java/lang/Integer.valueOf (I)Ljava/lang/Integer;
  19.     GOTO L2
  20.    L1
  21.     POP
  22.     ACONST_NULL
  23.    L2
  24.     INVOKEVIRTUAL java/io/PrintStream.println (Ljava/lang/Object;)V
  25.    L3
  26.     LINENUMBER 12 L3
  27.     RETURN
  28.    L4
  29.     LOCALVARIABLE string Ljava/lang/String; L0 L4 0
  30.     MAXSTACK = 3
  31.     MAXLOCALS = 1
  32. }

由字节码分析可见,其实所谓的 空指针安全操作符其实内部就是以此判空来确保不出现空指针 ,如果字节码不好理解,那我们使用上面的Decompile功能,将bytecode转成Java,如图操作

反编译后得到的Java代码为

 
 
 
 
  1. public static final void testNullSafeOperator(@Nullable String string) {
  2.       PrintStream var10000;
  3.       Integer var5;
  4.       label18: {
  5.          var10000 = System.out;
  6.          if(string != null) {
  7.             PrintStream var2 = var10000;
  8.             if(string == null) {
  9.                throw new TypeCastException("null cannot be cast to non-null type java.lang.String");
  10.             }
  11.             char[] var4 = ((String)string).toCharArray();
  12.             Intrinsics.checkExpressionValueIsNotNull(var4, "(this as java.lang.String).toCharArray()");
  13.             char[] var3 = var4;
  14.             var10000 = var2;
  15.             if(var3 != null) {
  16.                Character var10001 = ArraysKt.getOrNull(var3, 10);
  17.                if(var10001 != null) {
  18.                   var5 = Integer.valueOf(var10001.hashCode());
  19.                   break label18;
  20.                }
  21.             }
  22.          }
  23.          var5 = null;
  24.       }
  25.       var10000.println(var5);
  26.    }

这样读起来是不是更加容易理解呢。

Object类型研究

这里我们回到Object类型,还是再举个例子看看如何使用

 
 
 
 
  1. //这是定义
  2. object AppSettings {
  3.     fun updateConfig() {
  4.         //do some updating work
  5.     }
  6. }

关于应用也很简单

 
 
 
 
  1. //在Kotlin文件中调用
  2. AppSettings.updateConfig()
  3. //在Java文件中调用
  4. AppSettings.INSTANCE.updateConfig();

我们先看一下AppSettings的字节码文件

 
 
 
 
  1. // ================AppSettings.class =================
  2. // class version 50.0 (50)
  3. // access flags 0x31
  4. public final class AppSettings {
  5.   // access flags 0x11
  6.   public final updateConfig()V
  7.    L0
  8.     LINENUMBER 7 L0
  9.     RETURN
  10.    L1
  11.     LOCALVARIABLE this LAppSettings; L0 L1 0
  12.     MAXSTACK = 0
  13.     MAXLOCALS = 1
  14.   // access flags 0x2
  15.   private ()V
  16.    L0
  17.     LINENUMBER 4 L0
  18.     ALOAD 0
  19.     INVOKESPECIAL java/lang/Object. ()V
  20.     ALOAD 0
  21.     CHECKCAST AppSettings
  22.     PUTSTATIC AppSettings.INSTANCE : LAppSettings;
  23.     RETURN
  24.    L1
  25.     LOCALVARIABLE this LAppSettings; L0 L1 0
  26.     MAXSTACK = 1
  27.     MAXLOCALS = 1
  28.   // access flags 0x19
  29.   public final static LAppSettings; INSTANCE
  30.   // access flags 0x8
  31.   static ()V
  32.    L0
  33.     LINENUMBER 4 L0
  34.     //静态代码块中实例化,即类加载时便开始实例化
  35.     NEW AppSettings
  36.     INVOKESPECIAL AppSettings. ()V
  37.     RETURN
  38.     MAXSTACK = 1
  39.     MAXLOCALS = 0
  40.   @Lkotlin/Metadata;(mv={1, 1, 5}, bv={1, 0, 1}, k=1, d1={"\u0000\u0012\n\u0002\u0018\u0002\n\u0002\u0010\u0000\n\u0002\u0008\u0002\n\u0002\u0010\u0002\n\u0000\u0008\u00c6\u0002\u0018\u00002\u00020\u0001B\u0007\u0008\u0002\u00a2\u0006\u0002\u0010\u0002J\u0006\u0010\u0003\u001a\u00020\u0004\u00a8\u0006\u0005"}, d2={"LAppSettings;", "", "()V", "updateConfig", "", "production sources for module KotlinObject"})
  41.   // compiled from: AppSettings.kt
  42. }

由此可见,Kotlin的object也就是Java的单例模式的实现,在静态代码块初始化实例。如果字节码没有看懂的话,可以尝试反编译成Java代码来详细研究。

Lambda表达式研究

除此之外,Kotlin也是支持了Lambda表达式的。由于并非所有的JVM版本都支持invokedynamic(Lambda表达式依赖的字节码指令),比如Java 6的JVM,这其中就包含了许多安卓设备。所以我们怀疑Kotlin可能是像Scala那样将lambda表达式转换成了匿名内部类。

一个简单的Lambda表达式例子

 
 
 
 
  1. class Test {
  2.     fun testObservable() {
  3.         val observable = Observable()
  4.         observable.addObserver { o, arg ->
  5.             System.out.println("$o $arg")
  6.         }
  7.     }
  8. }

我们使用插件同样查看bytecode

 
 
 
 
  1. // ================Test.class =================
  2. // class version 50.0 (50)
  3. // access flags 0x31
  4. public final class Test {
  5.   // access flags 0x11
  6.   public final testObservable()V
  7.    L0
  8.     LINENUMBER 8 L0
  9.     NEW java/util/Observable
  10.     DUP
  11.     INVOKESPECIAL java/util/Observable. ()V
  12.     ASTORE 1
  13.    L1
  14.     LINENUMBER 9 L1
  15.     ALOAD 1
  16.     GETSTATIC Test$testObservable$1.INSTANCE : LTest$testObservable$1;  //这里就是使用了匿名内部类(常常包含$字符)
  17.     CHECKCAST java/util/Observer
  18.     INVOKEVIRTUAL java/util/Observable.addObserver (Ljava/util/Observer;)V
  19.    L2
  20.     LINENUMBER 12 L2
  21.     RETURN
  22.    L3
  23.     LOCALVARIABLE observable Ljava/util/Observable; L1 L3 1
  24.     LOCALVARIABLE this LTest; L0 L3 0
  25.     MAXSTACK = 2
  26.     MAXLOCALS = 2
  27.   // access flags 0x1
  28.   public ()V
  29.    L0
  30.     LINENUMBER 6 L0
  31.     ALOAD 0
  32.     INVOKESPECIAL java/lang/Object. ()V
  33.     RETURN
  34.    L1
  35.     LOCALVARIABLE this LTest; L0 L1 0
  36.     MAXSTACK = 1
  37.     MAXLOCALS = 1
  38.   @Lkotlin/Metadata;(mv={1, 1, 5}, bv={1, 0, 1}, k=1, d1={"\u0000\u0012\n\u0002\u0018\u0002\n\u0002\u0010\u0000\n\u0002\u0008\u0002\n\u0002\u0010\u0002\n\u0000\u0018\u00002\u00020\u0001B\u0005\u00a2\u0006\u0002\u0010\u0002J\u0006\u0010\u0003\u001a\u00020\u0004\u00a8\u0006\u0005"}, d2={"LTest;", "", "()V", "testObservable", "", "production sources for module KotlinObject"})
  39.   // access flags 0x18
  40.   final static INNERCLASS Test$testObservable$1 null null
  41.   // compiled from: Space.kt
  42. }
  43. // ================Test$testObservable$1.class =================
  44. // class version 50.0 (50)
  45. // access flags 0x30
  46. //生成的匿名内部类,规则为  当前的类名$当前的方法名$匿名内部类序号
  47. final class Test$testObservable$1 implements java/util/Observer  {
  48.   // access flags 0x11
  49.   public final update(Ljava/util/Observable;Ljava/lang/Object;)V
  50.    L0
  51.     LINENUMBER 10 L0
  52.     GETSTATIC java/lang/System.out : Ljava/io/PrintStream;
  53.     NEW java/lang/StringBuilder
  54.     DUP
  55.     INVOKESPECIAL java/lang/StringBuilder. ()V
  56.     ALOAD 1
  57.     INVOKEVIRTUAL java/lang/StringBuilder.append (Ljava/lang/Object;)Ljava/lang/StringBuilder;
  58.     LDC " "
  59.     INVOKEVIRTUAL java/lang/StringBuilder.append (Ljava/lang/String;)Ljava/lang/StringBuilder;
  60.     ALOAD 2
  61.     INVOKEVIRTUAL java/lang/StringBuilder.append (Ljava/lang/Object;)Ljava/lang/StringBuilder;
  62.     INVOKEVIRTUAL java/lang/StringBuilder.toString ()Ljava/lang/String;
  63.     INVOKEVIRTUAL java/io/PrintStream.println (Ljava/lang/String;)V
  64.    L1
  65.     LINENUMBER 11 L1
  66.     RETURN
  67.    L2
  68.     LOCALVARIABLE this LTest$testObservable$1; L0 L2 0
  69.     LOCALVARIABLE o Ljava/util/Observable; L0 L2 1
  70.     LOCALVARIABLE arg Ljava/lang/Object; L0 L2 2
  71.     MAXSTACK = 3
  72.     MAXLOCALS = 3 
  73.   // access flags 0x0
  74.   ()V
  75.     ALOAD 0
  76.     INVOKESPECIAL java/lang/Object. ()V
  77.     RETURN
  78.     MAXSTACK = 1
  79.     MAXLOCALS = 1 
  80.   // access flags 0x19
  81.   public final static LTest$testObservable$1; INSTANCE
  82.   // access flags 0x8
  83.   static ()V
  84.     NEW Test$testObservable$1
  85.     DUP
  86.     INVOKESPECIAL Test$testObservable$1. ()V
  87.     PUTSTATIC Test$testObservable$1.INSTANCE : LTest$testObservable$1;
  88.     RETURN
  89.     MAXSTACK = 2
  90.     MAXLOCALS = 0 
  91.   @Lkotlin/Metadata;(mv={1, 1, 5}, bv={1, 0, 1}, k=3, d1={"\u0000\u0016\n\u0000\n\u0002\u0010\u0002\n\u0000\n\u0002\u0018\u0002\n\u0002\u0008\u0002\n\u0002\u0010\u0000\n\u0000\u0010\u0000\u001a\u00020\u00012\u000e\u0010\u0002\u001a\n \u0004*\u0004\u0018\u00010\u00030\u00032\u000e\u0010\u0005\u001a\n \u0004*\u0004\u0018\u00010\u00060\u0006H\n\u00a2\u0006\u0002\u0008\u0007"}, d2={"", "", "o", "Ljava/util/Observable;", "kotlin.jvm.PlatformType", "arg", "", "update"})
  92.   OUTERCLASS Test testObservable ()V
  93.   // access flags 0x18
  94.   final static INNERCLASS Test$testObservable$1 null null
  95.   // compiled from: Space.kt
  96. }

分析字节码可以看到有两个class文件,因此可以推断出Kotlin的Lambda表达式目前是一种基于内部类的语法糖实现。

除此之外,我们还可以使用kotlinc(Kotlin编译器来验证)

 
 
 
 
  1. kotlinc Test.kt

执行完成后,查看生成的class文件

 
 
 
 
  1. ls | grep ^Test
  2. Test$testObservable$1.class
  3. Test.class
  4. Test.kt

当然,我们还可以使用javap同样实现查看bytecode的功能,即 javap -c className 。

除此之外,我们还可以利用上面的方法研究如下Kotlin的特性

  • lazy初始化
  • when表达式
  • 方法引用

关于Kotlin的研究方法目前就是这些,Kotlin很简单,但也要知其所以然,方能游刃有余编码。希望大家可以尝试Kotlin,并玩的开心。

文章题目:研究学习Kotlin的一些方法
URL链接:http://www.stwzsj.com/qtweb/news20/4570.html

网站建设、网络推广公司-创新互联,是专注品牌与效果的网站制作,网络营销seo公司;服务项目有等

广告

声明:本网站发布的内容(图片、视频和文字)以用户投稿、用户转载内容为主,如果涉及侵权请尽快告知,我们将会在第一时间删除。文章观点不代表本网站立场,如需处理请联系客服。电话:028-86922220;邮箱:631063699@qq.com。内容未经允许不得转载,或转载时需注明来源: 创新互联