淺談Kotlin語法篇之頂層函式、中綴呼叫、解構宣告(四)

極客熊貓發表於2018-04-10

簡述: 今天帶來的是Kotlin淺談系列的第四彈,這次主要聊下Kotlin獨有的新特性,Java不具備的。Kotlin是一種增加許多新功能的語言,允許編寫更簡潔易讀的程式碼,這使得我們的程式碼更易於維護。例如使用頂層函式和屬性從此消除Java中的static、中綴表示式呼叫和解構宣告等。

  • 1、為什麼要用頂層函式替代Java中的static函式
  • 2、頂層函式和屬性的基本使用
  • 3、頂層函式實質原理
  • 4、頂層函式使用應注意哪些問題
  • 5、一起嚐嚐中綴呼叫語法糖
  • 6、中綴呼叫的基本使用
  • 7、中綴呼叫實質原理
  • 8、中綴呼叫應注意哪些問題
  • 9、Kotlin中的解構宣告

一、為什麼要用頂層函式替代Java中的static函式

概念: 我們知道在Java中有靜態函式和靜態屬性概念,它們一般作用就是為了提供一個全域性共享訪問區域和方法。我們一般的習慣的寫法就是寫一個類包裹一些static修飾的方法,然後在外部訪問的直接利用類名.方法名訪問。

問題: 我們都知道靜態函式內部是不包含狀態的,也就是所謂的純函式,它的輸入僅僅來自於它的引數列表,而它的輸出也僅僅依賴於它引數列表。我們設想一下這樣開發情景,有時候我們並不想利用例項物件來呼叫函式,所以我們一般會往靜態函式容器類中新增靜態函式,如此反覆,這樣無疑是讓這個類容器膨脹。

解決: 在Kotlin中則認為一個函式或方法有時候並不是屬於任何一個類,它可以獨立存在。所以在Kotlin中類似靜態函式和靜態屬性會去掉外層類的容器,一個函式或者屬性可以直接定義在一個Kotlin檔案的頂層中,在使用的地方只需要import這個函式或屬性即可。如果你的程式碼還存在很多以"Util"字尾結尾的工具類,是時候去掉了。

二、頂層函式和屬性的基本使用

在Koltin中根本不需要去定義一些沒有意義包裹靜態函式的容器類,它們都被頂層檔案給替代。我們只需要定義一個Kotlin File,在裡面定義好一些函式(注意: 不需要static關鍵字)。那麼這些函式就可以當做靜態函式來使用

建立一個頂層檔案:

淺談Kotlin語法篇之頂層函式、中綴呼叫、解構宣告(四)

在頂層檔案中定義一個函式:

package com.mikyou.kotlin.top

import java.math.BigDecimal

/**
 * Created by mikyou on 2018/4/10.
 */
//這個頂層函式不屬於任何一個類,不需要類容器,不需要static關鍵字
fun formateFileSize(size: Double): String {
    if (size < 0) {
        return "0 KB"
    }

    val kBSize = size / 1024
    if (kBSize < 1) {
        return "$size B"
    }

    val mBSize = kBSize / 1024
    if (mBSize < 1) {
        return "${BigDecimal(kBSize.toString()).setScale(1, BigDecimal.ROUND_HALF_UP).toPlainString()} KB"
    }

    val mGSize = mBSize / 1024
    if (mGSize < 1) {
        return "${BigDecimal(mBSize.toString()).setScale(1, BigDecimal.ROUND_HALF_UP).toPlainString()} MB"
    }

    val mTSize = mGSize / 1024
    if (mTSize < 1) {
        return "${BigDecimal(mGSize.toString()).setScale(1, BigDecimal.ROUND_HALF_UP).toPlainString()} GB"
    }
    return "${BigDecimal(mTSize.toString()).setScale(1, BigDecimal.ROUND_HALF_UP).toPlainString()} TB"
}

//測試頂層函式,實際上Kotlin中main函式和Java不一樣,它可以不存在任何類容器中,可以直接定義在一個Kotlin 檔案中
//另一方面也解釋了Kotlin中的main函式不需要了static關鍵字,實際上它自己就是個頂層函式。
fun main(args: Array<String>) {
    println("檔案大小: ${formateFileSize(15582.0)}")
}
複製程式碼

從以上程式碼可以看出定義一個頂層函式是不是很簡單,那麼問題來了這個formateFileSize函式定義在一個檔案內部,在JVM中是怎麼執行的呢?請接著往下看...

三、頂層函式實質原理

通過以上例子我們思考一下頂層函式在JVM中是怎麼執行的,如果你僅僅是在Kotlin中使用這些頂層函式,那麼可以不用細究。但是如果你是Java和Kotlin混合開發模式,那麼你就有必要深入內部原理。我們都知道Kotlin和Java互操作性是很強的,所以就衍生出了一個問題:在Kotlin中定義的頂層函式,在Java可以呼叫嗎?答案肯定是可以的。怎麼呼叫的,請接著看。

要想知道內部呼叫原理很簡單,我們只需要把上面例子程式碼反編譯成Java程式碼就一目瞭然了。這裡科普一下反編譯Kotlin程式碼步驟,因為這是檢視Kotlin語法糖背後實質很好的方法。

步驟一: 確認IDE安裝好了Kotlin Plugin

淺談Kotlin語法篇之頂層函式、中綴呼叫、解構宣告(四)

步驟二: 在IDE中開啟你需要檢視反編譯的程式碼檔案,然後開啟頂部的"Tools",選擇"Kotlin",再選擇"Show Kotlin ByteCode"

淺談Kotlin語法篇之頂層函式、中綴呼叫、解構宣告(四)

步驟三: 左邊是Kotlin的原始碼,右邊是Kotlin的ByteCode

淺談Kotlin語法篇之頂層函式、中綴呼叫、解構宣告(四)

步驟四: 點選右側“Decompile”

package com.mikyou.kotlin.top;

import java.math.BigDecimal;
import kotlin.Metadata;
import kotlin.jvm.internal.Intrinsics;
import org.jetbrains.annotations.NotNull;

@Metadata(
   mv = {1, 1, 9},
   bv = {1, 0, 2},
   k = 2,
   d1 = {"\u0000\u001c\n\u0000\n\u0002\u0010\u000e\n\u0000\n\u0002\u0010\u0006\n\u0000\n\u0002\u0010\u0002\n\u0000\n\u0002\u0010\u0011\n\u0002\b\u0002\u001a\u000e\u0010\u0000\u001a\u00020\u00012\u0006\u0010\u0002\u001a\u00020\u0003\u001a\u0019\u0010\u0004\u001a\u00020\u00052\f\u0010\u0006\u001a\b\u0012\u0004\u0012\u00020\u00010\u0007¢\u0006\u0002\u0010\b¨\u0006\t"},
   d2 = {"formateFileSize", "", "size", "", "main", "", "args", "", "([Ljava/lang/String;)V", "production sources for module Function"}
)
public final class TopExtFileFormatKt {//一般以檔名+"Kt"字尾作為容器類名
   @NotNull
   public static final String formateFileSize(double size) {//頂層函式反編譯成Java中靜態函式
      if(size < (double)0) {
         return "0 KB";
      } else {
         double kBSize = size / (double)1024;
         if(kBSize < (double)1) {
            return "" + size + " B";
         } else {
            double mBSize = kBSize / (double)1024;
            if(mBSize < (double)1) {
               return "" + (new BigDecimal(String.valueOf(kBSize))).setScale(1, 4).toPlainString() + " KB";
            } else {
               double mGSize = mBSize / (double)1024;
               if(mGSize < (double)1) {
                  return "" + (new BigDecimal(String.valueOf(mBSize))).setScale(1, 4).toPlainString() + " MB";
               } else {
                  double mTSize = mGSize / (double)1024;
                  return mTSize < (double)1?"" + (new BigDecimal(String.valueOf(mGSize))).setScale(1, 4).toPlainString() + " GB":"" + (new BigDecimal(String.valueOf(mTSize))).setScale(1, 4).toPlainString() + " TB";
               }
            }
         }
      }
   }

   public static final void main(@NotNull String[] args) {//頂層函式反編譯成Java中靜態函式
      Intrinsics.checkParameterIsNotNull(args, "args");
      String var1 = "檔案大小: " + formateFileSize(15582.0D);
      System.out.println(var1);
   }
}

複製程式碼

通過以上的程式碼可以總結出兩點內容:

  • 1、頂層檔案會反編譯成一個容器類。(類名一般預設就是頂層檔名+"Kt"字尾,注意容器類名可以自定義)
  • 2、頂層函式會反編譯成一個static靜態函式,如程式碼中的formateFileSize和main函式

想必到這裡你大概猜到了Java中如何呼叫Kotlin中的頂層函式了吧。呼叫方式很簡單,就是利用反編譯生成的類作為靜態函式容器類直接呼叫對應的函式

package com.mikyou.kotlin.top;

/**
 * Created by mikyou on 2018/4/10.
 */
public class TopExtTest {
    public static void main(String[] args) {
        System.out.println("檔案大小: " + TopExtFileFormatKt.formateFileSize(1343553));// Java中呼叫Kotlin中定義頂層函式,一般是頂層檔名+"Kt"字尾作為靜態函式的類名呼叫相應函式
    }
}

複製程式碼

四、頂層函式使用應注意的問題

Kotlin中的頂層函式反編譯成的Java中的容器類名一般是頂層檔名+“Kt”字尾作為類名,但是也是可以自定義的。也就是說頂層檔名和生成容器類名沒有必然的聯絡。通過Kotlin中的@file: JvmName("自定義生成類名")註解就可以自動生成對應Java呼叫類名,注意需要放在檔案頂部,在package宣告的前面

//通過@file: JvmName("FileFormatUtil")註解,將生成的類名修改為FileFormatUtil,並且呼叫的時候直接呼叫FileFormatUtil.formateFileSize()即可
//放在檔案頂部,在package宣告的前面
@file: JvmName("FileFormatUtil")

package com.mikyou.kotlin.top

import java.math.BigDecimal

/**
 * Created by mikyou on 2018/4/10.
 */
//這個頂層函式不屬於任何一個類,不需要類容器,不需要static關鍵字
fun formateFileSize(size: Double): String {
    if (size < 0) {
        return "0 KB"
    }

    val kBSize = size / 1024
    if (kBSize < 1) {
        return "$size B"
    }

    val mBSize = kBSize / 1024
    if (mBSize < 1) {
        return "${BigDecimal(kBSize.toString()).setScale(1, BigDecimal.ROUND_HALF_UP).toPlainString()} KB"
    }

    val mGSize = mBSize / 1024
    if (mGSize < 1) {
        return "${BigDecimal(mBSize.toString()).setScale(1, BigDecimal.ROUND_HALF_UP).toPlainString()} MB"
    }

    val mTSize = mGSize / 1024
    if (mTSize < 1) {
        return "${BigDecimal(mGSize.toString()).setScale(1, BigDecimal.ROUND_HALF_UP).toPlainString()} GB"
    }
    return "${BigDecimal(mTSize.toString()).setScale(1, BigDecimal.ROUND_HALF_UP).toPlainString()} TB"
}

//測試頂層函式,實際上Kotlin中main函式和Java不一樣,它可以不存在任何類容器中,可以直接定義在一個Kotlin 檔案中
//另一方面也解釋了Kotlin中的main函式不需要了static關鍵字,實際上它自己就是個頂層函式。
fun main(args: Array<String>) {
    println("檔案大小: ${formateFileSize(15582.0)}")
}
複製程式碼

然後我們再來一起看看反編譯成Java程式碼變成什麼樣了

package com.mikyou.kotlin.top;

import java.math.BigDecimal;
import kotlin.Metadata;
import kotlin.jvm.JvmName;
import kotlin.jvm.internal.Intrinsics;
import org.jetbrains.annotations.NotNull;

@Metadata(
   mv = {1, 1, 9},
   bv = {1, 0, 2},
   k = 2,
   d1 = {"\u0000\u001c\n\u0000\n\u0002\u0010\u000e\n\u0000\n\u0002\u0010\u0006\n\u0000\n\u0002\u0010\u0002\n\u0000\n\u0002\u0010\u0011\n\u0002\b\u0002\u001a\u000e\u0010\u0000\u001a\u00020\u00012\u0006\u0010\u0002\u001a\u00020\u0003\u001a\u0019\u0010\u0004\u001a\u00020\u00052\f\u0010\u0006\u001a\b\u0012\u0004\u0012\u00020\u00010\u0007¢\u0006\u0002\u0010\b¨\u0006\t"},
   d2 = {"formateFileSize", "", "size", "", "main", "", "args", "", "([Ljava/lang/String;)V", "production sources for module Function"}
)
@JvmName(//注意這裡多了註解
   name = "FileFormatUtil"
)
public final class FileFormatUtil {//這裡生成的類名就是註解中自定義生成的類名了
   @NotNull
   public static final String formateFileSize(double size) {
      if(size < (double)0) {
         return "0 KB";
      } else {
         double kBSize = size / (double)1024;
         if(kBSize < (double)1) {
            return "" + size + " B";
         } else {
            double mBSize = kBSize / (double)1024;
            if(mBSize < (double)1) {
               return "" + (new BigDecimal(String.valueOf(kBSize))).setScale(1, 4).toPlainString() + " KB";
            } else {
               double mGSize = mBSize / (double)1024;
               if(mGSize < (double)1) {
                  return "" + (new BigDecimal(String.valueOf(mBSize))).setScale(1, 4).toPlainString() + " MB";
               } else {
                  double mTSize = mGSize / (double)1024;
                  return mTSize < (double)1?"" + (new BigDecimal(String.valueOf(mGSize))).setScale(1, 4).toPlainString() + " GB":"" + (new BigDecimal(String.valueOf(mTSize))).setScale(1, 4).toPlainString() + " TB";
               }
            }
         }
      }
   }

   public static final void main(@NotNull String[] args) {
      Intrinsics.checkParameterIsNotNull(args, "args");
      String var1 = "檔案大小: " + formateFileSize(15582.0D);
      System.out.println(var1);
   }
}

複製程式碼

這樣Java呼叫自定義類名頂層函式就更加自然,一般建議使用註解修改類名,這樣在Java層呼叫還是我們習慣工具類的命名,完全無法感知這個函式是來自Java中還是Kotlin中定義的,做到完全透明。

package com.mikyou.kotlin.top;

/**
 * Created by mikyou on 2018/4/10.
 */
public class TopExtTest {
    public static void main(String[] args) {
        System.out.println("檔案大小: " + FileFormatUtil.formateFileSize(1343553));// Java中呼叫Kotlin中定義頂層函式,如果自定義生成類名,直接用定義類名呼叫。
    }
}

複製程式碼

五、一起嚐嚐中綴呼叫語法糖

中綴呼叫看起來是很高階概念,實際上原理很簡單,但是它在語法層面簡化了很多,更方便以及更容易理解,讓我們寫程式碼會更加接近自然語言。我個人理解中綴呼叫實際上就是把原來只有一個引數的函式呼叫簡化成兩個操作直接使用類似中綴運算子呼叫,省略了類名或者物件名+"."+函式名呼叫方式。廢話不多說,直接發波糖

  • 例子一 初始化map
package com.mikyou.kotlin.infix

/**
 * Created by mikyou on 2018/4/10.
 */
//普通利用Pair()初始化一個map
fun main(args: Array<String>) {
    val map = mapOf(Pair(1, "A"), Pair(2, "B"), Pair(3, "C"))
    map.forEach { key, value ->
        println("key: $key   value:$value")
    }
}

//利用to函式初始化一個map
fun main(args: Array<String>) {
    val map = mapOf(1.to("A"), 2.to("B"), 3.to("C"))
    map.forEach { key, value ->
        println("key: $key   value:$value")
    }
}

//利用to函式中綴呼叫初始化一個map
fun main(args: Array<String>) {
    val map = mapOf(1 to "A", 2 to "B", 3 to "C")//to實際上一個返回Pair物件的函式,不是屬於map結構內部的運算子,但是to在語法層面使用很像中綴運算子呼叫
    map.forEach { key, value ->
        println("key: $key   value:$value")
    }
}
複製程式碼
  • 例子二 字串比較
//普通使用字串對比呼叫StringUtils.equals(strA, strB)
fun main(args: Array<String>) {
	val strA = "A"
	val strB = "B"
	if (StringUtils.equals(strA, strB)) {//這裡對比字串是了apache中的StringUtils
		println("str is the same")
	} else {
		println("str is the different")
	}
}

//利用中綴呼叫sameAs對比兩個字串
fun main(args: Array<String>) {
	val strA = "A"
	val strB = "B"
	if (strA sameAs strB) {//中綴呼叫 sameAs
		println("str is the same")
	} else {
		println("str is the different")
	}
}
複製程式碼
  • 例子三 判斷一個元素是否在集合中
//普通呼叫集合contains方法判斷元素是否在集合中
fun main(args: Array<String>) {
    val list = listOf(1, 3, 5, 7, 9)
    val element = 2
    if (list.contains(element)) {
        println("element: $element is into list")
    } else {
        println("element: $element is not into list")
    }
}

//利用中綴呼叫into判斷元素是否在集合中
fun main(args: Array<String>) {
    val list = listOf(1, 3, 5, 7, 9)
    val element = 2
    if (element into list) {//中綴呼叫,這樣的寫法,會更加接近我們自然語言的表達,更容易理解
        println("element: $element is into list")
    } else {
        println("element: $element is not into list")
    }
}
複製程式碼

六、中綴呼叫的基本使用

中綴呼叫使用非常簡單,準確來說它使用類似加減乘除運算操作符的使用。呼叫結構: A (中綴函式名) B 例如: element into list

七、中綴呼叫實質原理

7.1 to函式系統內建中綴呼叫

  • 原始碼分析
public infix fun <A, B> A.to(that: B): Pair<A, B> = Pair(this, that)
複製程式碼

淺談Kotlin語法篇之頂層函式、中綴呼叫、解構宣告(四)

分析: 使用infix關鍵字修飾的函式,傳入A,B兩個泛型物件,“A.to(B)”結構,是一種特殊結構暫時把它叫做帶接收者的結構,以至於後面的this就是指代A,並且函式的引數只有一個,返回的是一個Pair物件,this指代A,that就是傳入的B型別物件。

  • to函式建立Pair物件,然後可以用解構宣告展開

淺談Kotlin語法篇之頂層函式、中綴呼叫、解構宣告(四)
注意: 說明一下中綴呼叫和解構宣告是兩個不同的概念,沒有必然關係,只能說中綴呼叫返回的一個物件可以使用解構宣告把他們展開,在展開圖中是解構相等的。例如圖上的to函式返回的是一個Pair()物件,Pair物件支援解構宣告,然後to函式返回物件和解構後的物件相等。

7.2 into函式自定義中綴呼叫

  • 原始碼分析
infix fun <T> T.into(other: Collection<T>): Boolean = other.contains(this)
複製程式碼

淺談Kotlin語法篇之頂層函式、中綴呼叫、解構宣告(四)

分析: 使用infix關鍵字修飾的函式,泛型T物件元素是否存在於泛型T集合之中。“T.into(Collection<T>)”結構,實際也是一種帶接收者結構,this也就是指代了into函式前面呼叫T型別物件。

八、中綴呼叫應注意哪些問題

  • 1、前面所講to, into,sameAs實際上就是函式呼叫,如果把infix關鍵字去掉,那麼也就純粹按照函式呼叫方式來。比如1.to("A"), element.into(list)等,只有加了中綴呼叫的關鍵字infix後,才可以使用簡單的中綴呼叫例如 1 to "A", element into list等

  • 2、並不是所有的函式都能寫成中綴呼叫,中綴呼叫首先必須滿足一個條件就是函式的引數只有一個。然後再看這個函式的參與者是不是隻有兩個元素,這兩個元素可以是兩個數,可以是兩個物件,可以是集合等。

九、Kotlin中的解構宣告

9.1 解構宣告概述

解構宣告是把一個物件看成一組單獨的變數,有時候我們把一個物件看成一組單獨的變數管理會變得更加簡單。注意: 支援解構宣告的物件的類必須是資料類(使用data關鍵字修飾的類),因為只有data class才會生成對應的component()方法(這個會在後續中講解到),data class中的每個屬性都會有對應的component()方法對應

9.2 解構宣告基本使用

  • 先定義一個data class
package com.mikyou.kotlin.destruct

/**
 * Created by mikyou on 2018/4/10.
 */
data class Student(var name: String, var age: Int, var grade: Double)
複製程式碼
  • 解構宣告呼叫
package com.mikyou.kotlin.destruct

/**
 * Created by mikyou on 2018/4/10.
 */
fun main(args: Array<String>) {
    val student = Student("mikyou", 18, 99.0)
    val (name, age, grade) = student//將一個student物件解構成一組3個單獨的變數
    println("my name is $name , I'm $age years old, I get $grade score")//解構後的3個變數可以脫離物件,直接單獨使用

}
複製程式碼

9.3 解構宣告實質原理

解構宣告實際上就是將物件中所有屬性,解構成一組屬性變數,而且這些變數可以單獨使用,為什麼可以單獨使用,是因為每個屬性值的獲得最後都編譯成通過呼叫與之對應的component()方法,每個component()方法對應著類中每個屬性的值,然後在作用域定義各自屬性區域性變數,這些區域性變數儲存著各自對應屬性的值,所以看起來變數可以單獨使用,實際上使用的是區域性變數。如下反編譯成的Java程式碼

package com.mikyou.kotlin.destruct;

import kotlin.Metadata;
import kotlin.jvm.internal.Intrinsics;
import org.jetbrains.annotations.NotNull;

@Metadata(
   mv = {1, 1, 9},
   bv = {1, 0, 2},
   k = 2,
   d1 = {"\u0000\u0014\n\u0000\n\u0002\u0010\u0002\n\u0000\n\u0002\u0010\u0011\n\u0002\u0010\u000e\n\u0002\b\u0002\u001a\u0019\u0010\u0000\u001a\u00020\u00012\f\u0010\u0002\u001a\b\u0012\u0004\u0012\u00020\u00040\u0003¢\u0006\u0002\u0010\u0005¨\u0006\u0006"},
   d2 = {"main", "", "args", "", "", "([Ljava/lang/String;)V", "production sources for module Function"}
)
public final class DestructTestKt {
   public static final void main(@NotNull String[] args) {
      Intrinsics.checkParameterIsNotNull(args, "args");
      Student student = new Student("mikyou", 18, 99.0D);
      String name = student.component1();//對應的component1()方法,返回對應就是Student中name屬性,並賦值給建立區域性變數name
      int age = student.component2();//對應的component2()方法,返回對應就是Student中age屬性, 並賦值給建立區域性變數age
      double grade = student.component3();//對應的component3()方法,返回對應就是Student中屬性,並賦值給建立區域性變數grade
      String var6 = "my name is " + name + " , I'm " + age + " years old, I get " + grade + " score";
      System.out.println(var6);//注意: 這裡單獨使用的name, age, grade實際上是區域性變數
   }
}

複製程式碼

淺談Kotlin語法篇之頂層函式、中綴呼叫、解構宣告(四)

9.4 解構宣告應注意的問題

  • 1、解構宣告中解構物件的屬性是可選的,也就是並不是要求該物件中所有屬性都需要解構,也就是可選擇需要解構的屬性。可以使用下劃線"_"省略不需要解構的屬性也可以不寫改屬性直接忽略。注意: 雖然兩者作用是一樣的,但是最後生成component()方法不一樣,使用下劃線的會佔用compoent1()方法,age直接從component2()開始生成, 直接不寫name,age會從component1()方法開始生成。例如

下劃線_ 忽略name屬性例子

package com.mikyou.kotlin.destruct

/**
 * Created by mikyou on 2018/4/10.
 */
fun main(args: Array<String>) {
    val student = Student("mikyou", 18, 99.0)
    val (_, age, grade) = student//下劃線_ 忽略name屬性
    println("I'm $age years old, I get $grade score")//解構後的3個變數可以脫離物件,直接單獨使用
}

//下劃線_ 忽略name屬性 反編譯後Java程式碼
package com.mikyou.kotlin.destruct;

import kotlin.Metadata;
import kotlin.jvm.internal.Intrinsics;
import org.jetbrains.annotations.NotNull;

@Metadata(
   mv = {1, 1, 9},
   bv = {1, 0, 2},
   k = 2,
   d1 = {"\u0000\u0014\n\u0000\n\u0002\u0010\u0002\n\u0000\n\u0002\u0010\u0011\n\u0002\u0010\u000e\n\u0002\b\u0002\u001a\u0019\u0010\u0000\u001a\u00020\u00012\f\u0010\u0002\u001a\b\u0012\u0004\u0012\u00020\u00040\u0003¢\u0006\u0002\u0010\u0005¨\u0006\u0006"},
   d2 = {"main", "", "args", "", "", "([Ljava/lang/String;)V", "production sources for module Function"}
)
public final class DestructTestKt {
   public static final void main(@NotNull String[] args) {
      Intrinsics.checkParameterIsNotNull(args, "args");
      Student student = new Student("mikyou", 18, 99.0D);
      int age = student.component2();//name會佔用component1()方法,但是沒有生成,所以age從component2()方法開始
      double grade = student.component3();
      String var5 = "I'm " + age + " years old, I get " + grade + " score";
      System.out.println(var5);
   }
}

複製程式碼

直接不寫name屬性例子

package com.mikyou.kotlin.destruct

/**
 * Created by mikyou on 2018/4/10.
 */
fun main(args: Array<String>) {
    val student = Student("mikyou", 18, 99.0)
    val (age, grade) = student//直接不寫name屬性
    println("I'm $age years old, I get $grade score")//解構後的3個變數可以脫離物件,直接單獨使用
}

//直接不寫name屬性 反編譯後Java程式碼
package com.mikyou.kotlin.destruct;

import kotlin.Metadata;
import kotlin.jvm.internal.Intrinsics;
import org.jetbrains.annotations.NotNull;

@Metadata(
   mv = {1, 1, 9},
   bv = {1, 0, 2},
   k = 2,
   d1 = {"\u0000\u0014\n\u0000\n\u0002\u0010\u0002\n\u0000\n\u0002\u0010\u0011\n\u0002\u0010\u000e\n\u0002\b\u0002\u001a\u0019\u0010\u0000\u001a\u00020\u00012\f\u0010\u0002\u001a\b\u0012\u0004\u0012\u00020\u00040\u0003¢\u0006\u0002\u0010\u0005¨\u0006\u0006"},
   d2 = {"main", "", "args", "", "", "([Ljava/lang/String;)V", "production sources for module Function"}
)
public final class DestructTestKt {
   public static final void main(@NotNull String[] args) {
      Intrinsics.checkParameterIsNotNull(args, "args");
      Student student = new Student("mikyou", 18, 99.0D);
      String age = student.component1();//直接不寫name,然後age從component
      int grade = student.component2();
      String var4 = "I'm " + age + " years old, I get " + grade + " score";
      System.out.println(var4);
   }
}

複製程式碼
  • 2、解構宣告的物件型別一定是data class,普通的class是不會生成對應的component的方法。
淺談Kotlin語法篇之頂層函式、中綴呼叫、解構宣告(四)

歡迎關注Kotlin開發者聯盟,這裡有最新Kotlin技術文章,每週會不定期翻譯一篇Kotlin國外技術文章。如果你也喜歡Kotlin,歡迎加入我們~~~

相關文章