《Kotlin 程序設(shè)計(jì)》第九章 Kotlin與Java混合調(diào)用

第九章 Kotlin與Java混合調(diào)用

雖然 Kotlin 的開發(fā)很方便娃闲,但當(dāng)你與他人協(xié)作時(shí)添祸,總會(huì)碰到 Java 與 Kotlin 代碼共存的代碼項(xiàng)目著隆。本章就教你如何優(yōu)雅的實(shí)現(xiàn) Kotlin 與 Java 混合編程具壮。

1 使用工具互相轉(zhuǎn)換

1.1 將 Java 轉(zhuǎn)換為 Kotlin

如果你之前使用 Java 語(yǔ)言而沒(méi)有 Kotlin 開發(fā)經(jīng)驗(yàn),不用擔(dān)心镣屹,Intellij IDEA 會(huì)幫你一鍵轉(zhuǎn)換,將 Java 代碼轉(zhuǎn)換成 Kotlin 代碼价涝。

1.2 將 Kotlin 轉(zhuǎn)換為 Java

另外女蜈,通過(guò)IDEA的Kotlin插件,可以直接把Kotlin代碼ByteCode反編譯成Java代碼(雖然這個(gè)反編譯后的Java代碼不是那么的的原汁原味)色瘩。

2 反射獲取類的 Class

在 Java 或 Android 開發(fā)中伪窖,經(jīng)常會(huì)直接調(diào)用一個(gè)類的 Class 文件。我們?cè)贘ava中是直接這么調(diào)用的 Xyz.class居兆。

這樣的方式覆山,在Kotlin中稍微有點(diǎn)不同。在 M13 之前泥栖, Kotlin 代碼使用JavaClass<Xyz>簇宽,而 M13 之后寫法為 Xyz::class.java 勋篓。

3 在 Kotlin 中調(diào)用 Java 代碼

3.1 void 與 Unit

如果一個(gè) Java 方法返回 void,對(duì)應(yīng)的在 Kotlin 代碼中它將返回 Unit魏割。在 Kotlin 中可以省略這個(gè)Unit返回類型譬嚣。

3.2 Java 與 Kotlin 關(guān)鍵字沖突的處理

Java 有 static 關(guān)鍵字,在 Kotlin 中沒(méi)有這個(gè)關(guān)鍵字钞它,你需要使用@JvmStatic
替代這個(gè)關(guān)鍵字拜银。

同樣,在 Kotlin 中也有很多的關(guān)鍵字是 Java 中是沒(méi)有的须揣。例如

in
is
data

等盐股。

如果 Java 中使用了這些關(guān)鍵字,需要加上反引號(hào)轉(zhuǎn)義來(lái)避免沖突耻卡。例如

// Java 代碼中有個(gè)方法叫 is()
public void is(){
 //...
}
// 轉(zhuǎn)換為 Kotlin 代碼需要加反引號(hào)轉(zhuǎn)義
fun `is`() {
 //...
}

4 在 Java 中調(diào)用 Kotlin 代碼

4.1 static 方法

上文已經(jīng)提到過(guò)疯汁,在 Kotlin 中沒(méi)有 static
關(guān)鍵字,那么如果在 Java 代碼中想要通過(guò)類名調(diào)用一個(gè) Kotlin 類的方法,你需要給這個(gè)方法加入@JvmStatic注解卵酪。

另外幌蚊,你也可以通過(guò)對(duì)象object調(diào)用這個(gè)方法。

代碼示例

StringUtils.isEmpty("hello");  
StringUtils.INSTANCE.isEmpty2("hello");

object StringUtils {
    @JvmStatic fun isEmpty(str: String): Boolean {
        return "" == str
    }

    fun isEmpty2(str: String): Boolean {
        return "" == str
    }
}

4.2 靜態(tài)方法與伴生對(duì)象companion object

class StringUtils {
    companion object {
       fun isEmpty(str: String): Boolean {
            return "" == str
        }
    }
}

companion object溃卡, 表示外部類的一個(gè)伴生對(duì)象溢豆,你可以把他理解為外部類自動(dòng)創(chuàng)建了一個(gè)對(duì)象作為自己的field。與上面的類似瘸羡,Java 在調(diào)用時(shí)漩仙,可以這樣寫:

StringUtils.Companion.isEmpty();

4.3 包級(jí)別函數(shù)

與 Java 不同,Kotlin 允許函數(shù)獨(dú)立存在犹赖,而不必依賴于某個(gè)類队他,這類函數(shù)我們稱之為包級(jí)別函數(shù)(Package-Level Functions)。

為了兼容 Java峻村,Kotlin 默認(rèn)會(huì)將所有的包級(jí)別函數(shù)放在當(dāng)前kt源文件的類中麸折。比如說(shuō),HelloWorld.kt中的包級(jí)別的函數(shù)粘昨,默認(rèn)會(huì)放到HelloWorldKt類中垢啼。

//@file:JvmName("MyExample")
package com.easy.kotlin

/**
 * Created by jack on 2017/5/29.
 */

import java.util.Date
import java.text.SimpleDateFormat

fun main(args: Array<String>) {
    println("Hello, world!")
    println(SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(Date()))
    println(helloKotlin())
}


fun helloKotlin():String {
    val words = mutableListOf<String>()
    words.add("Hello")
    words.add("Kotlin!")
    words.add(java.util.Date().toString())
    return words.joinToString(separator=" ")
}

反編譯看下Kotlin ByteCode:

// ================com/easy/kotlin/HelloWorldKt.class =================
// class version 50.0 (50)
// access flags 0x31
public final class com/easy/kotlin/HelloWorldKt {


  // access flags 0x19
  public final static main([Ljava/lang/String;)V
    @Lorg/jetbrains/annotations/NotNull;() // invisible, parameter 0
    LDC "Hello, world!"
    INVOKESTATIC kotlin/io/ConsoleKt.println (Ljava/lang/Object;)V
    NEW java/text/SimpleDateFormat
    DUP
    LDC "yyyy-MM-dd HH:mm:ss"
    INVOKESPECIAL java/text/SimpleDateFormat.<init> (Ljava/lang/String;)V
    NEW java/util/Date
    DUP
    INVOKESPECIAL java/util/Date.<init> ()V
    INVOKEVIRTUAL java/text/SimpleDateFormat.format (Ljava/util/Date;)Ljava/lang/String;
    INVOKESTATIC kotlin/io/ConsoleKt.println (Ljava/lang/Object;)V
    INVOKESTATIC com/easy/kotlin/HelloWorldKt.helloKotlin ()Ljava/lang/String;
    INVOKESTATIC kotlin/io/ConsoleKt.println (Ljava/lang/Object;)V
    RETURN
   L0
    MAXSTACK = 3
    MAXLOCALS = 1

  // access flags 0x19
  public final static helloKotlin()Ljava/lang/String;
  @Lorg/jetbrains/annotations/NotNull;() // invisible
    INVOKESTATIC kotlin/collections/CollectionsKt.mutableListOf ()Ljava/util/List;
    ASTORE 0
   L0
    ALOAD 0
    LDC "Hello"
    INVOKEINTERFACE java/util/List.add (Ljava/lang/Object;)Z
    POP
    ALOAD 0
    LDC "Kotlin!"
    INVOKEINTERFACE java/util/List.add (Ljava/lang/Object;)Z
    POP
    ALOAD 0
    NEW java/util/Date
    DUP
    INVOKESPECIAL java/util/Date.<init> ()V
    INVOKEVIRTUAL java/util/Date.toString ()Ljava/lang/String;
    INVOKEINTERFACE java/util/List.add (Ljava/lang/Object;)Z
    POP
    ALOAD 0
    CHECKCAST java/lang/Iterable
    LDC " "
    CHECKCAST java/lang/CharSequence
    ACONST_NULL
    ACONST_NULL
    ICONST_0
    ACONST_NULL
    ACONST_NULL
    BIPUSH 62
    ACONST_NULL
    INVOKESTATIC kotlin/collections/CollectionsKt.joinToString$default (Ljava/lang/Iterable;Ljava/lang/CharSequence;Ljava/lang/CharSequence;Ljava/lang/CharSequence;ILjava/lang/CharSequence;Lkotlin/jvm/functions/Function1;ILjava/lang/Object;)Ljava/lang/String;
    ARETURN
   L1
   L2
    LOCALVARIABLE words Ljava/util/List; L0 L2 0
    MAXSTACK = 9
    MAXLOCALS = 1

  // access flags 0x19
  public final static <clinit>()V
    RETURN
   L0
    MAXSTACK = 0
    MAXLOCALS = 0
}



我們可以看出,Kotlin中的包級(jí)別函數(shù)

fun helloKotlin()

被編譯成成了public final static 方法:

public final static helloKotlin()

在 Java 中想要調(diào)用包級(jí)別函數(shù)時(shí)张肾,需要通過(guò)這個(gè)public final class com/easy/kotlin/HelloWorldKt類來(lái)調(diào)用芭析。

我們也可以通過(guò)注解@file:JvmName("MyExample")來(lái)自定義這個(gè)類名。這樣當(dāng)前文件中的所有包級(jí)別函數(shù), 將被放到一個(gè)自動(dòng)生成的文件名為 MyExample 的類中吞瞪。

代碼示例如下:

@file:JvmName("MyExample")
package com.easy.kotlin

/**
 * Created by jack on 2017/5/29.
 */

import java.util.Date
import java.text.SimpleDateFormat

fun main(args: Array<String>) {
    println("Hello, world!")
    println(SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(Date()))
    println(helloKotlin())
}


fun helloKotlin():String {
    val words = mutableListOf<String>()
    words.add("Hello")
    words.add("Kotlin!")
    words.add(java.util.Date().toString())
    return words.joinToString(separator=" ")
}

添加了如下代碼

@file:JvmName("MyExample")
package com.easy.kotlin

反編譯后:

// ================com/easy/kotlin/MyExample.class =================
// class version 50.0 (50)
// access flags 0x31
public final class com/easy/kotlin/MyExample {


  // access flags 0x19
  public final static main([Ljava/lang/String;)V
    ....

  // access flags 0x19
  public final static helloKotlin()Ljava/lang/String;
  ....
}



我們可以看到:類名變成了MyExample馁启。

簡(jiǎn)潔,使用更少的代碼做更多的事
在我看來(lái)尸饺,Kotlin很關(guān)鍵的一個(gè)優(yōu)點(diǎn)就是簡(jiǎn)潔进统。相對(duì)于Java助币,使用Kotlin往往能夠用更少的代碼獲得更多的功能。這有什么好處呢螟碎?很簡(jiǎn)單眉菱,寫的代碼越少,邏輯越清晰掉分,所犯的錯(cuò)誤就會(huì)越少俭缓。

  1. 數(shù)據(jù)類
    數(shù)據(jù)類大量重復(fù)的getter和setter相信會(huì)是很多人在開發(fā)過(guò)程中吐槽的一個(gè)點(diǎn)。舉一個(gè)很經(jīng)典的例子酥郭,我們需要一個(gè)Person的數(shù)據(jù)類华坦。

在Java中,需要這么寫:

public class Person {
private String name;
private int age;
private int sex;
private float height;

public Person(String name, int age, int sex, float height) {
    this.name = name;
    this.sex = sex;
    this.age = age;
    this.height = height;
}

public String getName() {
    return name;
}

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

public int getAge() {
    return age;
}

public void setAge(int age) {
    this.age = age;
}

public int getSex() {
     return sex;
}

public void setSex(int sex) {
    this.sex = sex;
}

public float getHeight() {
    return height;
}

public void setHeight(float height) {
    this.height = height;
}

@Override
public String toString() {
    return "Person{" +
            "name='" + name + '\'' +
            ", age=" + age +
            ", sex=" + (sex == 0 ? "男" : "女") +
            ", height=" + height +
            '}';

}
在Kotlin里不从,我們只需要一行代碼就能完成以上的功能:

data class Person(var name: String,
var age: Int,
var sex: Int,
var height: Float)
Kotlin提供的數(shù)據(jù)類會(huì)讓你自動(dòng)獲得所需的getter惜姐、setters、toString()椿息,這很大程度上減少了大量重復(fù)的工作歹袁。當(dāng)然,我們也可以很輕松的去覆蓋這些函數(shù)寝优,做自定義的事情条舔,但是在大多數(shù)情況下,只需聲明類和屬性就已經(jīng)足夠了乏矾。

  1. 區(qū)間表達(dá)式
    在Java中我們經(jīng)常要寫這樣的代碼孟抗,

for(int i = 0; i <= 10; i++){
System.out.println(i)
}
但是在Kotlin中,支持 .. 操作符形式的區(qū)間表達(dá)式钻心,我們轉(zhuǎn)換成Kotlin就變成了這樣:

for(i in 0..10){
println(i)
}
是不是簡(jiǎn)潔優(yōu)雅很多凄硼,不僅如此,還有更多相關(guān)的功能扔役。

//倒序迭代
for(i in 10 downTo 0){
...
}

//步長(zhǎng)為2的迭代
for(i in 0..10 setp 2){
...
}

//i在[0,10)區(qū)間帆喇,排除了10
for(i in 0 until 10){
...
}

  1. Lamda表達(dá)式
    Java在Java8才支持Lamda語(yǔ)法警医,在Kotlin里完全支持亿胸。有對(duì)比才有差距,看例子:

Java中:

btn.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
//dosomething
}});
Kotlin中:

btn.setOnClickListener { //dosomething }

  1. 類擴(kuò)展
    類擴(kuò)展是一個(gè)超級(jí)強(qiáng)大的功能预皇,我終于可以擺脫大量的Util工具類了侈玄。:)

看一個(gè)例子:

fun MutableList<Int>.swap(index1: Int, index2: Int) {
val tmp = this[index1] // 'this' 對(duì)應(yīng)該列表
this[index1] = this[index2]
this[index2] = tmp
}
}

//使用
val l = mutableListOf(1, 2, 3)
l.swap(0, 2) // 'swap()' 內(nèi)部的 'this' 得到 'l' 的值
是不是功能強(qiáng)大且代碼簡(jiǎn)潔優(yōu)雅?當(dāng)然吟温,擴(kuò)展并不能真正的修改它所擴(kuò)展的類序仙。通過(guò)定義一個(gè)擴(kuò)展,我們并沒(méi)有在一個(gè)類中插入新的方法鲁豪,僅僅是可以通過(guò)該類型的變量用點(diǎn)表達(dá)式來(lái)調(diào)用這個(gè)新函數(shù)潘悼。 口說(shuō)無(wú)憑律秃,我們來(lái)看看Kotlin編譯后的字節(jié)碼:

定義:

public final class ExtensionKt {
// access flags 0x19
// signature (Ljava/util/List<Ljava/lang/Integer;>;II)V
// declaration: void swap(java.util.List<java.lang.Integer>, int, int)
public final static swap(Ljava/util/List;II)V
@Lorg/jetbrains/annotations/NotNull;() // invisible, parameter 0
L0
ALOAD 0
LDC "receiver" INVOKESTATIC kotlin/jvm/internal/Intrinsics.checkParameterIsNotNull (Ljava/lang/Object;Ljava/lang/String;)V L1 LINENUMBER 6 L1 ALOAD 0 ILOAD 1 INVOKEINTERFACE java/util/List.get (I)Ljava/lang/Object; CHECKCAST java/lang/Number INVOKEVIRTUAL java/lang/Number.intValue ()I ISTORE 3 L2 LINENUMBER 7 L2 ALOAD 0 ILOAD 1 ALOAD 0 ILOAD 2 INVOKEINTERFACE java/util/List.get (I)Ljava/lang/Object; INVOKEINTERFACE java/util/List.set (ILjava/lang/Object;)Ljava/lang/Object; POP L3 LINENUMBER 8 L3 ALOAD 0 ILOAD 2 ILOAD 3 INVOKESTATIC java/lang/Integer.valueOf (I)Ljava/lang/Integer; INVOKEINTERFACE java/util/List.set (ILjava/lang/Object;)Ljava/lang/Object; POP L4 LINENUMBER 9 L4 RETURN L5 LOCALVARIABLE tmp I L2 L5 3 LOCALVARIABLEreceiver Ljava/util/List; L0 L5 0
LOCALVARIABLE index1 I L0 L5 1
LOCALVARIABLE index2 I L0 L5 2
MAXSTACK = 4
MAXLOCALS = 4

// compiled from: extension.kt
}

使用:

L1
LINENUMBER 11 L1
ALOAD 1
ICONST_0
ICONST_2
INVOKESTATIC ExtensionKt.swap (Ljava/util/List;II)V
L2
可以看出,Kotlin在編譯時(shí)會(huì)以所在文件名extension創(chuàng)建靜態(tài)類ExtensionKt治唤,并將定義的擴(kuò)展方法swap編譯成靜態(tài)方法供外部調(diào)用棒动。

在Java中形式大致是這樣:

public final class ExtensionKt {
public static final void swap(@NotNull List list, int index1, int index2) {
int tmp = ((Number)list.get(index1)).intValue();
list.set(index1, receiver.get(index2));
list.set(index2, Integer.valueOf(tmp));
}
}
所以在底層,其實(shí)就是我們平時(shí)所寫的Util工具類宾添,但是Kotlin默認(rèn)幫我們實(shí)現(xiàn)了船惨,我們只需更簡(jiǎn)潔的編寫調(diào)用就好了。

安全缕陕,避免NPE空指針異常

  1. 空安全
    這是一個(gè)讓人又愛又恨的特性粱锐。一直以來(lái),NullPointException空指針異常在開發(fā)中是最低級(jí)也最致命的問(wèn)題扛邑。我們往往需要進(jìn)行各種null的判斷以試圖去避免NPE的發(fā)生怜浅。Kotlin基于這個(gè)問(wèn)題,提出了一個(gè)空安全的概念蔬崩,即每個(gè)屬性默認(rèn)不可為null海雪。

舉個(gè)例子。

var a: String = "abcd"
a = null //編譯錯(cuò)誤
如果要允許為空舱殿,我們需要手動(dòng)聲明一個(gè)變量為可空字符串類型奥裸,寫為String?

var a: String? = "abcd"
a = null //編譯成功
那怎么實(shí)現(xiàn)默認(rèn)不可為Null呢? 我們來(lái)看一下字節(jié)碼:

@Lorg/jetbrains/annotations/NotNull;() // invisible
L0
LINENUMBER 10 L0
GETSTATIC PersonKt.a : Ljava/lang/String;
ARETURN
L1

//init
static <clinit>()V
L0
LINENUMBER 10 L0
LDC "abcd"
PUTSTATIC PersonKt.a : Ljava/lang/String;
RETURN
MAXSTACK = 1
MAXLOCALS = 0
可以看到Kotlin對(duì)變量進(jìn)行了NotNull注解沪袭。翻譯成Java代碼:

@NotNull
String a = "abcd"
不僅如此湾宙,為了避免NPE異常,Kotlin做了一件很有趣的事:當(dāng)你允許屬性可空時(shí)冈绊,Kotlin編譯器將不允許你在未經(jīng)檢查的情況下引用它侠鳄。

var person: Person? = null
person.name = "shinelw" //編譯失敗
person?.name = "shinelw" //編譯成功
如上面的代碼所示,當(dāng)person對(duì)象可為null時(shí)死宣,必須強(qiáng)制使用 ?. 來(lái)進(jìn)行null檢查伟恶。看看 ?. 在字節(jié)碼里的樣子毅该,

LINENUMBER 13 L1
ALOAD 0
DUP
IFNULL L2
LDC "shinelw"
INVOKEVIRTUAL Person.setName (Ljava/lang/String;)V
GOTO L3

L2
POP
L3
可見博秫,在person獲取name屬性的時(shí)候進(jìn)行了null的判斷,翻譯成java代碼:

Person person = (Person)null;
if(person != null) {
person.setName("shinelw");
}
這么看來(lái)眶掌,空安全特性的確帶來(lái)了巨大的好處挡育,極大程度上避免了空指針異常的出現(xiàn)。然而朴爬,世上沒(méi)有十全十美的東西即寒。空安全在開發(fā)過(guò)程中給我?guī)?lái)了很多幸福的煩惱。舉個(gè)例子母赵,以前用Java是這樣的:

public class A {
String a;
String b;
String c;
}
現(xiàn)在呢逸爵,Kotlin中是這樣的:

class A {
var a: String? = null
var b: String? = null
var c: String? = null
}
看出區(qū)別了嗎?

在Kotlin中我們需要在定義變量是就必須給出初始值凹嘲。開發(fā)過(guò)程中痊银,很多情況下變量在定義時(shí)尚不知道要賦何值的,Kotlin強(qiáng)制初始化賦值讓整個(gè)代碼看起來(lái)很“怪異”施绎。對(duì)我來(lái)說(shuō)溯革,如果一個(gè)變量可為null時(shí),它應(yīng)該是隱含地就默認(rèn)給予了null值谷醉。

我希望應(yīng)該是這樣的致稀,

class A {
var a: String? //默認(rèn)值為null
var b: String? //默認(rèn)值為null
var c: String? //默認(rèn)值為null
}
雖然說(shuō)Kotlin提供了lateinit類型懶加載的方式進(jìn)行初始化,但是也并不能很好的支持全部情況俱尼,它只能用于var的屬性抖单,并且只能在屬性沒(méi)有自定義getter或者setter時(shí)候使用。屬性的類型必須是非空值遇八,并且它不能使原始類型矛绘。

當(dāng)然,我們換個(gè)角度刃永,從語(yǔ)言設(shè)計(jì)的角度來(lái)說(shuō)货矮,Kotlin這么設(shè)計(jì)又是很合理的。所有屬性要求強(qiáng)制顯式的初始化能夠更容易的推理代碼斯够,明確每個(gè)屬性在何時(shí)何地初始化囚玫。

總的來(lái)說(shuō),空安全機(jī)制所做的事情就是读规,讓我們?cè)驹谶壿嫶a中進(jìn)行大量判空的工作轉(zhuǎn)移到了初始化上抓督,并很大程度地減輕了工作量。

更優(yōu)雅束亏,遵循Effective Java設(shè)計(jì)

  1. 類默認(rèn)不可繼承
    《Effective Java》提出一種觀點(diǎn):組合優(yōu)于繼承铃在,避免濫用繼承。要么為繼承而設(shè)計(jì)碍遍,并提供文檔說(shuō)明定铜,要么就禁止繼承。至于為什么這么說(shuō)呢雀久,我見過(guò)一句話很形象:攤開來(lái)的代碼宿稀,比疊起來(lái)的代碼趁舀,更加一目了然赖捌。詳細(xì)可以自行閱讀《Effective Java》。

Kotlin默認(rèn)類是final類型的,即每個(gè)類默認(rèn)不可繼承越庇。只有你真的需要繼承的時(shí)候罩锐,再通過(guò)open聲明使用,聲明方式如下:

open class A

  1. 更有效地使用構(gòu)建器模式
    我們建議使用構(gòu)建器模式卤唉,當(dāng)Java的構(gòu)造器存在多個(gè)可選的參數(shù)時(shí)涩惑,情況就會(huì)變得很復(fù)雜,代碼冗長(zhǎng)桑驱,也更容易出錯(cuò)竭恬。Kotlin提供了一種更有效的構(gòu)造器方式,通過(guò)默認(rèn)參數(shù)的功能實(shí)現(xiàn)熬的。

data class Person(var name: String,
var age: Int = 18,
var sex: Int = 0,
var height: Float = 1.8f
var weight: Float = 60f)

//創(chuàng)建對(duì)象
val person: Person = Person(name="shinelw",
age = 10,
height = 1.7f)

  1. 單例模式
    Kotlin默認(rèn)提供了單例模式的模板痊硕,通過(guò)object關(guān)鍵字即可實(shí)現(xiàn)。

object Singleton {
//各種函數(shù)
fun a(){...}
...
}

//使用時(shí)
Singleton.a()
完全不需要手動(dòng)構(gòu)建押框,看上去很好岔绸。但是我有一點(diǎn)質(zhì)疑,它是不是線程安全的橡伞,是不是懶加載的盒揉。隨后通過(guò)Kotlin編譯器得到字節(jié)碼,然后再反編譯回Java代碼兑徘。是長(zhǎng)這樣子的:

public class Singleton {
public static final Singleton INSTANCE;

public final void a() {
}

private Singleton() {
INSTANCE = (Singleton)this;
}

static {
new Singleton();
}
}
糟糕的是刚盈,從上面代碼可以看出,Kotlin的object只是一個(gè)最簡(jiǎn)單的餓漢式的單例模式挂脑。在第一次加載類到內(nèi)存的時(shí)候就會(huì)初始化扁掸,雖然它是線程安全的,但是不完美最域,對(duì)嗎谴分?

如果你是一個(gè)追求完美的人,下面是類似于靜態(tài)內(nèi)部類方式實(shí)現(xiàn)的單例模式镀脂,懶加載且線程安全牺蹄。缺點(diǎn)是跟Java一樣,需要手動(dòng)構(gòu)建薄翅。:)

class Singleton private constructor(){
companion object {
fun getInstance(): Singleton {
return SingletonHolder.instance
}
}
private object SingletonHolder {
val instance: Singleton = Singleton()
}

//各種函數(shù)..
fun a(){}

}

  1. 重載必須使用override
    Java中對(duì)于重載的注解@Override不是強(qiáng)制的沙兰,一旦項(xiàng)目代碼很復(fù)雜,這將是一場(chǎng)災(zāi)難翘魄。當(dāng)你分不清哪些是重載方法時(shí)鼎天,對(duì)方法進(jìn)行參數(shù)修改是災(zāi)難性的。Kotlin基于這點(diǎn)暑竟,要求重載方法時(shí)必須加上override關(guān)鍵字斋射。如果沒(méi)寫,編譯器將會(huì)報(bào)錯(cuò),強(qiáng)制你加上罗岖。

override fun a(){...}
完全兼容涧至,與Java互操作
這是Kotlin與Scala相比,優(yōu)勢(shì)突出的一點(diǎn)桑包。我們可以在Kotlin中調(diào)用現(xiàn)存的Java代碼南蓬,并且也能在Java代碼中順利的調(diào)用Kotlin代碼。這意味著我們可以馬上在現(xiàn)有的Java項(xiàng)目中使用上Kotlin哑了,同時(shí)所有之前舊的Java也一樣有效赘方。

這是很關(guān)鍵,也是我之所以很看好Kotlin的一個(gè)原因弱左。

至于怎么相互調(diào)用操作蒜焊,請(qǐng)大家看官方文檔關(guān)于Java互操作的部分。

這里只說(shuō)一個(gè)方面科贬,關(guān)于空安全方面泳梆。

因?yàn)镴ava中的任何應(yīng)用都可以為null,但是在Kotlin中是默認(rèn)不可為null的榜掌,這使得Kotlin對(duì)來(lái)自Java的對(duì)象要求嚴(yán)格空安全是不現(xiàn)實(shí)的优妙。Java聲明的類型在Kotlin中會(huì)被特別對(duì)待,稱之為平臺(tái)類型憎账。對(duì)這種類型的空檢查會(huì)放寬套硼,因此他們的安全保證與Java中是相同的。

看下面的例子:

public class Person{
public String getName(){
return null;
}
}
val person = Person()
val name = person.name // 編譯通過(guò) 運(yùn)行值為null
name本是非null變量胞皱,因?yàn)檎{(diào)用Java對(duì)象所以變成平臺(tái)類型邪意,放寬了類型空檢查。

當(dāng)然反砌,如果你想要延續(xù)Kotlin嚴(yán)格空安全機(jī)制的話雾鬼,可是有辦法滴。我們需要在編寫Java代碼時(shí)加上@NotNull注解宴树,這個(gè)很熟悉吧策菜,在介紹空安全機(jī)制的時(shí)候說(shuō)過(guò)Kotlin在實(shí)現(xiàn)默認(rèn)非null屬性就是這么實(shí)現(xiàn)的。然后代碼就變成了這樣酒贬,

public class Person{
public @NotNull String getName(){
return null;
}
}
然后在運(yùn)行的時(shí)候就會(huì)報(bào)以下的錯(cuò)誤:

Exception in thread "main" java.lang.IllegalStateException: @NotNull method Person.getName must not return null
at Person.$$$reportNull$$$0(Person.java)
at Person.getName(Person.java:9)
at MainKt.main(main.kt:7)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:498)
at com.intellij.rt.execution.application.AppMain.main(AppMain.java:147)
總結(jié)
總而言之又憨,經(jīng)過(guò)短短一個(gè)月的Kotlin使用,在實(shí)際項(xiàng)目中開發(fā)展現(xiàn)出的特性應(yīng)用是讓我感到興奮的锭吨。作為一名開發(fā)者蠢莺,在我眼里,Kotlin設(shè)計(jì)出來(lái)不是拋開Java談的零如,而是在Java的毛病的基礎(chǔ)上躏将,進(jìn)行的再開發(fā)锄弱,擁有很多其他語(yǔ)言優(yōu)秀的特性,同時(shí)完全兼容Java耸携。畢竟棵癣,對(duì)于一家大企業(yè)來(lái)講辕翰,為了一門新語(yǔ)言完全摒棄一個(gè)很成熟的項(xiàng)目進(jìn)行再開發(fā)是不現(xiàn)實(shí)的夺衍。相反的是,對(duì)于項(xiàng)目中Java難于處理的邏輯喜命,Kotlin的優(yōu)勢(shì)一覽無(wú)余沟沙,相輔相成,Kotlin和Java配合使用時(shí)目前最完美的方案壁榕。

但不可否認(rèn)的是矛紫,Kotlin真的讓人感到潛力十足,值得大家去試一試牌里。

參考資料:

1.http://kotlinlang.org/docs/reference/java-interop.html


Kotlin 開發(fā)者社區(qū)

國(guó)內(nèi)第一Kotlin 開發(fā)者社區(qū)公眾號(hào)颊咬,主要分享、交流 Kotlin 編程語(yǔ)言牡辽、Spring Boot喳篇、Android、React.js/Node.js态辛、函數(shù)式編程麸澜、編程思想等相關(guān)主題。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末奏黑,一起剝皮案震驚了整個(gè)濱河市炊邦,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌熟史,老刑警劉巖馁害,帶你破解...
    沈念sama閱讀 206,214評(píng)論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異蹂匹,居然都是意外死亡蜗细,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,307評(píng)論 2 382
  • 文/潘曉璐 我一進(jìn)店門怒详,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)炉媒,“玉大人,你說(shuō)我怎么就攤上這事昆烁〉踔瑁” “怎么了?”我有些...
    開封第一講書人閱讀 152,543評(píng)論 0 341
  • 文/不壞的土叔 我叫張陵静尼,是天一觀的道長(zhǎng)白粉。 經(jīng)常有香客問(wèn)我传泊,道長(zhǎng),這世上最難降的妖魔是什么鸭巴? 我笑而不...
    開封第一講書人閱讀 55,221評(píng)論 1 279
  • 正文 為了忘掉前任眷细,我火速辦了婚禮,結(jié)果婚禮上鹃祖,老公的妹妹穿的比我還像新娘溪椎。我一直安慰自己,他們只是感情好恬口,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,224評(píng)論 5 371
  • 文/花漫 我一把揭開白布校读。 她就那樣靜靜地躺著,像睡著了一般祖能。 火紅的嫁衣襯著肌膚如雪歉秫。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,007評(píng)論 1 284
  • 那天养铸,我揣著相機(jī)與錄音雁芙,去河邊找鬼。 笑死钞螟,一個(gè)胖子當(dāng)著我的面吹牛兔甘,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播筛圆,決...
    沈念sama閱讀 38,313評(píng)論 3 399
  • 文/蒼蘭香墨 我猛地睜開眼裂明,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了太援?” 一聲冷哼從身側(cè)響起闽晦,我...
    開封第一講書人閱讀 36,956評(píng)論 0 259
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎提岔,沒(méi)想到半個(gè)月后仙蛉,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 43,441評(píng)論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡碱蒙,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 35,925評(píng)論 2 323
  • 正文 我和宋清朗相戀三年荠瘪,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片赛惩。...
    茶點(diǎn)故事閱讀 38,018評(píng)論 1 333
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡哀墓,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出喷兼,到底是詐尸還是另有隱情篮绰,我是刑警寧澤,帶...
    沈念sama閱讀 33,685評(píng)論 4 322
  • 正文 年R本政府宣布季惯,位于F島的核電站吠各,受9級(jí)特大地震影響臀突,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜贾漏,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,234評(píng)論 3 307
  • 文/蒙蒙 一候学、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧纵散,春花似錦梳码、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,240評(píng)論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)翎承。三九已至硕盹,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間叨咖,已是汗流浹背瘩例。 一陣腳步聲響...
    開封第一講書人閱讀 31,464評(píng)論 1 261
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留甸各,地道東北人垛贤。 一個(gè)月前我還...
    沈念sama閱讀 45,467評(píng)論 2 352
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像趣倾,于是被迫代替她去往敵國(guó)和親聘惦。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,762評(píng)論 2 345

推薦閱讀更多精彩內(nèi)容

  • 前言 人生苦多儒恋,快來(lái) Kotlin 善绎,快速學(xué)習(xí)Kotlin! 什么是Kotlin诫尽? Kotlin 是種靜態(tài)類型編程...
    任半生囂狂閱讀 26,145評(píng)論 9 118
  • Spring Cloud為開發(fā)人員提供了快速構(gòu)建分布式系統(tǒng)中一些常見模式的工具(例如配置管理禀酱,服務(wù)發(fā)現(xiàn),斷路器牧嫉,智...
    卡卡羅2017閱讀 134,599評(píng)論 18 139
  • 1. Java基礎(chǔ)部分 基礎(chǔ)部分的順序:基本語(yǔ)法剂跟,類相關(guān)的語(yǔ)法,內(nèi)部類的語(yǔ)法酣藻,繼承相關(guān)的語(yǔ)法曹洽,異常的語(yǔ)法,線程的語(yǔ)...
    子非魚_t_閱讀 31,581評(píng)論 18 399
  • Kotlin is 100% interoperable with Java? and Android? 在前面的...
    JackChen1024閱讀 6,383評(píng)論 1 15
  • 秋窗枯蝶非戀花辽剧,路間閑人無(wú)顧暇送淆。白露凝霜節(jié)節(jié)寒,枝上紅柿謝春華抖仅。
    剪卻西風(fēng)不問(wèn)愁閱讀 155評(píng)論 0 0