秒懂Kotlin之Java工程師快速掌握Kotlin的技巧
概述
Kotlin 是一種在 Java 虛擬機(jī)上運(yùn)行的靜態(tài)類型編程語言,被稱之為 Android 世界的Swift,由 JetBrains 設(shè)計(jì)開發(fā)并開源。
Kotlin 可以編譯成Java字節(jié)碼,也可以編譯成 JavaScript,方便在沒有 JVM 的設(shè)備上運(yùn)行。
在Google I/O 2017中,Google 宣布 Kotlin 成為 Android 官方開發(fā)語言。
Kotlin/JVM 可以看做是對(duì)改進(jìn)Java的一種積極的嘗試,其試圖改進(jìn)Java編程語言中已知的被廣泛討論的缺點(diǎn)與不足。因?yàn)槲叶嗄昵皬氖逻^C#的開發(fā),初次看到Kotlin,感覺好多特性C#好多年前就有了。足見Java是多么的傳統(tǒng),不愿意過多的引入語法糖。
關(guān)于Kotlin與Java的愛恨情仇此處按下不表,等有機(jī)會(huì)單獨(dú)寫一篇相關(guān)文章。本文僅想從Java慣用者的角度給出Kotlin與Java的一些不同點(diǎn),這往往對(duì)Java慣用者迅速掌握Kotlin是至關(guān)重要的。因?yàn)镵otlin是對(duì)Java的一種改進(jìn),其與Java是100%互操作的,所以Java程序員經(jīng)過短暫的熟悉后往往可以快速入門,進(jìn)而熟練掌握Kotlin。
人類學(xué)習(xí)新事物的一個(gè)很重要的方法就是類比,如果新事物與舊事物相似性非常大,那么類比學(xué)習(xí)的效果會(huì)更加明顯,從Java到Kotlin非常符合這個(gè)特征,以你熟悉的Java來類比不熟悉的Kotlin將會(huì)事半功倍,讓我們開始吧。
本文基于Kotlin 1.4.0 版本
語法差異
Kotlin與Java在語法上存在一些差異,不算特別巨大,下面列出了我認(rèn)為最需要適應(yīng)的幾條:
Kotlin中的方法和屬性可以不包含在類中
我們知道,Java中的一切是以class為基礎(chǔ)的,都要在class中,但是Kotlin卻不是。下面的代碼中,方法、屬性以及類共存于同一級(jí),同一個(gè)文件中都是允許的。
//屬性 var name: String = "ShuSheng007" //方法 fun sum(x: Int, y: Int): Int { return x + y } //類 class Student{}
Kotlin中語句不需要以;結(jié)束
println("hello world")
Kotlin中數(shù)據(jù)類型是后置的
//變量聲明時(shí)類型后置 var name: String = "ShuSheng007" //方法參數(shù)及返回值類型后置 fun sum(x: Int, y: Int): Int { return x + y }
Kotlin方法使用fun關(guān)鍵字定義
fun sum(x: Int, y: Int): Int { return x + y }
Kotlin的類和方法默認(rèn)是public final的
類默認(rèn)不可以被繼承,基類中的方法默認(rèn)不可以被重寫,如果想要被繼承或者重寫需要用open關(guān)鍵字標(biāo)記
//Human類可以被繼承 open class Human{ //eat方法可以被overwrite open fun eat(){ } }
Kotlin中類繼承和接口實(shí)現(xiàn)使用:標(biāo)記
//類繼承 class Man : Human(){ override fun eat() { super.eat() } } //實(shí)現(xiàn)接口 interface action{ fun sleep() } class Woman :action{ override fun sleep() { //... } }
Kotlin中使用var,val聲明變量及屬性,而且可以進(jìn)行類型推斷
在Java中,我們聲明一個(gè)變量必須先指定其類型,例如
String name= "shusheng007";
但是在Kotlin中,編譯器可以根據(jù)賦值自動(dòng)推斷其類型為String
var name = "shusheng007"
Kotlin存在非空與可空類型
這個(gè)也是其宣傳的一大亮點(diǎn),嘗試去解決價(jià)值幾十億美金的問題:NullPointerException
在Kotlin中每個(gè)對(duì)象默認(rèn)都是非null的,除非你顯式的將其聲明為可null
//非空類型 var name1:String="shusheng" //可空類型 var name2:String?=null
Kotlin中package可以與文件路徑不一致
什么意思呢?假設(shè)我們有個(gè)類文件在 src/…/top/ss007/learn 文件路徑中
那么對(duì)于Java 此類的包名必須為
package top.ss007.learn
而對(duì)于Kotlin來說就沒有這個(gè)限制,可以隨便叫,例如叫:就是這么任性
package just.so.wayward
確實(shí)挺任性的,所以這點(diǎn)建議還是遵守Java的規(guī)范更好。
Kotlin中沒有受檢異常(Checked Exception)
在Java中有很多受檢查異常,程序員被強(qiáng)制要求處理它,或者拋給下層調(diào)用者處理。但是Kotlin沒有這個(gè)限制。
Kotlin強(qiáng)調(diào)不可變的概念
Kotlin會(huì)優(yōu)先使用不可變對(duì)象,例如不可變的集合,不可變的變量。這些不可變對(duì)象生成后就只能讀取,而不能修改。至于使用不可變對(duì)象有很多好處,例如其天然的線程安全性等
以上幾條是個(gè)人認(rèn)為的最顛覆我們認(rèn)知的差異點(diǎn),熟悉了上面幾條后Java程序員就基本上可以看得懂kotlin的代碼了
Java中不存在的特性
Kotlin中引入了很多Java中不存在的特性,下面是幾條我認(rèn)為比較重要的
方法類型(Function Type)
在Kotlin中方法是一等公民,意思就是方法就和類一樣,類可以干的事,方法都可以干。方法竟然可以作為類型當(dāng)其他方法的參數(shù)傳遞,當(dāng)其他方法的返回值類型,當(dāng)變量的聲明類型…這個(gè)比較顛覆Java程序員的三觀,Java中是沒有對(duì)應(yīng)的概念的。如果非要找一個(gè)對(duì)應(yīng)物的話,那就是函數(shù)接口了。
下面是一個(gè)方法類型(兩個(gè)int 數(shù)據(jù) 得到一個(gè)int,例如1+2=3)。我們完全可以將其看成是java中的一個(gè)類,所有可以使用類的地方這家伙都適用,也就是說kotlin中,方法可以當(dāng)參數(shù)傳遞了,相當(dāng)于實(shí)現(xiàn)了方法引用,牛逼哄哄的。
(Int, Int) -> Int
例如如下方法,第3個(gè)參數(shù)operation的類型就是上面的方法類型
fun calculate(x: Int, y: Int, operation: (Int, Int) -> Int): Int { return operation(x, y) } //如何調(diào)用 fun sum(x: Int, y: Int): Int { return x + y } //將sum方法當(dāng)參數(shù)傳入了calculate方法中 calculate(4, 5, ::sum) //我們也可以使用Lambda表達(dá)式 calculate(4, 5, { a, b -> a + b }) //當(dāng)Lambda表達(dá)式是方法的最后一個(gè)參數(shù)時(shí),其可以移到()外面 calculate(4, 5) { a, b -> a + b }
那么如何在Java中實(shí)現(xiàn)相同的功能呢?
第一步:按照需求定義一個(gè)函數(shù)接口
下面的接口只有一個(gè)抽象方法,是一個(gè)函數(shù)接口。
@FunctionalInterface public interface Fun2<P1, P2, R> { R invoke(P1 p1, P2 p2); }
第二步:將其作為方法參數(shù)類型
public int javaCalculate(int x, int y, Fun2<Integer, Integer, Integer> operation) { return operation.invoke(x, y); }
經(jīng)過上面兩步就搞定了。
當(dāng)在Kotlin中調(diào)用Java中的方法javaCalculate時(shí),IDE會(huì)提示如下信息
第一條是告訴你第三個(gè)參數(shù)可以使用Kotlin中的函數(shù)類型,代碼如下
javaCalculate(4, 5) { a, b -> a + b }
第二條是比較正統(tǒng)的,告知你需要一個(gè)類型為Fun2的參數(shù),代碼如下
javaCalculate(4, 5, object : Fun2<Int, Int, Int> { override fun invoke(p1: Int, p2: Int): Int { return p1 + p2 } })
在Java中我們使用new一個(gè)匿名類的對(duì)象,而在Kotlin中我們需要使用object關(guān)鍵字
屬性(Property)
var name:String="ShuSheng007"
對(duì)應(yīng)Java類里的私有字段(Field)外加getter和setter方法
private String name; public String getName() { return name; } public void setName(String name) { this.name = name; }
數(shù)據(jù)類 data class
data class User(val name: String, val age: Int)
大體相當(dāng)于Java中的JavaBean,但有細(xì)微差別,這也是Kotlin大勢(shì)宣傳其簡(jiǎn)潔性時(shí)經(jīng)常展示的看家特性
密封類 ( Sealed Classes)
這個(gè)比較牛逼,Java中沒有對(duì)應(yīng)的東東,其主要配合Kotlin中的when使用。最開始見到它的時(shí)候我不是很喜歡它,因?yàn)樗忻芊忸?,卻可以被繼承,后來發(fā)現(xiàn)了它設(shè)計(jì)的精妙性,真的實(shí)質(zhì)性的解決了開發(fā)中常常遇到的困境。
- 其是一種繼承受限的類,例如下面的Human類,其具有如下特點(diǎn)
- 其是abstract類其不可以被直接實(shí)例化
- 其子類必須和其處于同一個(gè)文件中
sealed class Human { abstract fun eat(): Unit open fun program(){ println("I use koltin") } } class Man : Human() { override fun eat() { } override fun equals(other: Any?): Boolean { return this === other } override fun hashCode(): Int { return System.identityHashCode(this) } } class Woman :Human(){ override fun eat() { } override fun program() { super.program() } }
那它有什么好處呢?主要就是用在when表達(dá)式中,和枚舉的功能差不多,但是其可以保護(hù)狀態(tài),枚舉是單例的。
擴(kuò)展方法(Extension functions)與擴(kuò)展屬性(Extension properties)
這個(gè)特性也是kotlin非常重要的特性,有著很廣泛的應(yīng)用。通過擴(kuò)展方法Kotlin有能力不通過繼承而為某個(gè)類增加新的方法和屬性
例如我們想實(shí)現(xiàn)一個(gè)wrapWithSymbol, 用于在字符串的前后加上<>,如何實(shí)現(xiàn)呢?如果是java的話就只能寫一個(gè)Utils方法了,而Kotlin可以使用擴(kuò)展方法來給String類增加一個(gè)新方法。
其實(shí)現(xiàn)方法如下,注意方法前面的那個(gè)要擴(kuò)展的類型,學(xué)名叫receiver
fun String.wrapWithSymbol() :String{ return "<$this>" } //使用 println("my name is ${"shusheng007".wrapWithSymbol()}")
輸出:
my name is <shusheng007>
可見已經(jīng)在目標(biāo)字符串前后加上了<>,從調(diào)用方式來看,wrapWithSymbol方法就像是String類的方法一樣。
擴(kuò)展屬性與擴(kuò)展方法類似:
val <T> List<T>.lastIndex: Int get() = size - 1 //調(diào)用 println(listOf(1,2,3,4).lastIndex)
操作符重載
這個(gè)其實(shí)還是比較復(fù)雜的,也容易被濫用,我認(rèn)為應(yīng)該屬于進(jìn)階知識(shí),此處僅簡(jiǎn)單的簡(jiǎn)紹一下。
Java是不支持操作符重載的,其實(shí)有沒有它也不影響大局。
那什么是操作符重載呢?想要理解這個(gè)問題,首先必須理解什么是操作符?
例如我們編程時(shí)候經(jīng)常用到 a++, a--, a+b, a==b ,a in b等等,其中那些++,--,+,== ,in就是操作符。
那什么是重載呢?這些操作符本身都是有自己的含義的,但是我們通過重載方式可以改變這些操作符的含義,這個(gè)過程就叫操作符重載。
在kotlin中,可以重載的操作符集合及其對(duì)應(yīng)的方法都是預(yù)先定義好的,不能隨便重載。下面列出其中的一組:
Expression | Translated to |
---|---|
a + b | a.plus(b) |
a - b | a.minus(b) |
a * b | a.times(b) |
a / b | a.div(b) |
a % b | a.rem(b), a.mod(b) (deprecated) |
a…b | a.rangeTo(b) |
操作符重載的方法既可以是實(shí)例方法,也可以是擴(kuò)展方法。
我們嘗試使用實(shí)例方法重載一下上表中的+操作符。有兩點(diǎn)需要注意:
- 重載方法必須以operator 標(biāo)記
- 重載方法名稱和參數(shù)必須是Kotlin預(yù)先定義好的,例如此處的 plus
data class Point(val x: Int, val y: Int){ operator fun plus(p: Point): Point { return Point(p.x + x,p.y + y) } } //調(diào)用 val p1 = Point(1, 2) val p2 = Point(3, 4) println(p1 + p2)
輸出:
Point(x=4, y=6)
可見,通過重載+操作符,我們改變了其內(nèi)在的含義,使得+也能用于對(duì)象了。
協(xié)程
較復(fù)雜,需要單獨(dú)文章描述
到此這篇關(guān)于秒懂Kotlin之Java工程師快速掌握Kotlin的技巧的文章就介紹到這了,更多相關(guān)Kotlin開始入門內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Java 采用反射獲取class屬性值的實(shí)現(xiàn)代碼
以下是對(duì)在Java中采用反射獲取class屬性值的實(shí)現(xiàn)代碼進(jìn)行了分析介紹,需要的朋友可以過來參考下2013-08-08一個(gè)處理用戶登陸的servlet簡(jiǎn)單實(shí)例
這篇文章主要介紹了一個(gè)處理用戶登陸的servlet簡(jiǎn)單實(shí)例,可通過servlet實(shí)現(xiàn)處理用戶登錄的功能,具有一定參考借鑒價(jià)值,需要的朋友可以參考下2015-01-01java中Base64字符串出現(xiàn)不合法字符的問題解決
非法的base64數(shù)據(jù)可能導(dǎo)致編碼或解碼過程出錯(cuò),本文主要介紹了java中Base64字符串出現(xiàn)不合法字符的問題解決,具有一定的參考價(jià)值,感興趣的可以了解一下2024-06-06SpringBoot關(guān)閉druid的頁面和添加密碼驗(yàn)證方式
這篇文章主要介紹了SpringBoot關(guān)閉druid的頁面和添加密碼驗(yàn)證方式,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2024-05-05Springboot使用Redis中ZSetOperations實(shí)現(xiàn)博客訪問量
在日常的網(wǎng)站使用中,經(jīng)常會(huì)碰到頁面的訪問量,本文主要介紹了Springboot使用Redis中ZSetOperations實(shí)現(xiàn)博客訪問量,具有一定的參考價(jià)值,感興趣的可以了解一下2024-01-01尋找二叉樹最遠(yuǎn)的葉子結(jié)點(diǎn)(實(shí)例講解)
下面小編就為大家分享一篇尋找二叉樹最遠(yuǎn)的葉子結(jié)點(diǎn)的實(shí)例講解,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過來看看吧2017-12-12