Kotlin語言基礎(chǔ)筆記
Kotlin流程控制語句筆記
Kotlin操作符重載與中綴表示法筆記
Kotlin擴展函數(shù)和擴展屬性筆記
Kotlin空指針安全(null-safety)筆記
Kotlin類型系統(tǒng)筆記
Kotlin面向?qū)ο缶幊坦P記
Kotlin委托(Delegation)筆記
Kotlin泛型型筆記
Kotlin函數(shù)式編程筆記
Kotlin與Java互操作筆記
Kotlin協(xié)程筆記
1. 根類型Any
Kotlin中所有的類都有一個共同的基類Any改鲫,如果類沒有申明繼承其他類的話,默認繼承的就是Any林束。我們測試一段代碼:
fun main(args: Array<String>) {
val any = Any()
println(any) //打印java.lang.Object@49476842
println(any::class) //打印class kotlin.Any
println(any::class.java) //打印class java.lang.Object
}
上面輸出可以看到其實Kotlin中的Any就是對應Java中的java.lang.Object類型像棘。在Java 中Object類是所有引用類型的父類,但是不包括那些基本類型壶冒,int缕题,long,float等胖腾。而在Kotlin中所有的類型都是引用類型烟零,統(tǒng)一繼承父類Any。
注意胸嘁,如果你發(fā)現(xiàn)
println(any::class)
打印出這么一段Kotlin reflection is not available
瓶摆,請在你的gradle.gradle文件中加入下面這段依賴compile "org.jetbrains.kotlin:kotlin-reflect:$kotlin_version"
。
Any的源碼如下:
它只有三個方法性宏,equals群井,hashCode和toString。這三個方法跟Java中的意思是一樣的毫胜,唯一不同的是书斜,Any的equals在Kotlin中進行了操作符重載诬辈,所以大家可以使用
==
來進行值相等的比較。
2. 基本類型
在Java中有byte荐吉、int焙糟、short、long样屠、float穿撮、double、char痪欲、boolean這些基本類型悦穿,另外,void也可以算是一種基本類型业踢,它也有一個裝箱類Void
(Koltin中也有類似的概念栗柒,Unit、Nothing)知举,Void不能new出來瞬沦。
在Kotlin中是真正的一切皆是對象。所有的類型都是引用類型雇锡。Kotlin中的基本類型的類圖如下:
2.1 數(shù)字(Number)類型
Kotlin 提供了如下的內(nèi)置類型來表示數(shù)字(與 Java 很相近):
類型 | 寬度(Bit) |
---|---|
Double | 64 |
Float | 32 |
Long | 64 |
Int | 32 |
Short | 16 |
Byte | 8 |
從上面的類結(jié)構(gòu)圖逛钻,我們可以看到這些內(nèi)置的數(shù)字類型,都繼承了Number和Comparable類锰提。Kotlin的數(shù)字類型跟Java的基本相同绣的,但是要注意的是Kotlin中數(shù)字沒有隱私轉(zhuǎn)化,也就是說Kotlin的Int不能自動轉(zhuǎn)換成Long欲账。另外:Kotlin中字符Char
不是數(shù)字。Kotlin的這些內(nèi)部數(shù)字類型在運行時會自動轉(zhuǎn)換成Java的基本類型芭概。
數(shù)值常量字面值有以下幾種
- 十進制:
123
- Long類型:
123L
- 十六進制:
0x0F
- 二進制:
0b000011
- Double類型:
123.123
赛不、123.123e10
- Float類型:
123.123f
、123.123F
- 我們也可以使用下劃線來分隔數(shù)字:
1_000_000
罢洲、1234_5678_9012_3456L
踢故、0xFF_EC_DE_5E
、0b11010010_01101001_10010100_10010010
顯示轉(zhuǎn)換
范圍較小的類型需要顯示轉(zhuǎn)換成較大的類型惹苗。
你需要顯示轉(zhuǎn)換類型殿较,如下代碼:
fun main(args: Array<String>) {
val a: Int? = 1
val g: Long? = a?.toLong()
val b: Byte = 1
val i: Int = b.toInt()
}
每個數(shù)字類型都繼承Number類,而Number中定義了一些方法用來方便的做顯示轉(zhuǎn)換桩蓉。
操作符重載
有時候缺少隱式轉(zhuǎn)換也沒關(guān)系淋纲,例如Long類對+
進行了重載。
public operator fun plus(other: Byte): Long
public operator fun plus(other: Short): Long
public operator fun plus(other: Int): Long
public operator fun plus(other: Long): Long
public operator fun plus(other: Float): Float
public operator fun plus(other: Double): Double
2.2 Char字符類型
字符用Char
表示院究。他們不能直接用來當作數(shù)字洽瞬。
特殊字符可以用反斜杠轉(zhuǎn)義本涕,當然也支持Unicode轉(zhuǎn)義序列語法,例如:
\uFF00
伙窃。
2.3 String 字符串類型
索引運算符
fun main(args: Array<String>) {
val s: String = "abc"
println(s[2]) //打印c
}
查看源碼可以知道菩颖,s[i]
會被翻譯成java.lang.String.charAt()
。
循環(huán)迭代字符串
fun main(args: Array<String>) {
val s: String = "abc"
for (c in s) {
println(c)
}
}
操作符+
重載
字符串String類型重載了+
操作符为障,作用對象是可以任意對象晦闰,包括空引用:
>>> "abc".plus(true)
abctrue
>>> "abc"+false
abcfalse
>>> "abc"+1
abc1
>>> "abc"+1.20
abc1.2
>>> "abc"+100L
abc100
>>> "abc"+"cdef"
abccdef
>>> "abc"+null
abcnull
>>> "abc"+'z'
abcz
>>> "abc"+arrayOf(1,2,3,4,5)
abc[Ljava.lang.Integer;@3d6f0054
字符串的值
使用兩個雙引號定義,其中可以包含一些轉(zhuǎn)義字符鳍怨。
val s: String = "Hello world, \n\n\n"
還可以使用三個雙引號("""
)括起來呻右,里面可以包含任意字符。
val s: String = """
for (c in "abc")
print(c)
"""
另外:在kotlin.text包下面的Indent.kt中定義了String類的擴展函數(shù)京景。我們可以使用trimMargin()
和trimIndent()
來去除空格窿冯。
trimMargin()默認使用"|"來作為邊界字符。
fun main(args: Array<String>) {
val text = """
|理論是你知道是這樣确徙,但它卻不好用醒串。
|實踐是它很好用,但你不知道是為什么鄙皇。
|程序員將理論和實踐結(jié)合到一起:
|既不好用芜赌,也不知道是為什么。
"""
println(text.trimMargin())
}
輸出
理論是你知道是這樣伴逸,但它卻不好用缠沈。
實踐是它很好用,但你不知道是為什么错蝴。
程序員將理論和實踐結(jié)合到一起:
既不好用洲愤,也不知道是為什么。
trimIndent()則是把字符串行的左邊空白對其切割:
fun main(args: Array<String>) {
val text = """
hello
world!
"""
println(text.trimIndent())
}
輸出
hello
world!
字符串模版
字符串中可以包含一些表達式顷锰,如下:
fun main(args: Array<String>) {
val name = "Denny"
val price = 100.5
println("My name is $name") //打印My name is Denny
println("$name.length is ${name.length}") //打印Denny.length is 5
println("""the price is $$price""") //打印the price is $100.5
}
2.4 Array 數(shù)組類型
class Array<T> private constructor() {
val size: Int
operator fun get(index: Int): T
operator fun set(index: Int, value: T): Unit
operator fun iterator(): Iterator<T>
// ……
}
我們可以看到Array重載了[]
操作符柬赐。
我們可以使用arrayOf()
來創(chuàng)建一個數(shù)組,并給定數(shù)組中的初始值官紫。
fun main(args: Array<String>) {
val a1 = arrayOf(1, 2, 3)
println(a1::class) //打印class kotlin.Array
println(a1::class.java) //打印class [Ljava.lang.Integer;
val a2 = arrayOf(1, "s", null)
println(a2::class) //打印class kotlin.Array
println(a2::class.java) //打印class [Ljava.lang.Object;
}
我們可以使用arrayOfNulls()
來創(chuàng)建一個指定大小肛宋,元素都為null的數(shù)組,在創(chuàng)建的時候我們必須指定其中元素的類型束世,要不然會報錯酝陈。
另外:Array類還有一個構(gòu)造函數(shù)。
public inline constructor(size: Int, init: (Int) -> T)
第一個參數(shù)是數(shù)組的大小毁涉,第二個參數(shù)是初始化函數(shù)類型的參數(shù)沉帮。比如:
fun main(args: Array<String>) {
val a = Array(8, { i -> i + i })
println(a.joinToString(prefix = "[", postfix = "]")) //打印[0, 2, 4, 6, 8, 10, 12, 14]
}
Kotlin中也有無裝箱開銷的用來表示原生類型的數(shù)組。這些原生數(shù)組類型如下:
- BooleanArray
- ByteArray
- CharArray
- ShortArray
- IntArray
- LongArray
- FloatArray
- DoubleArray
- BooleanArray
這些類和Array沒有繼承關(guān)系,但是他們有同樣的函數(shù)和屬性遇西,他們也有相應的工廠方法:
/**
* Returns an array containing the specified [Double] numbers.
*/
public fun doubleArrayOf(vararg elements: Double): DoubleArray
/**
* Returns an array containing the specified [Float] numbers.
*/
public fun floatArrayOf(vararg elements: Float): FloatArray
/**
* Returns an array containing the specified [Long] numbers.
*/
public fun longArrayOf(vararg elements: Long): LongArray
/**
* Returns an array containing the specified [Int] numbers.
*/
public fun intArrayOf(vararg elements: Int): IntArray
/**
* Returns an array containing the specified characters.
*/
public fun charArrayOf(vararg elements: Char): CharArray
/**
* Returns an array containing the specified [Short] numbers.
*/
public fun shortArrayOf(vararg elements: Short): ShortArray
/**
* Returns an array containing the specified [Byte] numbers.
*/
public fun byteArrayOf(vararg elements: Byte): ByteArray
/**
* Returns an array containing the specified boolean values.
*/
public fun booleanArrayOf(vararg elements: Boolean): BooleanArray
3. 可空類型(Nullable Types)
Kotlin把可空性作為類型系統(tǒng)的一部分馅精,這樣編譯器可以在編譯過程中發(fā)現(xiàn)一些可能的錯誤,減少運行過程中拋出異常的可能性粱檀。
Kotlin的類型系統(tǒng)和Java相比洲敢,主要的區(qū)別就是Kotlin對可空類型的顯示支持。
3.1 Kotlin中的null
fun main(args: Array<String>) {
println(null == null) //打印true
println(null != null) //打印false
println(null is Any) //打印false
println(null is Any?) //打印true
}
與Java不同茄蚯,Kotlin中null與null是相等的压彭,null不是Any類型,但是是Any渗常?類型壮不。下面我們來看看Null到底是什么類型。
編譯器告訴我們null的類型是
Nothing?
皱碘。
3.2 可空性的實現(xiàn)原理
假如我們有這樣一個StringUtil.kt類询一。
package com.dengyin2000.kotlintest1
object StringUtil{
fun testNullable1(x: String, y: String?): Int {
return x.length
}
fun testNullable2(x: String, y: String?): Int? {
return y?.length
}
fun testNullable3(x: String, y: String?): Int? {
return y!!.length
}
}
然后我們來看看上面的代碼對應生成的Java代碼是怎樣的。在Intellij IDEA中點擊"Tools" -> "Kotlin" -> "Show Kotlin Bytecode"癌椿。
再在出來的界面上點擊"Decompile"健蕊。
最后就能看到最終生成的Java代碼,代碼如下:
我們可以看到生成的Java代碼踢俄,不可為空變量都注解了@NotNull
缩功,而可為空變量注解了@Nullable
。
在函數(shù)調(diào)用前都用kotlin.jvm.internal.Intrinsics. checkParameterIsNotNull
方法檢查了不為空變量是否為空都办。
public static void checkParameterIsNotNull(Object value, String paramName) {
if (value == null) {
throwParameterIsNullException(paramName);
}
}
而可空變量的安全調(diào)用符y?.length
轉(zhuǎn)成Java的代碼如下:
y != null?Integer.valueOf(y.length()):null
可空變量的斷言調(diào)用y!!.length
轉(zhuǎn)成的Java代碼如下:
if (y == null) {
Intrinsics.throwNpe();
}
return y.length();
3.3 可空類型層次體系
Any
是非空類型的根嫡锌,Any?
是可空類型的根,由于Any?
是Any的根琳钉,所以Any?
是Kotlin的類型層次結(jié)構(gòu)的最頂端势木。
fun main(args: Array<String>) {
println(1 is Any) //打印true
println(1 is Any?) //打印true
println(null is Any) //打印false
println(null is Any?) //打印true
println(Any() is Any?) //打印true
}
4. kotlin.Unit類型
Kotlin中的Unit
類實現(xiàn)了跟Java中void
一樣的功能,大多數(shù)情況下歌懒,我們并不需要顯示的返回Unit
跟压,或者申明一個函數(shù)的返回類型為Unit
。編譯器會推斷它歼培。
fun sayHello1() {
}
fun sayHello2() {
return Unit
}
fun sayHello3(): Unit {
}
fun main(args: Array<String>) {
println(sayHello1()) //打印kotlin.Unit
println(sayHello2()) //打印kotlin.Unit
println(sayHello3()) //打印kotlin.Unit
}
這三個函數(shù)其實是一樣的∪兹總之這個Unit
并沒有什么特別之處躲庄,看看它的源碼:
package kotlin
/**
* The type with only one value: the Unit object. This type corresponds to the `void` type in Java.
*/
public object Unit {
override fun toString() = "kotlin.Unit"
}
跟其他的類型一樣, Unit
的父類是Any
钾虐,Unit?
的父類是Any?
噪窘。
5. kotlin.Nothing類型
Kotlin中沒有類似Java中的返回值為void
的標記,在Java中效扫,返回void
方法倔监,其返回值是無法被訪問到的直砂。void
不是變量的類型,但是在Java的包裝類中Void
是void
的包裝類浩习,如果你想讓一個方法返回類型永遠是null的話静暂,可以寫成如下:
public Void voidDemo() {
System.out.println("Hello,Void");
return null;
}
這個Void
就是Kotlin中的Nothing?
。它的唯一可被訪問到的值也是null谱秽。
注意:Unit
和Nothing
的區(qū)別:Unit類型表達式計算結(jié)果返回的是Unit類型洽蛀,Nothing類型表示永遠不會返回結(jié)果(跟Java的void相同)。
throw關(guān)鍵字中斷表達式的計算疟赊,并拋出堆棧的功能郊供。所以,一個throw Exception
的代碼就是返回Nothing
的表達式近哟。
fun formatCell(value: Double): String =
if (value.isNaN())
throw IllegalArgumentException("$value is not a number") // Nothing
else
value.toString()
再比如驮审,Kotlin的標準庫的exitProcess
函數(shù):
@file:kotlin.jvm.JvmName("ProcessKt")
@file:kotlin.jvm.JvmVersion
package kotlin.system
/**
* Terminates the currently running Java Virtual Machine. The
* argument serves as a status code; by convention, a nonzero status
* code indicates abnormal termination.
*
* This method never returns normally.
*/
@kotlin.internal.InlineOnly
public inline fun exitProcess(status: Int): Nothing {
System.exit(status)
throw RuntimeException("System.exit returned normally, while it was supposed to halt JVM.")
}
Nothing?可以只包含一個值:null。
6. 類型檢測與類型轉(zhuǎn)換
6.1 is運算符
Kotlin的is運算符跟Java中的instanceof是一樣的吉执,用來檢測某個對象是否某個類型或者父類的實例疯淫。其實我們之前已經(jīng)有用到is運算符了
。
當你使用is運算符時鼠证,類型會自動轉(zhuǎn)換峡竣,例如:
open class Animal{
open fun eat(){
println("animal eat")
}
}
open class Dog : Animal() {
fun run() {
println("run")
}
}
class Bird : Animal() {
fun fly() {
println("fly")
}
}
class Fish : Animal() {
fun swin() {
println("swin")
}
}
fun playAnimal(animal: Animal) {
when (animal) {
is Dog -> animal.run() //自動類型轉(zhuǎn)換
is Bird -> animal.fly() //自動類型轉(zhuǎn)換
is Fish -> animal.swin() //自動類型轉(zhuǎn)換
}
}
如果是在Java中,我們需要用instanceof判斷后然后再顯示類型轉(zhuǎn)換(Cast)量九。
6.2 as運算符
as
運算符用于進行顯示類型轉(zhuǎn)換适掰,如果轉(zhuǎn)換的類型與指定的類型兼容,轉(zhuǎn)換就能成功荠列,如果類型不兼容类浪,使用as?
就會返回null。
package com.dengyin2000.kotlintest1
open class Animal{
open fun eat(){
println("animal eat")
}
}
open class Dog : Animal() {
fun run() {
println("run")
}
}
class Bird : Animal() {
fun fly() {
println("fly")
}
}
class Fish : Animal() {
fun swin() {
println("swin")
}
}
fun main(args: Array<String>) {
val animal = Animal()
val dog1 = animal as? Dog
println(dog1) //打印null
val dog = animal as Dog //Exception in thread "main" java.lang.ClassCastException: com.dengyin2000.kotlintest1.Animal cannot be cast to com.dengyin2000.kotlintest1.Dog
}