1. Kotlin介紹
- 基于JVM??梢耘cJava進(jìn)行混合開(kāi)發(fā)和相互調(diào)用。
- 由JetBrains開(kāi)發(fā)。系出名門,也得到其IDE的良好支持。
- Android官方支持開(kāi)發(fā)語(yǔ)言。
- 更簡(jiǎn)潔,擁有各種語(yǔ)法糖。
- 更安全,對(duì)空安全等進(jìn)行了優(yōu)化處理。
2. Kotlin環(huán)境
# 項(xiàng)目build.gradle
buildscript {
ext.kotlin_version = '1.1.4-3'
repositories {
mavenCentral()
}
dependencies {
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
}
}
# 模塊build.gradle
apply plugin: 'kotlin-android'
apply plugin: 'kotlin-android-extensions'
dependencies {
compile "org.jetbrains.kotlin:kotlin-stdlib-jre7:$kotlin_version"
}
3. kotlin-android-extensions
和ButterKnife說(shuō)再見(jiàn)
- 當(dāng)然不用findViewById
- 也不用定義空間變量
- 甚至不用ButterKnife.bind
<Button
android:id="@+id/close_button"
android:text="關(guān)閉"
android:layout_alignParentEnd="true"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
import kotlinx.android.synthetic.main.main_activity.view.*
class MyActivity : Activity() {
override fun onCreate(savedInstanceState: Bundle?) {
setContentView(R.layout.main_activity)
close_button.setOnClickListener { mPresenter?.exit() }
}
}
4. Anko
- Anko Commons: 一個(gè)輕量級(jí)的Helper用于處理 intents, toast, dialogs, logging等等;
- Anko Layouts: 一種可輕量級(jí),可快速開(kāi)發(fā)的動(dòng)態(tài)Android layouts。用代碼寫(xiě)layout而不是xml;
- Anko SQLite: a query DSL and parser collection for Android SQLite;
- Anko Coroutines: utilities based on the kotlinx.coroutines library.
以Anko Layouts和Anko Commons舉例
在onCreate中使用以下代碼可以同時(shí)實(shí)現(xiàn)layout和setContentView
verticalLayout {
val name = editText()
button("Say Hello") {
onClick { toast("Hello, ${name.text}!") }
}
}

image
5. 數(shù)據(jù)類型與等式
首先,Kotlin沒(méi)有基本數(shù)據(jù)類型,所有變量包括所謂的基本數(shù)據(jù)類型在內(nèi),都是對(duì)象。
其次,Kotlin中有兩種相等:
- 參照相等:指向同一個(gè)對(duì)象 使用 === !==判斷
- 結(jié)構(gòu)相等:使用== !=判斷,相當(dāng)于 a?.equals(b) ?: b === null 可見(jiàn)
裝箱:可空標(biāo)識(shí)(比如說(shuō) Int?) 或者涉及泛型,數(shù)值都會(huì)被裝箱。裝箱過(guò)的數(shù)值已經(jīng)不保留先前的引用關(guān)系
val a: Int = 10000
print (a === a ) //打印 'true'
val boxedA: Int? =a
val anotherBoxedA: Int? = a
print (boxedA === anotherBoxedA ) //注意這里打印的是 'false'
print (boxedA == anotherBoxedA) // Prints 'true'
6. collection 和 字符串
kotlin-stdlib / kotlin.collections
- Array:用于取代Java的數(shù)組。
- List:用于取代Java的List
- Map: 用于取代Java的Map
// 通過(guò)指定Array大小并提供一個(gè)迭代器
// 創(chuàng)建一個(gè) Array<String> 內(nèi)容為 ["0", "1", "4", "9", "16"]
val asc = Array(5, {i -> (i * i).toString() })
val nullArray : Array<Int?> = arrayOfNulls(3) // 創(chuàng)建一個(gè)指定大小的空Array
val list = listOf("a", "b", "c")
val x: IntArray = intArrayOf(1, 2, 3)
val map = mapOf("a" to 1, "b" to 2, "c" to 3)
Kotlin 有專門的類來(lái)表示原始類型從而避免過(guò)度裝箱: ByteArray, ShortArray, IntArray 等等。這些類與 Array 沒(méi)有繼承關(guān)系,但它們有一樣的方法與屬性。
String
// 包含轉(zhuǎn)義符的字符串
val s = "Hello World!\n"
// 不包含轉(zhuǎn)義符的字符串,但不能包含3個(gè)及以上的"
val text = """
for (c in "foo")
print(c)
"""
// 模板
val s = "abc"
val str = "$s.length is ${s.length}" //識(shí)別為 "abc.length is 3"
8. Ranges
- 范圍主要用于 if in (范圍) 和 for in (級(jí)數(shù)) 中使用,判斷變量是否處于某個(gè)范圍或遍歷某個(gè)范圍
- 使用 .. 操作符來(lái)或者一些特有操作符來(lái)定義
// if in
if (i in 1..10) println(i)
if (x !in 1.0..3.0) println(x)
if (str in "island".."isle") println(str)
// for in
for (i in 1..4) print(i) // prints "1234"
for (i in 4..1) print(i) // prints nothing
for (x in 1.0..2.0) print("$x ") // prints "1.0 2.0 "
for (str in "island".."isle") println(str) // error: string range cannot be iterated over
// 特有操作符
// rangeTo
// 與 .. 相同
// downTo
for (i in 4 downTo 1) print(i)
// step 必須大于0
for (i in 1.0..2.0 step 0.3) print("$i ") // prints "1.0 1.3 1.6 1.9 "
// reversed
for (i in (1..4).reversed()) print(i) // prints "4321"
范圍和技術(shù)主要通過(guò)以下兩個(gè)接口實(shí)現(xiàn)
interface Range<T : Comparable<T>> {
val start: T
val end: T
fun contains(Element : T): Boolean
}
interface Progression<N : Number> : Iterable<N> {
val start : N
val end : N
val increment : Number
}
// if increment > 0
for (int i = start; i <= end; i += increment) {
// ...
}
// if increment < 0
for (int i = start; i >= end; i += increment) {
// ...
}
9. 空安全和類型轉(zhuǎn)換安全
var a: String ="abc"
a = null //編譯錯(cuò)誤
var b: String? = "abc"
b = null
val l = b.length() //錯(cuò)誤:b 不可為空
val l = if (b != null) b.length() else -1 // 類型經(jīng)過(guò)檢查后可以安全飲用
val l = b.length() ?: -1 // = ?: 空安全三元操作符
val name = node.getName() ?: throw IllegalArgumentException("name expected") // Throw return
// 安全調(diào)用 避免NullPointerException
b?.length()
val l = b !!.length()
// 不安全轉(zhuǎn)換 有可能拋出ClassCastException
val x: String = y as String
// 安全轉(zhuǎn)換 避免ClassCastException
val aInt: Int? = a as? Int
10. 流程控制
val max = if (a > b){
print("Choose a")
a
}
else{
print("Choose b")
b
}
// 用于代替switch if-else的when
when (x) {
1 -> print("x == 1")
2 -> print("x == 2")
else -> { //Note the block
print("x is neither 1 nor 2")
}
}
// 當(dāng)有兩個(gè)case使用同樣方法時(shí)
when (x) {
0,1 -> print("x == 0 or x == 1")
else -> print("otherwise")
}
// 用表達(dá)式作為case
when (x) {
parseInt(s) -> print("s encode x")
else -> print("s does not encode x")
}
// 用范圍作為case
when (x) {
in 1..10 -> print("x is in the range")
in validNumbers -> print("x is valid")
!in 10..20 -> print("x is outside the range")
else -> print("none of the above")
}
// 類型檢查作為case
val hasPrefix = when (x) {
is String -> x.startsWith("prefix")
else -> false
}
// return 控制打斷范圍
foo outer() {
foo inner() {
return@outer
}
}
11. 函數(shù)
使用中綴符號(hào)調(diào)用函數(shù) 成員函數(shù)和擴(kuò)展函數(shù)都適用
//給 Int 定義一個(gè)擴(kuò)展方法
infix fun Int.shl(x: Int): Int {
...
}
1 shl 2 //用中綴注解調(diào)用擴(kuò)展函數(shù),與下一句等效
1.shl(2)
使用默認(rèn)參數(shù)減少重載
可以命名參數(shù),當(dāng)參數(shù)較多時(shí)更方便設(shè)置參數(shù),可讀性更強(qiáng)
fun reformat(str: String, normalizeCase: Boolean = true,upperCaseFirstLetter: Boolean = true,
divideByCamelHumps: Boolean = false,
wordSeparator: Char = ' ') {
...
}
reformat(str, true, true, false, '_')
reformat(str, wordSeparator = '_')
12. 高階函數(shù)與lambda
高階函數(shù)就是可以接受函數(shù)作為參數(shù)并返回一個(gè)函數(shù)的函數(shù)。
fun lock<T>(lock: Lock, body: () -> T ) : T {
lock.lock()
try {
return body()
}
finally {
lock.unlock()
}
}
// 函數(shù)作為參數(shù)傳入
fun toBeSynchroized() = sharedResource.operation()
val result = lock(lock, ::toBeSynchroized)
// lambda表達(dá)式傳入
val result = lock(lock, { sharedResource.operation() })
- lambda中,如果參數(shù)只有一個(gè),可以不寫(xiě)出來(lái),在函數(shù)體中用it來(lái)表示
- 如果函數(shù)的參數(shù)只有一個(gè)函數(shù),則括號(hào)可以不寫(xiě),直接寫(xiě)lambda
fun <T, R> List<T>.map(transform: (T) -> R): List<R> {
val result = arrayListOf<R>()
for (item in this)
result.add(transform(item))
return result
}
// 調(diào)用
val doubled = ints.map {it -> it * 2}
// 簡(jiǎn)化
val doubled = ints.map { it * 2 }
此外,字面函數(shù)或函數(shù)擴(kuò)展還可以作為屬性變量而存在
val sum = {x: Int,y: Int -> x + y}
// 以下為完全寫(xiě)法
val sum: (Int, Int) -> Int = {x, y -> x+y }
// 函數(shù)擴(kuò)展
val sum = fun Int.(other: Int): Int = this + other
// 函數(shù)擴(kuò)展在參數(shù)中的聲明如下
sum : Int.(other: Int) -> Int
13. 標(biāo)準(zhǔn)庫(kù)
類似JDK提供的一系列標(biāo)準(zhǔn)庫(kù),Kotlin也提供了以下標(biāo)準(zhǔn)庫(kù),可供參考
| 包名 | 信息 |
|---|---|
| kotlin | 核心函數(shù)和類型,在所有支持平臺(tái)均可用 |
| kotlin.browser | 訪問(wèn)并操作瀏覽器 DOM 的 API |
| kotlin.concurrent | 并發(fā)編程的函數(shù)集合 |
| kotlin.dom | 用在 W3C DOM 的函數(shù) |
| kotlin.io | 用于文件和流的 IO API |
| kotlin.jvm | 專門用于 java 平臺(tái)的函數(shù)和注解 |
| kotlin.math | 數(shù)學(xué)反面的 API |
| kotlin.platform | 用于自定義 Kotlin 編譯器生成的代碼,使其在目標(biāo)平臺(tái)上更好的交互 |
| kotlin.properties | 實(shí)現(xiàn)泛型和泛型屬性的標(biāo)準(zhǔn)并幫助函數(shù)實(shí)現(xiàn)自定義泛型 |
| kotlin.reflect | Kotlin reflection 運(yùn)行時(shí) API |
| kotlin.reflect.jvm | 用于 Kotlin reflection 和 java 交互的運(yùn)行時(shí) API |
| kotlin.support | |
| kotlin.test | 寫(xiě)測(cè)試用例的 API |
| kotlin.text | 用于正則表達(dá)式和文字的函數(shù) |
| kotlin.util | 實(shí)用函數(shù) |
14. 尚未談及
本期文章中還有些尚未提及的知識(shí)點(diǎn),如有興趣還可以自行查閱
- 動(dòng)態(tài)類型
- 反射
- 注解
- 運(yùn)算符重載
- 內(nèi)聯(lián)函數(shù)