首頁>技術>

object 關鍵字

Kotlin 中有一種特殊的類,它本身也是一個例項(單例),這種既是類又是物件的類需要使用 object 關鍵字宣告(普通類宣告使用 class),它跟普通類一樣,也可以實現介面和繼承父類:

object MusicPlayer : Player(), OnStatusChangeListener {    var state: Int = 0    fun play(url: String) {        ...    }    fun stop() {        ...    }    override fun onMount(driver: Driver) {    }    override fun onUnmount(driver: Driver) {    }}

這種 object 類的方法可以透過類名直接呼叫:

fun main() {    MusicPlayer.play("http://qqmusic.com/123213.mp3")    MusicPlayer.stop()}

藉助 Show Kotlin Bytecode 工具,將上述 Kotlin 程式碼反編譯成 java 程式碼如下:

public final class MusicPlayer extends Player implements OnStatusChangeListener {   public static final MusicPlayer INSTANCE;   private MusicPlayer() {   }   static {      MusicPlayer var0 = new MusicPlayer();      INSTANCE = var0;   }   ...}

可以發現,Kotlin 中的 object 本質上就是 Java 中簡單的單例模式,同時它的構造方法是 private 的,因此 object 類不能自定義構造方法!

伴生物件(companion object)

日常開發中,經常會編寫一些 Bean 類,用於描述一類事物,Bean 中會包含事物的屬性,有的 Bean 類還會提供一系列"工具方法"來幫助開發者快速建立 Bean 物件,就比如在 Java 中,Integer.valueOf() 就可以幫助開發者快速將字串轉成整型,進入原始碼一看,可以發現這是一個使用了 static 關鍵字修飾的靜態方法:

public static Integer valueOf(String s) throws NumberFormatException {    return Integer.valueOf(parseInt(s, 10));}

可是,在 Kotlin 中是沒有 static 關鍵字的!!!想要實現透過 類名.xxx() 的方式呼叫工具方法,可以使用上述的 object 類,但 object 本身就是一個物件,我們無法在外部呼叫其構造方法,既然想擁有 object 類(透過類名呼叫方法)與 class 類(外部可以呼叫構造方法)的兩種特性,這時伴生物件 companion object 就完全可以滿足這個需求:

class Rectangle(val width: Int, val height: Int) {    companion object {        fun ofSize(width: Int, height: Int): Rectangle {            return Rectangle(width, height)        }        fun ofRectangle(rectangle: Rectangle): Rectangle {            return Rectangle(rectangle.width, rectangle.height)        }    }}fun main() {    val width = 4    val height = 5    val rectangle1 = Rectangle(width, height) // 呼叫構造方法    val rectangle2 = Rectangle.ofRectangle(rectangle1) // 透過類名呼叫方法    val rectangle3 = Rectangle.ofSize(width, height) // 透過類名呼叫方法}

伴生物件顧名思義,就是與類一起誕生的物件,類一載入,它的伴生物件也就被建立了,每個類都可以對應一個伴生物件,並且該伴生物件的成員全域性獨一份,也就是說伴生物件 companion object 也是一種單例,再次藉助 Show Kotlin Bytecode 工具,將上述 Kotlin 程式碼反編譯成 java 程式碼如下:

public final class Rectangle {   ...   public static final Rectangle.Companion Companion = new Rectangle.Companion((DefaultConstructorMarker)null);   public static final class Companion {      @NotNull      public final Rectangle ofSize(int width, int height) {         return new Rectangle(width, height);      }      @NotNull      public final Rectangle ofRectangle(@NotNull Rectangle rectangle) {         Intrinsics.checkParameterIsNotNull(rectangle, "rectangle");         return new Rectangle(rectangle.getWidth(), rectangle.getHeight());      }      private Companion() {      }      // $FF: synthetic method      public Companion(DefaultConstructorMarker $constructor_marker) {         this();      }   }}

可以發現,Kotlin 中的 companion object 其實對應到 Java 中也就只是 Rectangle 的一個靜態內部類例項而已。

JVM 靜態成員(@JvmStatic)

我們把上面 Kotlin 的 main 方法中的程式碼用 java 重寫一遍:

int width = 4;int height = 5;Rectangle rectangle1 = new Rectangle(width,height);Rectangle rectangle2 = Rectangle.Companion.ofSize(width,height);Rectangle rectangle3 = Rectangle.Companion.ofRectangle(rectangle2);

發現 ofSize 和 ofRectangle 方法並不是透過 Rectangle 類直接呼叫的,結合上述反編譯後的 java 程式碼,應該不難理解,因為 Kotlin 中 Rectangle 的伴生物件本質上就是這個 Rectangle.Companion 例項,ofSize 和 ofRectangle 都是 Rectangle.Companion 的方法,所以,用 Rectangle.Companion 物件呼叫它自己的方法這完全沒有毛病,可是,難道就不能在 Java 中也像 Kotlin 那樣,直接透過 類名.xxx() 的方法來呼叫伴生物件方法嗎?答案肯定是有的啦,Kotlin 提供了 @JvmStatic 和 @JvmField,可以讓伴生物件中的方法和屬性在 java 中透過類名直接呼叫:

class Rectangle(val width: Int, val height: Int) {    companion object {        @JvmStatic        fun ofSize(width: Int, height: Int): Rectangle {            return Rectangle(width, height)        }        @JvmStatic        fun ofRectangle(rectangle: Rectangle): Rectangle {            return Rectangle(rectangle.width, rectangle.height)        }        @JvmField        val Tag = "Rectangle"    }}

這次 java 程式碼也可以使用 Rectangle 類名來呼叫 ofSize 和 ofRectangle 方法了:

int width = 4;int height = 5;Rectangle rectangle1 = new Rectangle(width, height);Rectangle rectangle2 = Rectangle.ofSize(width, height);Rectangle rectangle3 = Rectangle.ofRectangle(rectangle2);System.out.println(Rectangle.Tag);

再來反編譯一次,看其究竟是什麼原理,原來就只是在 Rectangle 中增加了對應的 static 屬性和方法而已:

public final class Rectangle {   public static final Rectangle.Companion Companion = new Rectangle.Companion((DefaultConstructorMarker)null);   @JvmField   @NotNull   public static final String Tag = "Rectangle";   @JvmStatic   @NotNull   public static final Rectangle ofSize(int width, int height) {      return Companion.ofSize(width, height);   }   @JvmStatic   @NotNull   public static final Rectangle ofRectangle(@NotNull Rectangle rectangle) {      return Companion.ofRectangle(rectangle);   }   public static final class Companion {      @JvmStatic      @NotNull      public final Rectangle ofSize(int width, int height) {          ...      }      @JvmStatic      @NotNull      public final Rectangle ofRectangle(@NotNull Rectangle rectangle) {          ...      }   }}

8
最新評論
  • BSA-TRITC(10mg/ml) TRITC-BSA 牛血清白蛋白改性標記羅丹明
  • OpenSIPS的排程呼叫失效處理控制討論