当前位置: 首页 > 科技观察

研究学习Kotlin的一些方法

时间:2023-03-22 17:34:57 科技观察

Kotlin是一门很舒服的语言。与Java相比,更简洁,省去了繁琐的语法工作,提供类似Lambda和Stringtemplate,NullSafeOperator等特性。方便开发者使用。一般的Java/Android程序员通常只需要很短的时间就可以快速使用Kotlin。结合Kotlin的诸多优点,加上Flipboard美国团队从2015年开始引入Kotlin,Flipboard中国团队也开始将Kotlin作为Android的主要开发语言。虽然Kotlin简单易用,但由于我深入研究的习惯,每次接触到Kotlin的新功能,我都会马不停蹄地研究它的本质。下面介绍一些学习Kotlin的方法,以快速学习和掌握Kotlin。学什么?例如,Kotlin提供了一种叫做Object的类型,利用它我们可以快速实现单例模式的应用。代码很简单objectAppSettings{}那么问题来了,kotlin的对象类型类是怎么实现的,Null安全运算符的实现原理,Lambda表达式是基于内部类还是真正的Lambda,这些问题是我们需要研究的对象。如何学习Kotlin和Java都运行在JVM上,但实际上JVM并不知道Java和Kotlin,因为它只处理字节码(也就是类文件)。因此,通过研究字节码,我们可以深入了解Kotlin的一些原理。由于同样的字节码反编译成java和kotlin文件是等价的,所以将kotlin编译的class文件反编译成Java也具有参考和研究价值。的。有哪些实用方法使用Kotlin插件使用kotlinc、javap等工具实践NullSafeOperator实现原理在Java中,我们经常会遇到空指针的问题。Kotlin故意添加了一个空指针安全运算符?。像这样使用它funtestNullSafeOperator(string:String?){System.out.println(string?.toCharArray()?.getOrNull(10)?.hashCode())}当我们像这样调用testNullSafeOperator(null)testNullSafeOperator("12345678901")testNullSafeOperator("123")得到null49null的输出结果从结果可以看出,并没有像Java那样抛出NullPointerException,而是遇到空指针时不会继续执行。那么Kotlin的空指针安全运算符是如何工作的呢?我们可以使用IntelliJIDE的Kotlin插件来辅助我们的研究。步骤如下:使用IntelliJIDE打开一个要研究的Kotlin文件(确保安装了Kotlin插件)如下点击上面的步骤依次ShowKotlinBytecode,会得到这样的字节码//accessflags0x19publicfinalstatictestNullSafeOperator(Ljava/lang/String;)V@Lorg/jetbrains/annotations/Nullable;()//不可见,参数0L0LINENUMBER11L0GETSTATICjava/lang/System.out:Ljava/io/PrintStream;ALOAD0DUPIFNULLL1//为字符串清空INVOKESTATIKotlin/text/StringsKt.toCharArray(Ljava/lang/String;)[CDUPIFNULLL1//CharArray为空BIPUSH10INVOKESTATICKotlin/collections/ArraysKt.getOrNull([CI)Ljava/lang/Character;DUPIFNULLL1//Char为空INVOKEVIRTUALjava/lang/Object.hashCode()IINVOKESTATICjava/lang/Integer.valueOf(I)Ljava/lang/Integer;GOTOL2L1POPACONST_NULLL2INVOKEVIRTUALjava/io/PrintStream.println(Ljava/lang/Object;)VL3LINENUMBER12L3RETURNL4LOCALVARIABLEstringLjava/lang/String;L0L40MAXSTACK=3MAXLOCALS=1}从字节码分析可以看出that所谓的空指针安全运算符其实就是利用这个内部判断来保证不存在空指针。如果bytecode比较难看懂,那我们就用上面的Decompile函数来转换bytecode进入Java,如图所示,反编译后得到的Java代码为string==null){thrownewTypeCastException("nullcannotbecasttonon-nulltypejava.lang.String");}char[]var4=((String)string).toCharArray();Intrinsics.checkExpressionValueIsNotNull(var4,"(thisasjava.lang.String).toCharArray()");char[]var3=var4;var10000=var2;if(var3!=null){Charactervar10001=ArraysKt.getOrNull(var3,10);if(var10001!=null){var5=Integer.valueOf(var10001.hashCode());breaklabel18;}}}var5=null;}var10000.println(var5);}这样更容易理解吗?对象类型研究这里我们回归到Object类型,或者再举个例子看看如何使用在Kotlin文件中()//在Java文件中调用AppSettings.INSTANCE.updateConfig();我们先看AppSettings的字节码文件//==================AppSettings.class=================//classversion50.0(50)//accessflags0x31publicfinalclassAppSettings{//accessflags0x11publicfinalupdateConfig()VL0LINENUMBER7L0RETURNL1LOCALVARIABLEthisLAppSettings;L0L10MAXSTACK=0MAXLOCALS=1//accessflags0x2private()VL0LINENUMBER4L0ALOAD0INVOKESPECIALjava/lang/Object.()VALOAD0CHECKCASTAppSettingsPUTSTATICAppSettings.INSTANCE:LAppSettings;RETURNL1LOCALVARIABLEthisLAppSettings;L0L10MAXSTACK=1MAXLOCALS=1//accessflags0x19publicFinalStaticLappSettings;instance//accessefflags0x8static()vl0lineNumber4l0//元数据;(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\},"2"应用程序,"()V","updateConfig","","productionsourcesformoduleKotlinObject"})//compiledfrom:AppSettings.kt}可以看出,Kotlin的对象也是Java的单例模式的实现,在静态代码块初始化例子中如果不了解字节码的可以尝试反编译成Java代码详细研究Lambda表达式研究另外,Kotlin也支持Lambda表达式,由于不是所有的JVM版本都支持invokedynamic(Lambda表达式依赖的字节码指令),比如作为Java6的JVM,其中包含了很多Android设备,所以我们怀疑Kotlin可能会像Scala一样将lambda表达式转换为匿名内部类。AsimpleLambdaexpressionexampleclassTest{funtestObservable(){valobservable=Observable()observable.addObserver{o,arg->System.out.println("$o$arg")}}}Weusetheplugintoalsoviewbytecode//=================Test.class==================//classversion50.0(50)//accessflags0x31publicfinalclassTest{//accessflags0x11publicfinaltestObservable()VL0LINENUMBER8L0NEWjava/util/ObservableDUPINVOKESPECIALjava/util/Observable.()VASTORE1L1LINENUMBER9L1ALOAD1GETSTATICTest$testObservable$1.INSTANCE:LTest$testObservable$1;//这里就是使用了匿名内部类(常常包含$字符)CHECKCASTjava/util/ObserverINVOKEVIRTUALjava/util/Observable.addObserver(Ljava/util/Observer;)VL2LINENUMBER12L2RETURNL3LOCALVARIABLEobservableLjava/util/Observable;L1L31LOCALVARIABLEthisLTest;L0L30MAXSTACK=2MAXLOCALS=2//accessflags0x1public()VL0LINENUMBER6L0ALOAD0INVOKESPECIALjava/lang/Object.()VRETURNL1LOCALVARIABLEthisLTest;L0L10MAXSTACK=1MAXLOCALS=1@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\u00022nu0006\u0002\u0010\u0002J\u0006\u0010\u0003\u001a\u00020\u0004\u00a8\u0006\u0005"},d2={"LTest;","","()V","testObservable","","productionsourcesformoduleKotlinObject"})//accessflags0x18finalstaticINNERCLASSTest$testObservable$1nullnull//compiledfrom:Space.kt}//==================Test$testObservable$1.class==================//classversion50.0(50)//accessflags0x30//生成的匿名内部类,rule为当前类名$当前方法名$anonymousinner类序列号finalclassTest$testObservable$1implementsjava/util/Observer{//accessflags0x11publicfinalupdate(Ljava/util/Observable;Ljava/lang/Object;)VL0LINENUMBER10L0GETSTATICjava/lang/System.out:Ljava/io/PrintStream;NEWjava/lang/StringBuilderDUPINVOKESPECIALjava/lang/初始化>()VALOAD1INVOKEVIRTUALjava/lang/StringBuilder.append(Ljava/lang/Object;)Ljava/lang/StringBuilder;LDC""INVOKEVIRTUALjava/lang/StringBuilder.append(Ljava/lang/String;)Ljava/lang/StringBuilder;ALOAD2INVOKEVIRTUALjava/lang/StringBuilder.append(Ljava/lang/Object;)Ljava/lang/StringBuilder;INVOKEVIRTUALjava/lang/StringBuilder.toString()Ljava/lang/String;INVOKEVIRTUALjava/io/PrintStream.println(Ljava/lang/String;)VL1LINENUMBER11L1RETURNL2LOCALVARIABLEthisLTest$testObservable$1;L0L20LOCALVARIABLEoLjava/util/Observable;L0L21LOCALVARIABLEArgMAXLO=STLjava/L32LACK/Ljava/Lang/MAX2Object/accessflags0x0()VALOAD0INVOKESPECIALjava/lang/Object.()VRETURNMAXSTACK=1MAXLOCALS=1//accessflags0x19publicfinalstaticLTest$testObservable$1;INSTANCE//accessflags0x8static()VNEWTest$testObservable$1DUPINVOKESPECIALTest$test()VPUTSTATICTest$testObservable$1.INSTANCE:LTest$testObservable$1;RETURNMAXSTACK=2MAXLOCALS=0@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\u0012u0012u00012\u001a\n\u0004*\u0004\u0018\u00010\u00030\u00032\u000e\u0010\u0005\u001a\n\u0004*\u0004\u0018\u00010\u00060\n2\u0006Hu0007"2\u000}u000d2={"","","o","Ljava/util/Observable;","kotlin.jvm.PlatformType","arg","","更新"})OUTERCLASSTesttestObservable()V//accessflags0x18finalstaticINNERCLASSTest$testObservable$1nullnull//compiledfrom:Space.kt}分析字节码可以看出有两个class文件,所以可以推断Kotlin的Lambda表达式目前是一个内部类的语法糖实现之外,我们也可以使用kotlinc(Kotlin编译器来验证)kotlincTest.kt执行后,查看生成的class文件ls|grep^TestTest$testObservable$1.classTest.classTest.kt当然,我们也可以使用javap实现同样的功能查看字节码,即javap-cclassName,另外,we也可以通过上面的方法来研究Kotlin的以下特点。表达式方法的惰性初始化参考了Kotlin上的研究方法。目前就这些,Kotlin很简单,但是你必须知道为什么,这样你才能游刃有余。希望你们可以尝试Kotlin并玩得开心。

最新推荐
猜你喜欢