kotlin相較於java的新特性
本文主要看一下kotlin相較於java有哪些新的feature
大多數控制結構都是表示式
比如: if/when/try 等等在kotlin中是表示式,而(if)在java中是語句。
語句和表示式的區別在於,表示式有值,並且能作為另一個表示式的一部分使用;而語句總是包圍著它的程式碼塊中的頂層元素,並且沒有自己的值。
目錄和包
在java中,要求把類放到和包結構相匹配的檔案與目錄結構中。而在kotlin中定義一個包的package可以與當前目錄不同,並且可以把多個類放到一個檔案中
kotlin是推薦使用者把一些特別小的有關聯的類放到同一個檔案中的。但是對於kotlin和java混用的專案,檔案最好還是一個一個的按照java目錄來組織比較好。
when
kotlin中的when要比java中的switch強大的多,不僅可以使用程式碼塊作為分支體, 並且在kotlin中when可以“不帶引數”:
如果沒有給when表示式提供引數,分支條件就是任意的布林表示式,這個用來替代多個if十分有效:
fun test(value1:Int, value2:Int){ when{ value1 == 1 ->{ } value2 == 2 ->{} value1 > 10 && value2 == 0 ->{ } else ->{} } }
異常
kotlin中並不區分受檢查異常和未受檢查異常,不用指定函式丟擲的異常。
這種設計是基於java中使用異常的實踐做出的決定,經驗顯示java異常規則常常導致許多毫無意義的重新丟擲或者忽略異常程式碼,而且這些規則不能總是保護你免受可能發生的錯誤。
頂層函式與頂層屬性
kotlin中頂層函式的設計很大一部分能解決java中各種Utils類的問題,在kotlin我們可以不用建立這些類,直接把這些函式放到一個檔案中就可以,在使用時使用import來匯入這個函式,和頂層函式一樣,屬性也可以放到檔案中的最頂層。
其實無論是頂層函式還是頂層屬性,在最終都還是被編譯為一個靜態類的方法或者屬性執行在JVM中的。
擴充套件函式
kotlin中的擴充套件函式就是靜態函式的一個高效的語法糖。比如下面這個函式:
//這個類定義在 StringUitlsKt.kt檔案中 fun String.lastChar():Char = this.get(this.length - 1)
實際上的實現就是,生成一個這個類的靜態函式,在呼叫時把呼叫物件this,作為函式的第一個引數傳入。因此如果我們想在java中使用kotlin的擴充套件函式可以這樣使用:
char c = StringUtilsKt.lastChar("java");
- 擴充套件函式是不可以被重寫的,沒有多型性
擴充套件函式並不是類的一部分,它是宣告在類之外的。儘管可以給基類和子類都分別定義一個同名的擴充套件函式,但當這個函式被呼叫時,它是由該變數的靜態型別所決定,而不是這個變數的執行時型別。
介面
kotlin中的介面與java8類似,它們可以包含抽象方法的定義以及非抽象方法的實現,但它們不能包含任何狀態,比如下面這個介面:
interface Clickable{ fun click() fun showOff() = printlin("I'm clickable!") }
kotlin是相容到java6的,但是java6是不支援介面中的預設方法的,因此它會把每個帶預設方法的介面編譯成一個普通介面和一個將方法體作為靜態函式的類的結合體。
少繼承的思想
我們知道在java中類和方法預設是open的,而kotlin中預設都是final的,即如果你想允許建立一個類的子類,需要使用open修飾符來表示這個類,統一。對於可以被重寫的屬性或者方法需要新增open修飾符。
脆弱基類的問題:對於繼承,對於基類的更改是會導致子類不正確的行為的,因為基類程式碼的修改不再符合在其子類中的假設,如果類沒有提供子類應該怎麼實現的明確規則,當事人可能會有按基類作者預期之外的方式來重寫方法的風險。
內部類
kotlin中的內部類不能訪問外部類的例項,即內部類不持有外部類的引用,而在java中內部類就會持有外部類的引用。不過kotlin支援在類宣告上新增inner
,這樣內部類就會持有外部類引用。
class Outer{ inner class Inner{ fun getOuterReference():Outer = this@Outer } }
密封類 sealed
如果一個類添加了sealed
修飾符,那麼這個類的子類宣告必須巢狀在父類中。(kotlin1.1解除了這個限制)密封類對於when
表示式有特殊的效果:如果你在when表示式中處理所有sealed類的子類,你就不再需要提供預設分支,並且如果增加了新的子類,編譯帶返回值的when表示式時會編譯失敗
sealed class Expr{ class Num:Expr() class Sum:Expr() }
getter 與 setter
我們知道kotlin中的屬性,預設實現了getter
與setter
,不過我們可以重寫他們:
var address:String = "aa" set(value:String){ field = "bbbb"//在set方法中可以使用 支援欄位`field`來對欄位進行賦值 }
資料類
如果為一個累添加了data
修飾符,則編譯器會預設生成 toString、equals、hashCode等方法。equals和hashCode方法會將所有在主構造方法中宣告的屬性納入考慮。
類委託
為了避免繼承的脆弱性,我們有時會使用裝飾模式
來對一個類進行擴充套件:這種設計模式的本質就是建立一個新類,實現與原始類一樣的介面並將原來的類的例項作為一個欄位儲存。
在kotlin中實現裝飾模式
十分容易,無論實現什麼介面,可以使用by
關鍵字將介面的實現委託到另一個物件,比如下面這個例子:
class CountingSet<T>(val innerSet:MutableCollection<T> = HashSet()) : MutableCollectio<T> by innerSet
object關鍵字
- 物件宣告:建立單例物件
object Singleton
與類一樣,一個物件宣告可以包含屬性、方法、初始化語句塊等宣告,可以實現介面,並且,物件宣告在定義的時候就被立即建立了,物件宣告不允許有構建方法。
- 伴生物件
在kotlin中不允許擁有靜態成員,不過在kotlin中有頂層函式和伴生物件,使用companion
修飾的物件宣告類似於java中的靜態方法:
class A{ companion object{ fun bar(){} } } >> A.bar()