本文是對<<Kotlin in Action>>
的學習筆記,若是須要運行相應的代碼能夠訪問在線環境 try.kotlinlang.org,這部分的思惟導圖爲: java
Java
把基本數據類型和引用類型作了區分:數組
int
的變量直接存儲了它的值,咱們不能對這些值調用方法,或者把它們放到集合中。Kotlin
不區分基本數據類型和引用類型,它使用的永遠是一個類型(例如Int
),此外,你還能對一個數字類型的值調用方法。ide
在運行時,數字類型會盡量地使用最高效的方式來表示,大多數狀況下,對於變量、屬性、參數和返回類型,Kotlin
的Int
類型會被編譯成Java
基本數據類型int
。惟一不可行的例外是泛型類,例如集合,用做泛型類型參數的基本數據類型會被編譯成對象的Java
包類型。函數
對應到Java
基本數據類型的類型完整列表以下:學習
Byte
、Short
、Int
、Long
Float
、Double
Char
Boolean
像Int
這樣的Kotlin
類型在底層能夠輕易地編譯成對應的Java
基本數據類型。而在Kotlin
中使用Java
聲明時,Java
基本數據類型會變成非空類型,由於它們不能持有null
值。spa
Kotlin
中的可空類型不能用Java
的基本數據類型表示,由於null
只能被存儲在Java
的引用類型的變量中。任什麼時候候,只要使用了基本數據類型的可空版本,它就會被編譯成對應的包裝類型,而且不能比較兩個可空基本數據類型的大小,由於它們之中任何一個均可能爲null
。設計
除此以外,泛型類是包裝類型應用的另外一種狀況,若是你 用基本數據類型做爲泛型類的類型參數,那麼 Kotlin 會使用該類型的包裝形式,例以下面這段代碼,就會建立一個Integer
包裝類的列表,儘管你歷來沒有指定過可空類型或者用過null
值:3d
val listOfInts = listOf(1, 2, 3)
複製代碼
這是由Java
虛擬機實現泛型的方式決定的,JVM
不支持用基本數據類型做爲類型參數,因此泛型類必須始終使用類型的包裝表示。code
Kotlin
和Java
之間一條重要的區別就是處理數字轉換的方式,Kotlin
不會自動地把數字從一種類型轉換成另外一種,即使是轉換成範圍更大的類型,咱們必須 顯示地轉換,對每一種基本數據類型都定義有轉換函數:toByte()
、toShort()
、toChar()
等,這些函數支持雙向轉換: cdn
equals
不只會檢查它們存儲的值,還要比較裝箱類型,也就是說
new Integer(42).equals(new Long(42))
會返回
false
。
Kotlin
除了支持簡單的十進制數字以外,還支持下面這些在代碼中書籤數字字面值的方式:
L
表示Long
:123L
Double
:0.12
、1.2e10
和1.2e-10
。F
表示Float
:123.4f
、.456F
和1e3f
。0x
或者0X
表示十六進制:0xbcdL
。0b
或者0B
表示二進制字面值:0b0001
。當你使用數字字面值去初始化一個類型已知的變量時,又或是把字面值做爲實參傳給函數時,必要的轉換會自動地發生。
此外,算術運算符也被重載了,它們能夠接收全部適當的數字類型。Any
類型是Kotlin
全部非空類型的超類型,包括像Int
這樣的基本數據類型,和Java
同樣,把基本數據類型的值賦給Any
類型的變量會自動裝箱。
在Kotlin
中,若是你須要能夠持有任何可能值的變量,包括null
在內,必須使用Any?
類型。
在底層,Any
類型對應java.lang.Object
,Kotlin
把Java
方法參數和返回類型中用到的Object
類型看做Any
,當Kotlin
函數函數中使用Any
時,它會被編譯成Java
字節碼中的Object
。
全部的Kotlin
類都包含下面三個方法:toString
、equals
和hashCode
,這些方法都繼承自Any
。Any
不能使用其它Object
的方法(例如wait
和notify
),可是能夠經過手動把值轉換成java.lang.Object
來調用這些方法。
Kotlin
中的Unit
類型完成了Java
中的void
同樣的功能,當函數沒有有意思的結果要返回時,它能夠用做函數的返回類型:
fun f() : Unit { .. }
複製代碼
Unit
是一個完備的類型,能夠做爲類型參數,而void
卻不行。只存在一個值是Unit
類型,這個值也叫作Unit
,而且(在函數中)會被隱式地返回,當你在重寫返回泛型參數的函數時這很是有用,只須要讓方法返回Unit
類型的值:
對於某些Kotlin
函數來講,「返回類型」的概念沒有任何意義,由於它們歷來不會成功地結束,Kotlin
使用一種特殊的返回類型Nothing
來表示:
Nothing
類型沒有任何值,只有被看成函數返回值使用,或者被看成泛型函數返回值的類型參數使用纔會有意義,在其它狀況下,聲明一個不能存儲任何值的變量沒有任何意義。
返回Nothing
的函數能夠放在Elvis
運算符的右邊來作先決條件檢查:
在 Kotlin 知識梳理(6) - Kotlin 的可空性 中,咱們討論了可空類型的概念,但僅僅簡略地談到類型參數的可空性,其實集合也能夠持有null
元素,和變量能夠持有null
同樣,類型在被看成類型參數時也能夠用一樣的方式來標記。
下面咱們建立一個包含可空值的集合,以後遍歷該集合,打印出有效的數字之和以及爲null
的集合元素個數:
Kotlin
的集合設計與Java
不一樣的另外一項重要特質是:它把訪問集合數據的接口和修改集合數據的結構分開了:
kotlin.collections.Collection
:使用這個接口,能夠遍歷集合中的元素、獲取集合大小、判斷集合中是否包含某個元素,執行其餘從該集合中讀取數據的操做。kotlin.collections.MutableCollection
:修改集合中的數據。通常的原則是:在代碼的任何地方都應該使用只讀接口,只在代碼須要修改集合的地方使用可變接口的變體。
下面的例子演示瞭如何使用只讀集合和可變集合:
運行結果爲:每個Kotlin
接口都是其對應Java
集合接口的一個實例,在Kotlin
和Java
之間轉移並不須要轉換;不須要包裝器也不須要拷貝數據。
每一種Java
集合接口在Kotlin
中都有兩種表示:一種是隻讀的,另外一種是可變的。在下圖當中,能夠看出Kotlin
集合接口的層級結構,Java
類ArrayList
和HashSet
都繼承了Kotlin
可變接口。
Kotlin
中只讀接口和可變接口的基本結構與
java.util
中的
Java
集合接口的結構是平行的。可變接口直接對應
java.util
包中的接口,而它們的只讀版本缺乏了全部產生改變的方法。
上圖中包含了Java
類中的ArrayList
和HashSet
,在Kotlin
看來,它們分別繼承自MutableList
和MutableSet
接口,這樣既獲得了兼容性,也獲得了可變接口和只讀接口之間清晰的分離。
除了集合以外,Kotlin
中Map
類也被表示成了兩種不一樣的版本:Map
和MutableMap
。咱們以前見到的listOf/setOf/mapOf
所返回的都是隻讀版本。
當你有一個使用java.util.Collection
作形參的Java
方法,能夠把任意Collection
或MutableCollection
的值做爲實參傳遞給這個形參。Java
並不會區分只讀集合和可變集合,也就是說即便Kotlin
中把集合聲明成只讀的,Java
代碼也能夠修改這個集合,例以下面的代碼,雖然咱們將printInUppercase
接收的list
參數聲明爲只讀的,可是仍然能夠經過Java
代碼修改它。
//CollectionUtils.java
public class CollectionUtils {
public static List<String> uppercaseAll(List<String> items) {
for (int i = 0; i < items.size(); i++) {
items.set(i, items.get(i).toUpperCase());
}
return items;
}
}
//collections.kt
fun printInUppercase(list : List<String>) {
println(CollectionUtils.uppercaseAll(list));
println(list.first())
}
複製代碼
前面咱們介紹過,Kotlin
把那些定義在Java
代碼中的類型當作 平臺類型,Kotlin
沒有任何關於平臺類型的可空性信息,因此編譯器容許Kotlin
代碼將其視爲可空或者非空,一樣,Java
中聲明的集合類型的變量也被視爲平臺類型。
當咱們須要重寫或者實現簽名中有集合類型的Java
方法時,這些差別才變得重要,咱們須要決定使用哪種Kotlin
類型來表示這個Java
類型,它們會反映在產生的Kotlin
參數類型中:
例以下面這個使用集合參數的Java
接口:
interface DataParser<T> {
void parseData(String input, List<T> output, List<String> errors);
}
複製代碼
咱們的選擇爲:
List<String>
將是非空的,由於調用者老是須要接收錯誤信息。List<String>
將是可變的,由於實現代碼須要向其中添加元素。那麼Kotlin
的實現以下:
class PersonParser : DataParser<Person> {
override fun parseData(input : String, output : MutableList<Person>,
errors : MutableList<String?>)
}
複製代碼
Kotlin
中的一個數組是一個帶有類型參數的類,其元素類型被指定爲相應的類型參數,要在Kotlin
中建立數組,有下面這些方法供你選擇:
arrayOf
函數建立一個數組,它包含的元素是指定爲該函數的實參arrayOfNulls
建立一個給定大小的數組,包含的是null
元素,固然,它只能用來建立包含元素類型可空的數組Array
構造方法接收數組的大小和一個lambda
表達式,調用lambda
表達式來建立每個數組元素,這就是使用非空元素類型來初始化數組,但不用顯示地傳遞每一個元素的方式數組類型的類型參數始終會變成對象類型,所以,若是你聲明瞭一個Array<Int>
,它將會是一個包含裝箱整型的數組,若是你須要建立沒有裝箱的基本數據類型的數組,必須使用一個基本數據類型數組的特殊類。
Kotlin
提供了若干個獨立的類,每一種基本數據類型對應一個,例如Int
類型值的數組叫做IntArray
,要建立一個基本數據類型的數組,有以下的選擇:
size
參數並返回一個使用對應基本數據類型默認值初始化好的數組。IntArray
的intArrayOf
,以及其餘數組類型的函數)接收變長參數的值並建立和存儲這些值的數組。lambda
。