博客主頁java
Kotlin核心語法(二):kotlin程序結構、函數
Kotlin核心語法(三):kotlin類、對象、接口
Kotlin核心語法(四):kotlin Lambda編程git
Kotlin就是一門能夠運行在Java虛擬機、Android、瀏覽器上的靜態語言,它與JAVA 100%兼容,若是你對Java很是熟悉,那麼你就會發現Kotlin除了本身的標準庫以外,大多仍然使用經典的JAVA集合框架。github
先來體驗一下Kotlin代碼。Book類包含兩個屬性:name 和 price。 price屬性默認值爲null,算法
// 可空類型(Float?) price的實參默認值爲null data class Book(val name: String, val price: Float? = null) // 入口函數 fun main(args: Array<String>) { val books = listOf(Book("Kotlin"), Book("Java", 13.9f)) // lambda表達式, maxBy查找books列表中價格最貴的書 // 若是price屬性值爲null,Elvis雲算法(?:)會返回零 val book = books.maxBy { it.price ?: 0.0f } println("the book is :$book") } // 自動生成Book的toString方法 // the book is :Book(name=Java, price=13.9)
Kotlin具備類型推導能力,在源代碼中也不用顯示地聲明每一個變量的類型,會根據上下文自動判斷,val x = 1, 會自動判斷變量x的類型是Int
Kotlin對可空類型的支持,在編譯期檢測可能存在的空指針異常編程
http://github.com/jetbrains/k...
Kotlin團隊打造的庫Anko(http://github.com/kotlin/anko)給不少標準Android API 添加了Kotlin友好的適配器。segmentfault
Kotlin 和 Java 都是編譯型語言,必須先編譯,而後才能執行代碼。數組
Kotlin官方網站
https://kotlinlang.org/docs/t...
Kotlin源代碼存放在後綴名爲.kt的文件中。Kotlin編譯器會分析源代碼並生成.class文件,而後執行生成的.class文件瀏覽器
$ kotlinc hello.kt -include-runtime -d hello.jar $ java -jar hello.jar
// HelloWord.kt // 1. 使用關鍵字fun聲明一個函數 // 2. 參數的類型寫在參數名後面,args: Array<String> // 3. 函數能夠定義在文件的最外層,不須要把它放在類中 // 4. 數組就是類,在Kotlin中沒有聲明數組類型的特殊語法,如:Array<String> // 5. 使用println代替System.out.println。Kotlin標準庫給JAVA標準庫函數提供不少語法更簡潔的包裝 // 6. 能夠省略每行代碼結尾的分號 fun main(args: Array<String>) { println("Hello, Word!") }
聲明一個有返回值的函數,參數列表的後面跟着返回值類型,用 冒號(:)隔開安全
// 聲明格式: fun 函數名(參數列表) : 返回類型 { 函數體 } fun max(a: Int, b: Int): Int { // 在Kotlin中,if 是表達式,而不是語句。表達式有值,且能做爲另外一個表達式的一部分是使用 return if (a > b) a else b } // 若是函數體是單個表達式,這個表達式能夠做爲完整的函數體,且可省略花括號 和 return 語句 fun max2(a: Int, b: Int): Int = if (a > b) a else b // 也能夠省略 返回值類型。注意:只有表達式體函數的返回類型能夠省略 fun max3(a: Int, b: Int) = if (a > b) a else b
變量標識一個對象的地址,稱爲標識符。在Kotlin中,全部變量類型都是引用類型。服務器
Kotlin中,變量又分爲 可變變量(var) 和 不可變量(val)
默認狀況下,儘量使用 val 關鍵字聲明全部的Kotlin變量。
幾個注意點:
val message: String if (條件) { message = "Success" } else { message = "Failed" }
val books = arrayListOf("Kotlin") books.add("Java")
var age = 23 // 錯誤:類型不匹配 age = "32"
Kotlin中,變量聲明以關鍵字(val、var)開始,而後變量名。最後加上變量類型(也能夠不加,編譯器會自動類型推導)
val a: Int = 12 // 也能夠省略變量類型,編譯器會分析初始化表達式的值,並把它的類型做爲變量的類型 val a = 12 // 若是變量沒有初始化,須要顯示地指定它的類型 val b: Int b = 13
基本數據類型與引用數據類型在建立時,內存存儲方式區別:
Java中每個基本數據類型都引入了對應的包裝類型(wrapper class),如:int 的包裝類型就是 Integer,從Java 5 開始引入自動裝箱、拆箱機制。
Kotlin中去掉了原始類型,只有包裝類型,編譯器在編譯代碼的時候,會自動優化性能,把對應的包裝類型拆箱爲原始類型。
Kotlin是不區分基本數據類型和它們的包裝類。如:Int
val a: Int = 12 val list: List<Int> = listOf(11, 12, 13)
Kotlin還能夠對數字類型的值調用方法,coerceIn是標準庫函數,把值限制在特定範圍內
val a: Int = 110 val newValue = a.coerceIn(0, 100) println("把值限制在0到100之間, 限制前的值: $a, 限制後的值: $newValue") // 把值限制在0到100之間, 限制前的值: 110, 限制後的值: 100
Koltin的Int類型會被編譯成Java基本數據類型int,除泛型類外,泛型類型參數的基本數據類型會被編譯成對應的Java包裝類型,如Int類型編譯成java.lang.Integer
在Kotlin中,不可空基本數據類型與Java中的原始的數字類型對應,如:Kotlin中Int,對應Java中的int;
可空的基本數據類型與Java中的裝箱類型對應,如:Kotlin中Int?,對應Java中Integer
null 只能被存儲在Java引用類型的變量中,Kotlin中可空數基本據類型不能用Java的基本數據類型表示。
// 使用可空基本數據類型(Int?)變量a,會被編譯成java.lang.Integer類型 fun isGreaterThan5(a: Int?): Boolean? { if (a == null) return null return a > 5 }
Kotlin不會自動把數字從一種類型轉換成另外一種類型,如:Int類型不會自動轉換爲Long類型
val a: Int = 12 // 這行代碼報錯:類型不匹配 val b: Long = a // 須要顯示的進行轉換 val b: Long = a.toLong()
Koltin要求轉換必須是顯示的,尤爲在比較裝箱值的時。比較兩個裝箱值的equals方法不只會檢查它們存儲的值,還要比較裝箱類型。在Java中new Integer(12).equals(new Long(12)) 返回false。
val a: Int = 12 val list = listOf(12L, 13L, 14L) // 這行代碼編譯器不會編譯,要求顯示的轉換類型 a in list // 顯示的將 Int 轉換 Long 才能夠比較 a.toLong() in list
Kotlin 標準庫提供不少擴展方法,如:字符串轉換成基本數據類型(toInt,toByte,toBoolean等),若是轉換失敗拋出 NumberFormatException
println("12".toInt())
Java的超類型是Object,Kotlin的全部非空類型的超類型是Any類型。可是:在Java中,Object只是全部引用類型的超類型(引用類型的根),而基本數據類型並非。在Kotlin中,Any是全部類型的超類型,包括Int基本數據類型。
// 基本數據類型的值賦值給Any類型的變量時會自動裝箱 // Any是非空類型,不能持有null值,若是持有任何可能值,包括null,必須使用 Any? 類型 val a: Any = 12 // Kotlin中使用Any類型會編譯轉換成java.lang.Object
全部Kotlin類都包含下面三個方法:equals、hashCode、toString。這三個方法定義在Any類中,可是Any類不能使用其餘java.lang.Object的方法(如:wait、notify),能夠轉換成java.lang.Object來調用這些方法。
Kotlin中若是函數沒有返回值時,可使用Unit做爲函數返回類型
fun f(): Unit { ... } // Unit 能夠省略 fun f(){ ... }
Kotlin的Unit 和 Java的 void區別:Unit能夠做爲類型參數,而void不行。當只存在一個值是Unit類型,這個值也叫做Unit,且在函數中會被隱式的返回。
interface Processor<T> { fun process(): T } class NoResultProcessor : Processor<Unit> { // 返回Unit類型,可是能夠省略 override fun process() { // 不須要顯式的return,編譯器會隱式地加上return Unit } }
在Java中,爲了解決使用 沒有值 做爲類型參數,給出的方案沒有Kotlin好。一種是選擇使用分開的接口定義來分別表示須要和不須要返回值的接口(如:Callable 和 Runnable),另外一種是用特殊的 java.lang.Void 類型做爲類型參數,但仍是須要加入一個return null;語句
Nothing類型沒有任何值,只有被看成函數返回值使用,或者被看成泛型函數返回值的類型參數使用纔有意義。
Kotlin對 可空類型 是顯示支持的。如:String?、Int?,能夠存儲 null 引用。沒有問號的類型表示這種類型不能存儲null引用,說明默認都是非空的類型,除非顯示的把它標記爲可空類型。
// 下面是一段java代碼 // 若是調用函數時傳入null,將拋出NullPointerException int strLen(String s) { return s.length(); } // 下面是一段Kotlin代碼 // 若是調用函數時傳入null,kotlin編譯器是不容許的,保證了strLen函數永不會拋出NullPointerException // 編譯期會標記成錯誤:Null can not be a value of a non-null type String fun strLen(s: String): Int = s.length
上例中,若是在調用strLen函數的時容許傳入null,須要顯示的在參數類型後面加上問號(?)標記
// ? 能夠加在任何類型的後面來表示這個類型的變量能夠存儲null引用 fun strLenSafe(s: String?): Int = ... // 下面這段Kotlin代碼,s.length 編譯器是不容許的 // ERROR: only safe (?) or non null asserted (!!.) calls are allowed // on a nullable receiver of type kotlin.String? // 可使用 if 檢查處理可控性,可是代碼就會變冗長。但Kotlin提供了更簡潔的方式處理可空值 fun strLenSafe(s: String?): Int = s.length // 下面這段Kotlin代碼,編譯器也是不容許的,不能賦值給非空類型的變量 // ERROR: Type mismatch:Required String , Found String? val a: String? = null val b: String = a
安全調用運算符:?. 容許把null的檢查和方法調用合併一個操做。
string?.length() 等價於 if (string != null) string.length() else null
安全調用運算符:?. 調用的結果類型也是可空的,下面例子中,s?.toUpperCase()結果類型是String?
fun printAllCaps(s: String?) { // allCaps 多是null val allCaps: String? = s?.toUpperCase() println(allCaps) } >>> printAllCaps(null) null
多個安全調用運算符能夠連接調用
class Address(val name: String) class Company(val name: String, val address: Address?) class Person(val name: String, val company: Company?) fun Person.printAddress(): String? { // 多個安全調用運算符連接調用 val addressName = this.company?.address?.name // Elvis運算符:?: addressName?:"Unknown" return if (addressName != null) addressName else "Unknown" } >>> val person = Person("kerwin", null) >>> println(person.printAddress()) Unknown
Kotlin使用Elvis運算符(或者null合併運算符)來提供代替 null 的默認值。
Elvis運算符?: 和 安全調用運算符?.一塊兒使用
// 當s==null, s?.length 返回null // s?.length == null 返回 0 fun strLenSafe(s: String?): Int? = s?.length ?: 0 >>> println(strLenSafe("abc")) 3 >>> println(strLenSafe(null)) 0
as? 運算符嘗試把值轉換成指定的類型,若是值不是合適的類型就返回null
class Person(val firstName: String, val lastName: String) { override fun equals(other: Any?): Boolean { // 若是other不是Person類型,other as? Person 返回null,就會直接返回false val otherPerson = other as? Person ?: return false return otherPerson.firstName == firstName && otherPerson.lastName == lastName } } >>> val p1 = Person("Kerwin", "Tom") >>> val p2 = Person("Kerwin", "Tom") >>> println(p1 == p2) true
非空斷言使用 雙感嘆號(!!)表示,能夠把任何值轉換成非空類型,若是對null值作非空斷言,則會拋出異常。
fun ignoreNulls(s: String?) { // 若是s == null, 拋出KotlinNullPointerException val sNotNull = s!! println(sNotNull.length) } >>> ignoreNulls(null) Exception in thread "main" kotlin.KotlinNullPointerException
和安全調用運算符一塊兒使用,容許對錶達式求值,檢查求值結果是否爲null,並把結果保存爲一個變量。
let函數只在值非空時才被調用
fun sendMessage(message: String) { println(message) } // 當 message != null時,纔會執行lambda表達式 >>> val message: String? = null >>> message?.let { msg -> sendMessage(msg) } >>> // it 默認變量名,能夠簡寫 message?.let { sendMessage(it) }
不少框架會對對象實例建立以後用專門的方法來初始化對象。如:Activity的初始化發生在onCreate方法中,JUnit要求把初始化邏輯放在用 @Before註解的方法中。
class MyService{ fun performAction(): String = "test" } class MyTest { // 聲明一個可空類型的屬性並初始化爲null private var myService: MyService? = null @Before fun setup() { // 在setup方法中提供真正的初始化器 myService = MyService() } @Test fun testAction() { // 必須注意可空性:要麼用 !!,要麼用 ?. Assert.assertEquals("test", myService!!.performAction()) } }
上例kotlin代碼中,對屬性myService每一次訪問都須要可空性判斷,Kotlin爲了解決這個問題,能夠把屬性myService聲明成能夠延遲初始化,使用 lateinit 修飾符
class MyService{ fun performAction(): String = "test" } class MyTest { // 使用 lateinit 聲明一個不須要初始化器的非空類型的屬性 // 延遲初始化的屬性都是var // 若是在屬性初始化以前就訪問了它,拋出異常:lateinit property myService has not been initialized private lateinit var myService: MyService @Before fun setup() { myService = MyService() } @Test fun testAction() { // 不須要 null 檢查直接訪問屬性 Assert.assertEquals("test", myService.performAction()) } }
爲可空類型定義擴展函數是一種更強大的處理null值的方式。Kotlin標準庫中定義的 String 的兩個擴展函數 isEmpty 和 isBlank 。函數isEmptyOrNull 和 isNullOrBlank 就能夠由 String? 類型的接受者調用
fun verifyUserInput(input: String?) { // 可空類型的值.可空類型的擴展 if (input.isNullOrBlank()){ // 不須要安全調用 println("input is null or blank.") } } >>> verifyUserInput(null) input is null or blank. // 函數isNullOrBlank實現,可空字符串的擴展 // return this == null || this.isBlank()
Kotlin中全部泛型類和泛型函數的類型參數默認都是可空的。任何類型,包括可空類型在內,均可以替換類型參數。使用類型參數做爲類型的聲明都容許爲 null,儘管類型參數T並無問號結尾
fun <T> printHashCode(t: T) { // 由於 t 可能爲null,因此必須使用安全調用,儘管沒有問號結尾,實參t仍容許持有null println(t?.hashCode()) } // T 被推導成 Any? >>> printHashCode(null) null
要使類型參數非空,必需要爲它指定一個非空的上界
// 如今 T 就不是可空的 fun <T: Any> printHashCode(t: T) { println(t.hashCode()) } // 編譯器不容許的,不能傳遞null,由於指望值是非空值 >>> printHashCode(null) Null can not be a value of a non-null type TypeVariable(T)
Java中使用註解表達可空性,如 @Nullable String 被Kotlin看成 String?,而 @NotNull String被Kotlin看成 String
Kotlin中的集合庫是已Java爲基礎構建的,並經過擴展函數增長的特性來加強它。
Kotlin支持類型參數的可空性。可是要當心決定什麼是可空的:集合的元素仍是集合自己
// List<Int?>能持有Int?類型值的列表,也就是說持有 Int 或者 null fun readNumbers(reader: BufferedReader): List<Int?> { // 建立包含可空Int值的列表 val result = ArrayList<Int?>() for (line in reader.lineSequence()) { println("line: $line") try { // 向列表添加非空值整數 val number = line.toInt() result.add(number) } catch (e: NumberFormatException) { // 解析失敗,向列表中添加null值 result.add(null) } } return result }
在使用可空值的集合時,須要使用null檢查
// List<Int?>? 聲明一個變量持有可空的列表,且包含空的數字 // List<Int?> 聲明一個變量不爲null的列表,且包含空的數字 fun addValidNumbers(numbers: List<Int?>) { var sumOfValidNumbers = 0 var invalidNumbers = 0 for (number in numbers) { // 檢查是否爲null if (number != null) { sumOfValidNumbers += number } else { invalidNumbers++ } } println("sum of valid numbers:$sumOfValidNumbers") println("Invalid numbers:$invalidNumbers") } // 可使用Kotlin提供的標準庫函數filterNotNull()來完成的,遍歷一個包含可空值的集合並過濾掉null // 可是filterNotNull()返回的集合類型,不會在包含任何爲null的元素,因此返回集合類型如:List<Int>
Kotlin中把訪問集合數據的接口和修改集合數據的接口分開了。通常規則:在代碼的任何地方都應該使用只讀接口,在代碼須要修改集合的地方使用可變接口的變體。 可是不能把只讀集合類型的變量賦值給可變的集合變量。
從kotlin.collections.Collection接口中能夠看出:能夠遍歷集合中的元素、獲取集合大小、判斷集合中是否包含某個元素。這個接口沒有任何添加或者移除元素的方法。
public interface Collection<out E> : Iterable<E> { public val size: Int public fun isEmpty(): Boolean public operator fun contains(element: @UnsafeVariance E): Boolean override fun iterator(): Iterator<E> public fun containsAll(elements: Collection<@UnsafeVariance E>): Boolean }
kotlin.collections.MutableCollection接口能夠修改集合中的數據。
public interface MutableCollection<E> : Collection<E>, MutableIterable<E> { override fun iterator(): MutableIterator<E> public fun add(element: E): Boolean public fun remove(element: E): Boolean public fun addAll(elements: Collection<E>): Boolean public fun removeAll(elements: Collection<E>): Boolean public fun retainAll(elements: Collection<E>): Boolean public fun clear(): Unit }
注意:只讀集合並不老是線程安全的。
集合建立函數
集合類型 | 只讀 | 可變 |
---|---|---|
List | listOf | mutableListOf、arrayListOf |
Set | setOf | mutableSetOf、 hashSetOf、linkedSetOf、sortedSetOf |
Map | mapOf | mutableMapOf、hashMapOf、linkedMapOf、sortedMapOf |
默認狀況下,應該優先使用集合,而不是數組。
Kotlin中數組是一個帶有類型參數的類,其元素類型被指定爲相應的類型參數
fun printArray(args: Array<String>) { // 使用擴展屬性args.indices,在下標的範圍內迭代 for (i in args.indices) { // 經過下標使用array[index]訪問元素 println("Argument $i is ${args[i]}") } }
在Kotlin中建立數組:
// 使用Array構造函數建立數組,能夠省略數組元素的類型 val letters = Array<String>(26) { // lambda表達式接收數組元素的下標並返回放在數組下標位置的值 i -> ('a' + i).toString() } println(letters.joinToString("")) //abcdefghijklmnopqrstuvwxyz
Kotlin最多見的建立數組的狀況之一是調用參數爲數組的Java方法,或者調用帶有vararg參數的Kotlin函數。
// 向vararg方法傳遞集合 val strings = listOf("a", "b", "c") // fun String.format(vararg args: Any?): String // 指望vararg參數時,使用展開運算符(*)傳遞數組 // 使用toTypedArray方法將集合轉換爲數組 println("%s/%s/%s".format(*strings.toTypedArray())) // a/b/c
Kotlin提供了若干獨立的類表示基本數據類型的數組,如:Int類型值的數組叫做IntArray,還提供ByteArray、CharArray、BooleanArray等。他們對應Java基本數據類型數組,如:int[]、byte[]、char[]。這些數組中值存儲時沒有裝箱,最高效。
在Kotlin建立基本數據類型的數組:
// 建立存儲5個0的整數數組 一、 val arr1 = IntArray(5) 二、 val arr2 = intArrayOf(0, 0, 0, 0, 0) 三、 val arr3 = IntArray(5) { i -> 0 } public fun intArrayOf(vararg elements: Int): IntArray public class IntArray(size: Int) { public inline constructor(size: Int, init: (Int) -> Int) }
若是個人文章對您有幫助,不妨點個贊鼓勵一下(^_^)