第九章 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ì)越少俭缓。
- 數(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)足夠了乏矾。
- 區(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){
...
}
- 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 }
- 類擴(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 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空指針異常
- 空安全
這是一個(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ì)
- 類默認(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
- 更有效地使用構(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)
- 單例模式
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(){}
}
- 重載必須使用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)主題。