Kotlin與Java互操作筆記

Kotlin語言基礎筆記

Kotlin流程控制語句筆記

Kotlin操作符重載與中綴表示法筆記

Kotlin擴展函數(shù)和擴展屬性筆記

Kotlin空指針安全(null-safety)筆記

Kotlin類型系統(tǒng)筆記

Kotlin面向對象編程筆記

Kotlin委托(Delegation)筆記

Kotlin泛型型筆記

Kotlin函數(shù)式編程筆記

Kotlin與Java互操作筆記

Kotlin協(xié)程筆記

Kotlin官方一直以100% interoperable with Java?作為第一要素,他不是像Scala一樣把類庫都自己實現(xiàn)一遍空免,而是通過擴展函數(shù)种远、函數(shù)編程等特性對現(xiàn)有的Java進行增強,同時保持對Java的100%兼容智蝠。正是這個特性,我們可以在一個項目中同時使用Java和Kotlin奈梳,一個大型的項目杈湾,如果換一種語言來實現(xiàn)的話,這個代價是非常大的攘须。但是對于一個Java項目漆撞,你可以某一部分使用Kotlin來實現(xiàn),然后慢慢地一步步的把整個項目所有代碼都改成Kotlin實現(xiàn)于宙。這樣風險就會小非常多浮驳。

1. Kotlin調用Java

1.1 Kotlin使用Java的集合類

    val kotlinList = listOf(1, 2, 3, 4)

    //Java原生的ArrayList
    val javaList = ArrayList<Int>()

    for (item in kotlinList) {
        javaList.add(item)
    }

操作Java原生的集合類跟Kotlin中使用類沒有什么區(qū)別。

1.2 調用Java類的getter和setter

假如我們有這樣一個Person類捞魁。

package com.dengyin2000.java;

public class Person {
    private String name;
    private Long id;
    private boolean isFemale;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public Long getId() {
        return id;
    }

    public void setId(Long id) {
        this.id = id;
    }

    public boolean isFemale() {
        return isFemale;
    }

    public void setFemale(boolean female) {
        isFemale = female;
    }
}

那么在Kotlin中要怎樣使用呢抹恳?

    val person = Person()
    person.id = 1
    person.name = "Denny"
    person.isFemale = false

    println("Person(id=${person.id}, name=${person.name}, isFemale=${person.isFemale})")

println函數(shù)中的person類調用的是Person類的getter,上面屬性設置實際上調用的是Person類的setter方法署驻。

1.3 空安全

Java聲明的類型在Kotlin中會被特別對待并稱為平臺類型(platform types)奋献,對于這種類型的空檢查會放寬健霹,這樣做就使得跟Java的調用方式一致,但是Java中的任何引用都可能為null瓶蚂,這樣我們使用Kotlin調用來自Java的對象的時候就有可能會出現(xiàn)空安全的問題糖埋。例如:

    val person = Person()
    person.id = 1
    person.isFemale = false

    println(person.name.substring(1))

person.name運行時為null,當然調用的時候會person.name.substring(1)時會發(fā)生異常窃这。


異常

當然為了避免null的問題瞳别,我們可以使用Koltin的安全調用:

    println(person.name?.substring(1))  //打印null

1.4 平臺類型

平臺類型不能在程序中顯式表述,因此在語言中沒有相應語法杭攻。 然而祟敛,編譯器和 IDE 有時需要(在錯誤信息中、參數(shù)信息中等)顯示他們兆解,所以我們用一個助記符來表示他們:

  • T! : 表示 T 或者 T?
  • (Mutable) Collection<T>! : 表示 “可以可變或不可變馆铁、可空或不可空的 T 的 Java 集合”
  • Array<(out) T>! : 表示“可空或者不可空的 T(或 T 的子類型)的 Java 數(shù)組”

1.5 Kotlin與Java中的類型映射

Kotlin 特殊處理一部分 Java 類型。這樣的類型不是“按原樣”從 Java 加載锅睛,而是 映射 到相應的 Kotlin 類型埠巨。 映射只發(fā)生在編譯期間,運行時表示保持不變现拒。怎么理解這句話呢辣垒?就是說在Kotlin中加載這些Java的類型時,編譯器會轉成對應的Kotlin類型印蔬,這樣就能用到Koltin中對Java的增強功能(擴展函數(shù)等)勋桶。

1.5.1 Java的原生類型映射到對應的Kotlin類型
Java的原生類型映射到對應的Kotlin類型
1.5.2 Java中的一些內置類型也會做相應的映射
Java中的一些內置類型也會做相應的映射
1.5.3 Java的基本類型的包裝類對應到可空額Kotlin類型
Java的基本類型的包裝類對應到可空額Kotlin類型
1.5.4 類型參數(shù)的Java類型映射到Kotlin中的平臺類型

例如:List<java.lang.Integer> 在Kotlin會變成List<Int!>
集合類型在 Kotlin 中可以是只讀的或可變的,因此 Java 集合類型作如下映射: (下表中的所有 Kotlin 類型都在 kotlin.collections包中):


類型參數(shù)的Java類型映射到Kotlin中的平臺類型
1.5.5 Java的數(shù)組映射
Java的數(shù)組映射

1.6 Kotlin中使用Java泛型

Kotlin 的泛型與 Java 有點不同侥猬。當將 Java 類型導入 Kotlin 時例驹,我們會執(zhí)行一些轉換:
Kotlin中使用Java泛型

和 Java 一樣,Kotlin 在運行時不保留泛型陵究,即對象不攜帶傳遞到他們構造器中的那些類型參數(shù)的實際類型。

1.7 Java可變參數(shù)

假如我們有一個這樣的類奥帘,有一個可變參數(shù)的靜態(tài)方法:

package com.dengyin2000.java;

public class StringUtils {

    public static String connect(String... strings) {
        StringBuilder sb = new StringBuilder();
        for (String string : strings) {
            sb.append(string).append(",");
        }
        return sb.toString();
    }
}

因為Kotlin并沒有可變類型铜邮,所以我們需要使用*來傳遞一個String數(shù)組來達到相應的目的:

    val listOf = arrayOf("Denny", "Deng")
    println(StringUtils.connect(*listOf))  //打印Denny,Deng

1.8 Unchecked Exception

在Kotlin中,所有的異常都是Unchecked Exception寨蹋,也就是說編譯器不會強迫你捕獲任何的異常松蒜,但是在Java中你是需要捕獲Checked Exception。如下:


Java Checked Exception

我們需要try catch保護起來已旧。


image.png

但是在Kotlin中不需要try catch秸苗,只是路過拋出來你沒有try catch的話,程序還是會掛运褪。

1.9 java.lang.Object方法使用

當 Java 類型導入到 Kotlin 中時惊楼,類型 java.lang.Object 的所有引用都成了 Any玖瘸。 而因為 Any 不是平臺指定的,它只聲明了 toString()檀咙、hashCode() 和 equals() 作為其成員雅倒, 所以為了能用到 java.lang.Object 的其他成員,你需要如下手段:

1.9.1 wait() / notify()

需要把對象轉成java.lang.Object來使用:

    val p = Person()
    (p as java.lang.Object).wait()
1.9.2 getClass()

要取得對象的 Java 類弧可,我們可以在類引用上使用 java 擴展屬性蔑匣,它是Kotlin的反射類kotlin.reflect.KClass的擴展屬性。

val fooClass = foo::class.java

上面的代碼使用了自 Kotlin 1.1 起支持的綁定類引用棕诵。我們也可以使用 javaClass 擴展屬性裁良。

val fooClass = foo.javaClass
1.9.3 clone()

要覆蓋 clone(),需要繼承 kotlin.Cloneable

class Example : Cloneable {
    override fun clone(): Any { …… }
}

1.10 Kotlin與Java 的反射

我們可以使用 instance::class.java校套、ClassName::class.java 或者 instance.javaClass 通過 java.lang.Class 來進入 Java 的反射類java.lang.Class价脾, 之后我們就可以使用Java中的反射的功能特性了。

1.11 SAM轉換

SAM = single abstract method搔确,在Java中被稱為SAM類型彼棍。例如:Runnable接口。在Kotlin中我們可以這樣創(chuàng)建SAM接口實例:

val runnable = Runnable { println("SAM") } 

1.12 Java使用了Kotlin的關鍵字

一些 Kotlin 關鍵字在 Java 中是有效標識符:in膳算、 object座硕、 is等等。
如果一個 Java 庫使用了 Kotlin 關鍵字作為方法涕蜂,我們可以通過反引號(`)字符轉義它來調用該方法华匾。例如我們有個Java類,其中有個is方法:

public class StringUtils {

    public static boolean is(String value, String value1) {
        return value.equals(value1);
    }

}

我們需要在Kotlin中需要這樣調用:

println(StringUtils.`is`("a", "b"))  //打印false

2. Java調用Kotlin

Kotlin最終還是會編譯成class机隙,所以要怎么調Kotlin其實就是要看Kotlin翻譯成的class是怎樣的蜘拉。Kotlin可以通過一些annotation來調整Kotlin翻譯成的class結果。

2.1 Java訪問Kotlin屬性

假如我們有下面一個Kotlin的類:

class Student{
    var id: Long = -1L
    var name: String = "Denny"
    var isFemale: Boolean = false
}

我們通過Intellij IDEA的Tools->Kotlin->Show Kotlin Bytecode->Decompile可以看到生成的Java的代碼如下:

public final class Student {
   private long id = -1L;
   @NotNull
   private String name = "Denny";
   private boolean isFemale;

   public final long getId() {
      return this.id;
   }

   public final void setId(long var1) {
      this.id = var1;
   }

   @NotNull
   public final String getName() {
      return this.name;
   }

   public final void setName(@NotNull String var1) {
      Intrinsics.checkParameterIsNotNull(var1, "<set-?>");
      this.name = var1;
   }

   public final boolean isFemale() {
      return this.isFemale;
   }

   public final void setFemale(boolean var1) {
      this.isFemale = var1;
   }
}

看到Java代碼有鹿,你應該知道怎么調用了吧旭旭。

2.2 Java調用Kotlin包級函數(shù)

package com.dengyin2000.kotlintest1包里面的StringUtil.kt文件中生命的所有函數(shù)、屬性葱跋、都將編譯成一個名為com.dengyin2000.kotlintest1.StringUtilKt的Java類的靜態(tài)方法持寄。假如我們有一個下面的Kotlin文件:

package com.dengyin2000.kotlintest1

fun sayHello() {
    println("Hello ${name}")
}

val name:String = "Denny"

fun String.firstChar() :Char{
    return this[0]
}

fun main(args: Array<String>) {
    println("Denny".firstChar())
}

通過上面的方式可以看到生成的Java代碼如下:

package com.dengyin2000.kotlintest1;

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

public final class StringUtilKt {
   @NotNull
   private static final String name = "Denny";

   public static final void sayHello() {
      String var0 = "Hello " + name;
      System.out.println(var0);
   }

   @NotNull
   public static final String getName() {
      return name;
   }

   public static final char firstChar(@NotNull String $receiver) {
      Intrinsics.checkParameterIsNotNull($receiver, "$receiver");
      return $receiver.charAt(0);
   }

   public static final void main(@NotNull String[] args) {
      Intrinsics.checkParameterIsNotNull(args, "args");
      char var1 = firstChar("Denny");
      System.out.println(var1);
   }
}

可以看到String的擴展函數(shù)第一個參數(shù)變成了接受者。如果我們想要修改生成的Java對象的名稱娱俺,我們可以使用@file:JvmName注解稍味,如下:

@file:JvmName("Strings")

package com.dengyin2000.kotlintest1

fun sayHello() {
    println("Hello ${name}")
}

val name:String = "Denny"

fun String.firstChar() :Char{
    return this[0]
}

fun main(args: Array<String>) {
    println("Denny".firstChar())
}

這樣生成的類名變成了Strings。

2.3 實例字段

如果我們不希望某個屬性生成getter setter方法荠卷,我們希望生成一個實例字段的話模庐,我們可以使用@JvmField注解標注Kotlin的屬性。如下:

class Student{
    var id: Long = -1L
    var name: String = "Denny"
    var isFemale: Boolean = false
    
    @JvmField
    var grade: Int = 0
}

生成的Java如下:

public final class Student {
   private long id = -1L;
   @NotNull
   private String name = "Denny";
   private boolean isFemale;
   @JvmField
   public int grade;

   public final long getId() {
      return this.id;
   }

   public final void setId(long var1) {
      this.id = var1;
   }

   @NotNull
   public final String getName() {
      return this.name;
   }

   public final void setName(@NotNull String var1) {
      Intrinsics.checkParameterIsNotNull(var1, "<set-?>");
      this.name = var1;
   }

   public final boolean isFemale() {
      return this.isFemale;
   }

   public final void setFemale(boolean var1) {
      this.isFemale = var1;
   }
}

2.4 靜態(tài)字段

在伴生對象和命名對象(object 類)的屬性上使用@JvmField的區(qū)別如下:

class Student{
    var id: Long = -1L
    var name: String = "Denny"
    var isFemale: Boolean = false

    @JvmField
    var grade: Int = 0

    companion object {
        var teachName = "Sally"

        @JvmField
        var schoolMaster = "Noah"
    }
}

生成的Java代碼如下:

public final class Student {
   private long id = -1L;
   @NotNull
   private String name = "Denny";
   private boolean isFemale;
   @JvmField
   public int grade;
   @NotNull
   private static String teachName = "Sally";
   @JvmField
   @NotNull
   public static String schoolMaster = "Noah";
   public static final Student.Companion Companion = new Student.Companion((DefaultConstructorMarker)null);

   public final long getId() {
      return this.id;
   }

   public final void setId(long var1) {
      this.id = var1;
   }

   @NotNull
   public final String getName() {
      return this.name;
   }

   public final void setName(@NotNull String var1) {
      Intrinsics.checkParameterIsNotNull(var1, "<set-?>");
      this.name = var1;
   }

   public final boolean isFemale() {
      return this.isFemale;
   }

   public final void setFemale(boolean var1) {
      this.isFemale = var1;
   }
   public static final class Companion {
      @NotNull
      public final String getTeachName() {
         return Student.teachName;
      }

      public final void setTeachName(@NotNull String var1) {
         Intrinsics.checkParameterIsNotNull(var1, "<set-?>");
         Student.teachName = var1;
      }

      private Companion() {
      }

      // $FF: synthetic method
      public Companion(DefaultConstructorMarker $constructor_marker) {
         this();
      }
   }
}

調用方式如下:

        String schoolMaster = Student.schoolMaster;
        String teachName = Student.Companion.getTeachName();

2.5 靜態(tài)方法

類似的只要將伴生對象或者命名對象的的方法用@JvmStatic注釋油宜,這樣就能生成Java的靜態(tài)方法掂碱。Kotlin代碼如下:

class Student{
    var id: Long = -1L
    var name: String = "Denny"
    var isFemale: Boolean = false

    @JvmField
    var grade: Int = 0

    companion object {
        var teachName = "Sally"

        @JvmField
        var schoolMaster = "Noah"

        @JvmStatic
        fun sayHello() {
            println("Hello world")
        }
    }
}

生成的Java代碼如下:

public final class Student {
   private long id = -1L;
   @NotNull
   private String name = "Denny";
   private boolean isFemale;
   @JvmField
   public int grade;
   @NotNull
   private static String teachName = "Sally";
   @JvmField
   @NotNull
   public static String schoolMaster = "Noah";
   public static final Student.Companion Companion = new Student.Companion((DefaultConstructorMarker)null);

   public final long getId() {
      return this.id;
   }

   public final void setId(long var1) {
      this.id = var1;
   }

   @NotNull
   public final String getName() {
      return this.name;
   }

   public final void setName(@NotNull String var1) {
      Intrinsics.checkParameterIsNotNull(var1, "<set-?>");
      this.name = var1;
   }

   public final boolean isFemale() {
      return this.isFemale;
   }

   public final void setFemale(boolean var1) {
      this.isFemale = var1;
   }

   @JvmStatic
   public static final void sayHello() {
      Companion.sayHello();
   }

   public static final class Companion {
      @NotNull
      public final String getTeachName() {
         return Student.teachName;
      }

      public final void setTeachName(@NotNull String var1) {
         Intrinsics.checkParameterIsNotNull(var1, "<set-?>");
         Student.teachName = var1;
      }

      @JvmStatic
      public final void sayHello() {
         String var1 = "Hello world";
         System.out.println(var1);
      }

      private Companion() {
      }

      // $FF: synthetic method
      public Companion(DefaultConstructorMarker $constructor_marker) {
         this();
      }
   }
}

這樣調用

Student.sayHello();

2.6 生成重載方法

通常怜姿,如果你寫一個有默認參數(shù)值的 Kotlin 函數(shù),在 Java 中只會有一個所有參數(shù)都存在的完整參數(shù)簽名的方法可見顶吮,如果希望向 Java 調用者暴露多個重載社牲,可以使用 @JvmOverloads 注解。

該注解也適用于構造函數(shù)悴了、靜態(tài)方法等搏恤。它不能用于抽象方法,包括在接口中定義的方法湃交。

Kotlin的類如下:

class Animal @JvmOverloads constructor(name: String, type: Int = 0){
    
    fun talk(name: String, by: Int = 1) {

    }

    @JvmOverloads
    fun talkTo(name: String, by: Int = 1) {
        
    }
}

生成的Java代碼如下:

public final class Animal {
   public final void talk(@NotNull String name, int by) {
      Intrinsics.checkParameterIsNotNull(name, "name");
   }

   // $FF: synthetic method
   // $FF: bridge method
   public static void talk$default(Animal var0, String var1, int var2, int var3, Object var4) {
      if ((var3 & 2) != 0) {
         var2 = 1;
      }

      var0.talk(var1, var2);
   }

   @JvmOverloads
   public final void talkTo(@NotNull String name, int by) {
      Intrinsics.checkParameterIsNotNull(name, "name");
   }

   // $FF: synthetic method
   // $FF: bridge method
   @JvmOverloads
   public static void talkTo$default(Animal var0, String var1, int var2, int var3, Object var4) {
      if ((var3 & 2) != 0) {
         var2 = 1;
      }

      var0.talkTo(var1, var2);
   }

   @JvmOverloads
   public final void talkTo(@NotNull String name) {
      talkTo$default(this, name, 0, 2, (Object)null);
   }

   @JvmOverloads
   public Animal(@NotNull String name, int type) {
      Intrinsics.checkParameterIsNotNull(name, "name");
      super();
   }

   // $FF: synthetic method
   @JvmOverloads
   public Animal(String var1, int var2, int var3, DefaultConstructorMarker var4) {
      if ((var3 & 2) != 0) {
         var2 = 0;
      }

      this(var1, var2);
   }

   @JvmOverloads
   public Animal(@NotNull String name) {
      this(name, 0, 2, (DefaultConstructorMarker)null);
   }
}

生成了兩個構造方法熟空,talkTo也生成了兩個方法,talk就只有一個搞莺;


重載構造方法
重載方法

2.7 可見性

Kotlin 的可見性與Java的可見性的映射關系如下表所示:


可見性

2.8 Kotlin中異常

在Kotlin中是不需要顯示的try catch Checked Exception的息罗,比如下面這個throwException方法:

class Animal @JvmOverloads constructor(name: String, type: Int = 0){

    fun talk(name: String, by: Int = 1) {

    }

    @JvmOverloads
    fun talkTo(name: String, by: Int = 1) {

    }

    fun throwException() {
        throw Exception("hahaha")
    }
}

在Java中調用不需要try catch。

no try catch

如果你想讓Java調用時需要主動try catch的話才沧,那你需要使用@Throws(Exception::class)注解迈喉。
注解

這時候Java調用方就需要try catch了。
try catch

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
  • 序言:七十年代末温圆,一起剝皮案震驚了整個濱河市挨摸,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌岁歉,老刑警劉巖得运,帶你破解...
    沈念sama閱讀 206,482評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異锅移,居然都是意外死亡熔掺,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,377評論 2 382
  • 文/潘曉璐 我一進店門非剃,熙熙樓的掌柜王于貴愁眉苦臉地迎上來置逻,“玉大人,你說我怎么就攤上這事备绽∪耄” “怎么了?”我有些...
    開封第一講書人閱讀 152,762評論 0 342
  • 文/不壞的土叔 我叫張陵疯坤,是天一觀的道長报慕。 經(jīng)常有香客問我深浮,道長压怠,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 55,273評論 1 279
  • 正文 為了忘掉前任飞苇,我火速辦了婚禮菌瘫,結果婚禮上蜗顽,老公的妹妹穿的比我還像新娘。我一直安慰自己雨让,他們只是感情好雇盖,可當我...
    茶點故事閱讀 64,289評論 5 373
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著栖忠,像睡著了一般崔挖。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上庵寞,一...
    開封第一講書人閱讀 49,046評論 1 285
  • 那天狸相,我揣著相機與錄音,去河邊找鬼捐川。 笑死脓鹃,一個胖子當著我的面吹牛,可吹牛的內容都是我干的古沥。 我是一名探鬼主播瘸右,決...
    沈念sama閱讀 38,351評論 3 400
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼岩齿!你這毒婦竟也來了太颤?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 36,988評論 0 259
  • 序言:老撾萬榮一對情侶失蹤纯衍,失蹤者是張志新(化名)和其女友劉穎栋齿,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體襟诸,經(jīng)...
    沈念sama閱讀 43,476評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡瓦堵,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 35,948評論 2 324
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了歌亲。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片菇用。...
    茶點故事閱讀 38,064評論 1 333
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖陷揪,靈堂內的尸體忽然破棺而出惋鸥,到底是詐尸還是另有隱情,我是刑警寧澤悍缠,帶...
    沈念sama閱讀 33,712評論 4 323
  • 正文 年R本政府宣布卦绣,位于F島的核電站,受9級特大地震影響飞蚓,放射性物質發(fā)生泄漏滤港。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 39,261評論 3 307
  • 文/蒙蒙 一趴拧、第九天 我趴在偏房一處隱蔽的房頂上張望溅漾。 院中可真熱鬧山叮,春花似錦、人聲如沸添履。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,264評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽暮胧。三九已至锐借,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間往衷,已是汗流浹背瞎饲。 一陣腳步聲響...
    開封第一講書人閱讀 31,486評論 1 262
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留炼绘,地道東北人嗅战。 一個月前我還...
    沈念sama閱讀 45,511評論 2 354
  • 正文 我出身青樓,卻偏偏與公主長得像俺亮,于是被迫代替她去往敵國和親驮捍。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 42,802評論 2 345

推薦閱讀更多精彩內容