溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務(wù)條款》

研究學(xué)習(xí)Kotlin的方法有哪些

發(fā)布時間:2022-01-11 11:24:00 來源:億速云 閱讀:139 作者:柒染 欄目:編程語言

這篇文章將為大家詳細(xì)講解有關(guān)研究學(xué)習(xí)Kotlin的方法有哪些,文章內(nèi)容質(zhì)量較高,因此小編分享給大家做個參考,希望大家閱讀完這篇文章后對相關(guān)知識有一定的了解。

Kotlin是一門讓人感到很舒服的語言,相比Java來說,它更加簡潔,省去了瑣瑣碎碎的語法工作,同時了提供了類似Lambda,String  template,Null Safe Operator等特性。讓開發(fā)者用起來得心應(yīng)手。

普通的Java/Android程序員通常只需要很短的時間就能快速使用Kotlin。綜合Kotlin的諸多優(yōu)點,加上Flipboard美國團(tuán)隊自2015年已引入Kotlin,F(xiàn)lipboard中國團(tuán)隊也已經(jīng)開始采用Kotlin來作為Android主要開發(fā)語言。

雖然Kotlin使用簡單快捷,然而由于自己的深入研究的習(xí)慣導(dǎo)致每接觸到Kotlin的新功能,就馬不停蹄的研究它的本質(zhì),這里總結(jié)一下關(guān)于如何研究Kotlin的一些方法來快速研究掌握Kotlin。

到底研究什么

比如Kotlin中提供了一種類型叫做Object,使用它我們可以快速實現(xiàn)單例模式的應(yīng)用。代碼特別的簡單

object AppSettings {  }

那么問題來了,kotlin這個object類型的類是如何實現(xiàn)的呢,Null安全操作符的實現(xiàn)原理,Lambda表達(dá)式是基于內(nèi)部類還是真正的Lambda,這些問題就是我們要研究的對象。

怎么研究

  • Kotlin和Java都是運(yùn)行在JVM上,但是實際上JVM并不認(rèn)識Java和Kotlin,因為它只和bytecode(即class文件)打交道。

  • 因而通過研究bytecode,我們是可以了解Kotlin的一些深入原理的

  • 由于同一份bytecode反編譯成java和kotlin文件是等價的,所以將kotlin編譯后的class文件反編譯成Java,也是具有參考和研究價值的。

實踐方法有哪些

  • 利用Kotlin插件

  • 利用kotlinc,javap等工具

一些實踐

Null Safe Operator實現(xiàn)原理

在Java中,我們經(jīng)常會遇到空指針的問題,Kotlin特意增加了一個空指針安全操作符?。使用起來如下

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

當(dāng)我們進(jìn)行這樣的調(diào)用時

testNullSafeOperator(null) testNullSafeOperator("12345678901") testNullSafeOperator("123")

得到的輸出結(jié)果為

null 49 null

從結(jié)果可見,并沒有像Java那樣拋出NullPointerException,而是遇到空指針則不繼續(xù)執(zhí)行了。

那么Kotlin的這個空指針安全操作符是如何工作的呢,我們可以借助IntelliJ IDE的Kotlin插件來輔助我們研究,步驟如下

  • 使用IntelliJ IDE打開一個待研究的Kotlin文件(需確保Kotlin插件已安裝)

  • 按照下圖依次點擊至Show Kotlin Bytecode

研究學(xué)習(xí)Kotlin的方法有哪些

  • 上面的步驟操作后,會得到這樣的bytecode

// access flags 0x19   public final static testNullSafeOperator(Ljava/lang/String;)V     @Lorg/jetbrains/annotations/Nullable;() // invisible, parameter 0    L0     LINENUMBER 11 L0     GETSTATIC java/lang/System.out : Ljava/io/PrintStream;     ALOAD 0     DUP     IFNULL L1   //對string字符串判空     INVOKESTATIC kotlin/text/StringsKt.toCharArray (Ljava/lang/String;)[C     DUP     IFNULL L1  //對CharArray判空     BIPUSH 10     INVOKESTATIC kotlin/collections/ArraysKt.getOrNull ([CI)Ljava/lang/Character;     DUP     IFNULL L1  //對Char判空     INVOKEVIRTUAL java/lang/Object.hashCode ()I     INVOKESTATIC java/lang/Integer.valueOf (I)Ljava/lang/Integer;     GOTO L2    L1     POP     ACONST_NULL    L2     INVOKEVIRTUAL java/io/PrintStream.println (Ljava/lang/Object;)V    L3     LINENUMBER 12 L3     RETURN    L4     LOCALVARIABLE string Ljava/lang/String; L0 L4 0     MAXSTACK = 3     MAXLOCALS = 1 }

由字節(jié)碼分析可見,其實所謂的 空指針安全操作符其實內(nèi)部就是以此判空來確保不出現(xiàn)空指針  ,如果字節(jié)碼不好理解,那我們使用上面的Decompile功能,將bytecode轉(zhuǎn)成Java,如圖操作

研究學(xué)習(xí)Kotlin的方法有哪些

反編譯后得到的Java代碼為

public static final void testNullSafeOperator(@Nullable String string) {       PrintStream var10000;       Integer var5;       label18: {          var10000 = System.out;          if(string != null) {             PrintStream var2 = var10000;             if(string == null) {                throw new TypeCastException("null cannot be cast to non-null type java.lang.String");             }              char[] var4 = ((String)string).toCharArray();             Intrinsics.checkExpressionValueIsNotNull(var4, "(this as java.lang.String).toCharArray()");             char[] var3 = var4;             var10000 = var2;             if(var3 != null) {                Character var10001 = ArraysKt.getOrNull(var3, 10);                if(var10001 != null) {                   var5 = Integer.valueOf(var10001.hashCode());                   break label18;                }             }          }           var5 = null;       }        var10000.println(var5);    }

這樣讀起來是不是更加容易理解呢。

Object類型研究

這里我們回到Object類型,還是再舉個例子看看如何使用

//這是定義 object AppSettings {     fun updateConfig() {         //do some updating work     } }

關(guān)于應(yīng)用也很簡單

//在Kotlin文件中調(diào)用 AppSettings.updateConfig()  //在Java文件中調(diào)用 AppSettings.INSTANCE.updateConfig();

我們先看一下AppSettings的字節(jié)碼文件

// ================AppSettings.class ================= // class version 50.0 (50) // access flags 0x31 public final class AppSettings {   // access flags 0x11   public final updateConfig()V    L0     LINENUMBER 7 L0     RETURN    L1     LOCALVARIABLE this LAppSettings; L0 L1 0     MAXSTACK = 0     MAXLOCALS = 1    // access flags 0x2   private <init>()V    L0     LINENUMBER 4 L0     ALOAD 0     INVOKESPECIAL java/lang/Object.<init> ()V     ALOAD 0     CHECKCAST AppSettings     PUTSTATIC AppSettings.INSTANCE : LAppSettings;     RETURN    L1     LOCALVARIABLE this LAppSettings; L0 L1 0     MAXSTACK = 1     MAXLOCALS = 1    // access flags 0x19   public final static LAppSettings; INSTANCE    // access flags 0x8   static <clinit>()V    L0     LINENUMBER 4 L0     //靜態(tài)代碼塊中實例化,即類加載時便開始實例化     NEW AppSettings     INVOKESPECIAL AppSettings.<init> ()V     RETURN     MAXSTACK = 1     MAXLOCALS = 0    @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"})   // compiled from: AppSettings.kt }

由此可見,Kotlin的object也就是Java的單例模式的實現(xiàn),在靜態(tài)代碼塊初始化實例。如果字節(jié)碼沒有看懂的話,可以嘗試反編譯成Java代碼來詳細(xì)研究。

Lambda表達(dá)式研究

除此之外,Kotlin也是支持了Lambda表達(dá)式的。由于并非所有的JVM版本都支持invokedynamic(Lambda表達(dá)式依賴的字節(jié)碼指令),比如Java  6的JVM,這其中就包含了許多安卓設(shè)備。所以我們懷疑Kotlin可能是像Scala那樣將lambda表達(dá)式轉(zhuǎn)換成了匿名內(nèi)部類。

一個簡單的Lambda表達(dá)式例子

class Test {     fun testObservable() {         val observable = Observable()         observable.addObserver { o, arg ->             System.out.println("$o $arg")         }     } }

我們使用插件同樣查看bytecode

// ================Test.class ================= // class version 50.0 (50) // access flags 0x31 public final class Test {    // access flags 0x11   public final testObservable()V    L0     LINENUMBER 8 L0     NEW java/util/Observable     DUP     INVOKESPECIAL java/util/Observable.<init> ()V     ASTORE 1    L1     LINENUMBER 9 L1     ALOAD 1     GETSTATIC Test$testObservable$1.INSTANCE : LTest$testObservable$1;  //這里就是使用了匿名內(nèi)部類(常常包含$字符)     CHECKCAST java/util/Observer     INVOKEVIRTUAL java/util/Observable.addObserver (Ljava/util/Observer;)V    L2     LINENUMBER 12 L2     RETURN    L3     LOCALVARIABLE observable Ljava/util/Observable; L1 L3 1     LOCALVARIABLE this LTest; L0 L3 0     MAXSTACK = 2     MAXLOCALS = 2    // access flags 0x1   public <init>()V    L0     LINENUMBER 6 L0     ALOAD 0     INVOKESPECIAL java/lang/Object.<init> ()V     RETURN    L1     LOCALVARIABLE this LTest; L0 L1 0     MAXSTACK = 1     MAXLOCALS = 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\u0005\u00a2\u0006\u0002\u0010\u0002J\u0006\u0010\u0003\u001a\u00020\u0004\u00a8\u0006\u0005"}, d2={"LTest;", "", "()V", "testObservable", "", "production sources for module KotlinObject"})   // access flags 0x18   final static INNERCLASS Test$testObservable$1 null null   // compiled from: Space.kt }   // ================Test$testObservable$1.class ================= // class version 50.0 (50) // access flags 0x30 //生成的匿名內(nèi)部類,規(guī)則為  當(dāng)前的類名$當(dāng)前的方法名$匿名內(nèi)部類序號 final class Test$testObservable$1 implements java/util/Observer  {    // access flags 0x11   public final update(Ljava/util/Observable;Ljava/lang/Object;)V    L0     LINENUMBER 10 L0     GETSTATIC java/lang/System.out : Ljava/io/PrintStream;     NEW java/lang/StringBuilder     DUP     INVOKESPECIAL java/lang/StringBuilder.<init> ()V     ALOAD 1     INVOKEVIRTUAL java/lang/StringBuilder.append (Ljava/lang/Object;)Ljava/lang/StringBuilder;     LDC " "     INVOKEVIRTUAL java/lang/StringBuilder.append (Ljava/lang/String;)Ljava/lang/StringBuilder;     ALOAD 2     INVOKEVIRTUAL java/lang/StringBuilder.append (Ljava/lang/Object;)Ljava/lang/StringBuilder;     INVOKEVIRTUAL java/lang/StringBuilder.toString ()Ljava/lang/String;     INVOKEVIRTUAL java/io/PrintStream.println (Ljava/lang/String;)V    L1     LINENUMBER 11 L1     RETURN    L2     LOCALVARIABLE this LTest$testObservable$1; L0 L2 0     LOCALVARIABLE o Ljava/util/Observable; L0 L2 1     LOCALVARIABLE arg Ljava/lang/Object; L0 L2 2     MAXSTACK = 3     MAXLOCALS = 3    // access flags 0x0   <init>()V     ALOAD 0     INVOKESPECIAL java/lang/Object.<init> ()V     RETURN     MAXSTACK = 1     MAXLOCALS = 1    // access flags 0x19   public final static LTest$testObservable$1; INSTANCE    // access flags 0x8   static <clinit>()V     NEW Test$testObservable$1     DUP     INVOKESPECIAL Test$testObservable$1.<init> ()V     PUTSTATIC Test$testObservable$1.INSTANCE : LTest$testObservable$1;     RETURN     MAXSTACK = 2     MAXLOCALS = 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\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={"<anonymous>", "", "o", "Ljava/util/Observable;", "kotlin.jvm.PlatformType", "arg", "", "update"})   OUTERCLASS Test testObservable ()V   // access flags 0x18   final static INNERCLASS Test$testObservable$1 null null   // compiled from: Space.kt }

分析字節(jié)碼可以看到有兩個class文件,因此可以推斷出Kotlin的Lambda表達(dá)式目前是一種基于內(nèi)部類的語法糖實現(xiàn)。

除此之外,我們還可以使用kotlinc(Kotlin編譯器來驗證)

kotlinc Test.kt

執(zhí)行完成后,查看生成的class文件

ls | grep ^Test Test$testObservable$1.class Test.class Test.kt

當(dāng)然,我們還可以使用javap同樣實現(xiàn)查看bytecode的功能,即 javap -c className 。

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

  • lazy初始化

  • when表達(dá)式

  • 方法引用

關(guān)于研究學(xué)習(xí)Kotlin的方法有哪些就分享到這里了,希望以上內(nèi)容可以對大家有一定的幫助,可以學(xué)到更多知識。如果覺得文章不錯,可以把它分享出去讓更多的人看到。

向AI問一下細(xì)節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點不代表本網(wǎng)站立場,如果涉及侵權(quán)請聯(lián)系站長郵箱:is@yisu.com進(jìn)行舉報,并提供相關(guān)證據(jù),一經(jīng)查實,將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI