前言
2017年谷歌IO大會(huì)宣布,將Android
開發(fā)的官方語言更換為Kotlin
,作為Android
開發(fā)有必要對(duì)Kotlin
語言進(jìn)行了解使用犁罩。
Kotlin
語言提供了與java語言100%的互操作性插佛,將現(xiàn)代語言的優(yōu)勢(shì)帶入Android
開發(fā)的過程當(dāng)中,并引入了內(nèi)聯(lián)函數(shù)以及lambda
表達(dá)式等使得代碼更精簡(jiǎn)剑勾,運(yùn)行效率更高埃撵。
對(duì)于熟悉java開發(fā)的開發(fā)人員來說,kotlin
的學(xué)習(xí)成本會(huì)大大降低虽另,并且以及在很多大型互聯(lián)網(wǎng)公司進(jìn)入應(yīng)用階段暂刘。
1. 基礎(chǔ)類型
Kotlin
所支持的基礎(chǔ)數(shù)據(jù)類型包括:數(shù)字、字符捂刺、布爾谣拣、數(shù)組募寨、字符串。
1.1 數(shù)字
與java當(dāng)中的數(shù)字類型相似森缠,kotlin內(nèi)置的數(shù)字類型包括
類型 | 字節(jié) |
---|---|
Double | 64 |
Float | 32 |
Long | 64 |
Int | 32 |
Short | 16 |
Byte | 8 |
字面值常量包括
類型 | 表示 |
---|---|
十進(jìn)制 | 123 |
二進(jìn)制 | 0b00001011 |
十六進(jìn)制 | 0x0F |
對(duì)于各個(gè)類型之間的轉(zhuǎn)換與java有所不同拔鹰,在java當(dāng)中,當(dāng)小類型向大的類型賦值時(shí)會(huì)發(fā)生隱式轉(zhuǎn)換:
int a = 123;
long b = a;
System.out.println(a==b);//輸出true
但是在kotlin當(dāng)中不存在這種隱式轉(zhuǎn)換贵涵,在涉及到這種類型轉(zhuǎn)換的地方列肢,使用顯示轉(zhuǎn)換。
val a:Int = 123
val b:Long = a //報(bào)錯(cuò)
val b:Long = a.toLong()
1.2 字符
字符類型使用Char
類型標(biāo)識(shí)宾茂。
val c:Char = '0'
val str:String = c.toString()//Char類型轉(zhuǎn)換為String str = "0"
val i:Int = c.toInt()//Char類型轉(zhuǎn)換為Int,轉(zhuǎn)換為字符對(duì)應(yīng)Unicode編碼值 i=48
1.3 布爾
與java中的布爾類型相似瓷马,布爾類型只有兩個(gè)值true
或者false
,使用Boolean
類型表示:
val b1:Boolean = true
val b2:Boolean = false
1.4 數(shù)組
數(shù)組類型使用Array
來表示,系統(tǒng)提供了數(shù)字類型的get
和set
方法以及size
屬性等跨晴。創(chuàng)建一個(gè)數(shù)組的方式有兩種:
- 使用
arrayOf
欧聘、intArrayOf
、shortArrayOf
函數(shù)初始化數(shù)組坟奥。 - 使用
Array
的構(gòu)造函數(shù)初始化數(shù)組树瞭。
//方法1 使用`arrayOf`函數(shù)初始化數(shù)組
val array1 = arrayOf(1, 2, 3)
//遍歷數(shù)組的方法后續(xù)介紹
for(i in array1.indices){
println("array1[$i] = ${array1.get(i)}")
}
val array2:IntArray = intArrayOf(100,200,300)
for(i in array2.indices){
println("array2[$i] = ${array2.get(i)}")
}
//方法2 使用`Array`的構(gòu)造函數(shù)初始化數(shù)組
//Array(size: Int, init: (Int) -> T)
val array3 = Array(5) {
it*it
}
for(i in array3.indices){
println("array3[$i] = ${array3.get(i)}")
}
輸出結(jié)果
array1[0] = 1 array1[1] = 2 array1[2] = 3
array2[0] = 100 array2[1] = 200 array2[2] = 300
array3[0] = 0 array3[1] = 1 array3[2] = 4 array3[3] = 9 array3[4] = 16
1.5 字符串
字符串使用String
類型表示。與java中的字符串類似可以使用+
符號(hào)進(jìn)行字符串鏈接
val str = "aaaa"
println(str+"bbbb") //輸出aaaabbbb
對(duì)于字符串換行爱谁,kotlin
提供兩種方式
- 雙引號(hào)
"
和\n
的方式 - 三引號(hào)使用原始字符串
"""
val str = "aaaa\nbbbb"
val str2 = """aaaa
|bbbb
""".trimMargin() //trimMargin() 方法去除前導(dǎo)空格
//默認(rèn) | 用作邊界前綴晒喷,但你可以選擇其他字符并作為參數(shù)傳入,比如 trimMargin(">") 访敌。
//兩種方式輸出結(jié)果相同
aaaa
bbbb
kotlin
提供了字符串模板
凉敲,即字符串中含有少部分代碼。字符串模板的使用方式為$變量
或者${表達(dá)式}
的方式
val str = "aa"
println("變量 str 的值為$str,長(zhǎng)度為${str.length}")
//輸出結(jié)果
變量 str 的值為aa,長(zhǎng)度為2
2. 控制流
2.1 if
在kotlin
當(dāng)中寺旺,if是一個(gè)含有返回值的表達(dá)式爷抓,除了像java
當(dāng)中作為語句處理之外,在每個(gè)分支語塊下的最后的表達(dá)式將作為返回值返回阻塑。
//val代表不可變參數(shù)蓝撇,類似于java的final,var代表可變參數(shù)
var a = 1
if (a in 1..10) {
println("a is in 1..10")
} else {
println("unkonwn")
}
a = 15
//判斷a的范圍并返回
val result = if (a in 1..10) {
"a is in 1..10"
} else if (a in 11..20) {
"a is in 11..20"
} else {
"unkonwn"
}
println(result)
//結(jié)果輸出
a is in 1..10
a is in 11..20
2.2 when
when
表達(dá)式類似于java
中的switch操作符陈莽。與if
同樣渤昌,既可以作為語句也可以作為表達(dá)式返回值。
val i = 1
when(i){
1 -> println("branch 1")
2 -> println("branch 2")
3 -> println("branch 3")
4 -> println("branch 4")
else -> println("unknown")
}
val result = when(i){
1 ->"branch 1"
2 -> "branch 2"
3 -> "branch 3"
4 -> "branch 4"
else -> "unknown"
}
println("result is $result")
//輸出結(jié)果
branch 1
result is branch 1
相比于如果對(duì)于多種分支返回相同的結(jié)果走搁,可以集中處理独柑,并且可以使用表達(dá)式作為分支條件,而不是變量私植。
when(i){
//如果是1或者2
1,2 -> println("i is 1 or 2")
//如果i的取值在3--10
in 3..10 -> println("i is in 3--10")
//如果i是int類型
is Int -> println("i is Int")
else -> println("unknown")
}
2.3 for
for
循環(huán)可以對(duì)任何提供了迭代器的對(duì)象進(jìn)行遍歷忌栅。
遍歷一個(gè)數(shù)組
//使用索引遍歷數(shù)組
for(i in array1.indices){
println("array1[$i] = ${array1.get(i)}")
}
//使用庫函數(shù)遍歷
for ((index, value) in array1.withIndex()) {
println("the element at $index is $value")
}
遍歷數(shù)字區(qū)間
//遍歷1--3
for (i in 1..3) { println(i)
}
//遍歷6--0 步長(zhǎng)為2
for (i in 6 downTo 0 step 2) {
println(i) }
遍歷map集合
for((key,value) in values){
println("key is $key,value is $value")
}
2.4 while
while
語法的用法與java中基本相同
var index = 0
while (index < items.size) {
println("item at $index is ${items[index]}")
index++
}
2.5 標(biāo)簽
Kotlin
提供了三種標(biāo)簽:return
、break
和continue
曲稼,其作用方式與java中相同
- return:從包含它的函數(shù)中返回索绪。
- break:從包含它的循環(huán)中返回湖员。
- continue:繼續(xù)下一次循環(huán)。
除了正常類似java中的調(diào)用方式外者春,還可以配合標(biāo)簽使用破衔。標(biāo)簽的格式為標(biāo)識(shí)符后跟 @ 符號(hào)清女,例如:abc@ 钱烟、loop@。
代碼說明
fun main(args: Array<String>) {
var result = 0
loop@ for (i in 0..10) {
for (j in 0..10) {
if (i == 2 && j == 2) {
//如果不加標(biāo)簽跳出j的循環(huán)嫡丙,加標(biāo)簽后跳出loop指定循環(huán)
println("break to loop")
break@loop
}else{
result++
}
}
}
println("result is $result")
}
//結(jié)果輸出
break to loop
result is 24
使用標(biāo)簽跳出lambda表達(dá)式
fun foo() {
listOf(1, 2, 3, 4, 5).forEach {
// 在lambda表達(dá)式中不使用標(biāo)簽拴袭,直接退出到函數(shù)調(diào)用者,即退出最近的一個(gè)fun()
if (it == 3) return
print(it)
}
}
fun foo() {
listOf(1, 2, 3, 4, 5).forEach lit@{
// 使用標(biāo)簽可以退出當(dāng)前l(fā)ambda表達(dá)式曙博,如果不顯式添加標(biāo)簽拥刻,默認(rèn)標(biāo)簽與函數(shù)名相同
if (it == 3) return@lit
print(it)
}
}
也可以在標(biāo)簽處添加返回值如:return@loop 1
3. 類
在kotlin
當(dāng)中的類使用關(guān)鍵字class
聲明class A{}
,類中的成員包括:構(gòu)造函數(shù)和初始化塊
父泳、屬性
般哼、函數(shù)
、嵌套類和內(nèi)部類
以及對(duì)象聲明
惠窄。
3.1 構(gòu)造函數(shù)和初始化塊
kotlin
的構(gòu)造函數(shù)與java
不同蒸眠,分為主構(gòu)造函數(shù)和次構(gòu)造函數(shù),構(gòu)造函數(shù)使用關(guān)鍵字constructor
聲明杆融。主構(gòu)造函數(shù)內(nèi)不能有任何代碼楞卡,其聲明方式如下:
/**
* 在主構(gòu)造函數(shù)中初始化屬性,當(dāng)不聲明構(gòu)造函數(shù)時(shí)會(huì)默認(rèn)有一個(gè)無參構(gòu)造函數(shù)
* class ClassName public constructor()
*/
class Person constructor(firstName: String)
//也可以在構(gòu)造函數(shù)中對(duì)參數(shù)進(jìn)行賦值操作
class TestClass constructor(val key: String = "a", val value: String = "b"):ParentClass(key)
如果主構(gòu)造函數(shù)沒有任何注解或者可?性修飾符脾歇,可以省略這個(gè) constructor 關(guān)鍵字蒋腮。
class Person(firstName: String)
在類內(nèi)部可以聲明次構(gòu)造函數(shù)
,同樣使用constructor
關(guān)鍵字聲明藕各。
//每個(gè)次構(gòu)造函數(shù)都需要委托給主構(gòu)造函數(shù)池摧,委托方式有兩種
//1.直接委托給主構(gòu)造函數(shù)
constructor(key: String, value: String, param1: String) : this(key, value){
println("constructor 1")
}
//2. 通過其他次構(gòu)造函數(shù)委托
constructor(key: String, value: String, param1: String, param2: String) : this(key, value, param1) {
println("constructor 2")
}
構(gòu)造函數(shù)內(nèi)部不能包含代碼,類的初始化工作就放在了初始化代碼塊中激况。初始化代碼塊使用init
表示作彤。
//初始化代碼可以放在init關(guān)鍵字表示的代碼塊中處理
//在類對(duì)象初始化時(shí),與類屬性順序執(zhí)行
//實(shí)際上這部分代碼會(huì)成為主構(gòu)造函數(shù)的一部分,在次函數(shù)委托給主函數(shù)之后誉碴,與次函數(shù)第一行執(zhí)行這部分代碼
val classKey = "the Key is : $key".also(::println)
init {
println("first init code in this")
}
val classValue = "the value is : $value".also(::println)
init {
println("second init code in this")
}
3.2 接口
Kotlin
的接口使用interface
來表示宦棺。
interface Base {
val message: String
fun print()
}
/**
* 實(shí)現(xiàn)接口
*/
class BaseImpl(val x: Int) : Base {
override val message = "nnnn"
override fun print() {
println(message)
}
}
3.3 繼承
在Kotlin
中所有類都有一個(gè)共同的超類Any
,這對(duì)于沒有超類型聲明的類是默認(rèn)超類黔帕。如果想要顯式的聲明一個(gè)超類代咸,我們使用open
關(guān)鍵字。默認(rèn)情況下Kotlin
中的類都是不允許被繼承的成黄,只有用open
關(guān)鍵字聲明的類才允許被繼承呐芥。
/**
* open關(guān)鍵字與final關(guān)鍵字相反逻杖,它允許其他類對(duì)它進(jìn)行繼承。
* 默認(rèn)情況下Kotlin中的類都是public final類型
*/
open class ParentClass(key: String)
如果子類沒有主構(gòu)造函數(shù)思瘟,則子類的所有構(gòu)造函數(shù)都必須使用super
關(guān)鍵字初始化其基類型荸百,或委托給另一個(gè)構(gòu)造函數(shù)做到這一點(diǎn)。如果子類有主構(gòu)造函數(shù)滨攻,子類可以在繼承時(shí)直接使用父類主構(gòu)造函數(shù)初始化够话。
open class ParentClass(key: String) {
constructor(key: String, value: String) : this(key) {
println("parent constructror 1")
}
}
//當(dāng)子類沒有主構(gòu)造函數(shù)時(shí),可以在子類的構(gòu)造方法后繼承不同的父構(gòu)造方法
class Child: ParentClass {
constructor(key: String, value: String):super(key)
//委托給不同的父構(gòu)造函數(shù)
constructor(key: String, value: String, param1: String) : super(key, value) {
println("constructor 1")
}
}
//當(dāng)子類有構(gòu)造函數(shù)時(shí)光绕,需要繼承父類的構(gòu)造函數(shù)
class Child(key: String) : ParentClass(key) {
//子類有主構(gòu)造函數(shù)女嘲,委托給子類的主構(gòu)造函數(shù)進(jìn)行。
constructor(key: String, value: String):this(key)
constructor(key: String, value: String, param1: String) : this(key, value) {
println("constructor 1")
}
}
對(duì)于父類的屬性和方法诞帐,如果允許子類對(duì)其進(jìn)行覆蓋欣尼,則需要用open
關(guān)鍵字顯式標(biāo)注。在子類中使用override
關(guān)鍵字覆蓋停蕉。
open class ParentClass(key: String) {
/**
* 聲明為open關(guān)鍵字表示其可以被子類復(fù)寫愕鼓,如果不聲明open關(guān)鍵字,默認(rèn)為public final
* 使用var可以覆蓋val屬性慧起,反之則不可以菇晃,因?yàn)関al屬性默認(rèn)只有g(shù)etter方法,而var有g(shù)etter和setter方法完慧。
*/
open val a = "aaa".also {
println(it)
}
/**
* 聲明為open關(guān)鍵字表示其可以被子類復(fù)寫谋旦,如果不聲明open關(guān)鍵字,默認(rèn)為public final
*/
open fun method(param:String) {
println("......")
}
}
class Child : ParentClass {
override val a = "bbb".also { println(it) }
override fun method(param: String) {
super.method(param)
println("[[[[[[")
}
}
新建子類對(duì)象時(shí)屈尼,會(huì)先完成基類的初始化操作册着,然后在初始化子類。
在 Kotlin 中脾歧,實(shí)現(xiàn)繼承由下述規(guī)則規(guī)定:如果一個(gè)類從它的直接超類繼承相同成員的多個(gè)實(shí)現(xiàn)甲捏,它必須覆蓋這個(gè)成員并提供其自己的實(shí)現(xiàn)(也許用繼承來 的其中之一)。為了表示采用從哪個(gè)超類型繼承的實(shí)現(xiàn)鞭执,我們使用由尖括號(hào)中超類型名限定的 super司顿,如 super<Base> :
open class A {
open fun f() { print("A") } fun a() { print("a") }
}
interface B {
open fun f() { print("B") } // 接口成員默認(rèn)就是“open”的 fun b() { print("b") }
}
class C : A(),B {
// 編譯器要求覆蓋 f():
override fun f() {
super<A>.f() // 調(diào)用 A.f()
super<B>.f() // 調(diào)用 B.f()
}
}
3.4 伴生對(duì)象
在Kotlin
當(dāng)中并沒有靜態(tài)方法,也就沒有辦法使用單例等不需要?jiǎng)?chuàng)建類的實(shí)例兄纺,通過類名就可以訪問的函數(shù)大溜。這時(shí)候就用到了伴生對(duì)象。伴生對(duì)象用companion
關(guān)鍵字標(biāo)記估脆。
class InstanceClass {
companion object A{
fun create(): InstanceClass = InstanceClass()
}
}
fun main(args:Array<String>){
val instance = InstanceClass.create()
}
這里需要注意的是钦奋,這種用法看起來像是類似于Java中的靜態(tài)方法,實(shí)際上是相當(dāng)于聲明一個(gè)靜態(tài)內(nèi)部類A,然后調(diào)用靜態(tài)內(nèi)部類中的方法。
3.5 其他類
數(shù)據(jù)類
在Kotlin
中使用data
標(biāo)記的類為數(shù)據(jù)類付材。
data class User(val name: String, val age: Int)
為了確保生成的代碼的一致性和有意義的行為朦拖,數(shù)據(jù)類必須滿足以下要求:
- 主構(gòu)造函數(shù)需要至少有一個(gè)參數(shù);
- 主構(gòu)造函數(shù)的所有參數(shù)需要標(biāo)記為 val 或 var ;
- 數(shù)據(jù)類不能是抽象、開放厌衔、密封或者內(nèi)部的;
- (在1.1之前)數(shù)據(jù)類只能實(shí)現(xiàn)接口璧帝。
密封類
密封類用來表示受限的類繼承結(jié)構(gòu):當(dāng)一個(gè)值為有限集中的類型、而不能有任何其他類型時(shí)富寿。
要聲明一個(gè)密封類睬隶,需要在類名前面添加 sealed 修飾符。雖然密封類也可以有子類作喘,但是所有子類都必須在與密封類自身相同的文件中聲明理疙。
sealed class Expr
data class Const(val number: Double) : Expr()
data class Sum(val e1: Expr, val e2: Expr) : Expr()
object NotANumber : Expr()
//使用when區(qū)分不同的情況。
fun eval(expr: Expr): Double = when(expr) {
is Const -> expr.number
is Sum -> eval(expr.e1) + eval(expr.e2)
NotANumber -> Double.NaN
// 不再需要 `else` 子句泞坦,因?yàn)槲覀円呀?jīng)覆蓋了所有的情況
}
嵌套類和內(nèi)部類
一個(gè)類可以定義到另一個(gè)類當(dāng)中
class Outer {
private val bar: Int = 1
class Nested {
fun foo() = 2
}
}
val demo = Outer.Nested().foo() // == 2
使用inner
標(biāo)記為內(nèi)部類,并可以訪問外部類的成員砖顷。
class Outer {
private val bar: Int = 1
inner class Inner {
fun foo() = bar
}
}
val demo = Outer().Inner().foo() // == 1
匿名內(nèi)部類的實(shí)現(xiàn)采用對(duì)象表達(dá)式的方式
window.addMouseListener(object: MouseAdapter() {
override fun mouseClicked(e: MouseEvent) {
// ......
}
override fun mouseEntered(e: MouseEvent) { // ......
}
})
如果對(duì)象是函數(shù)式Java
接口(即具有單個(gè)抽象方法的Java
接口)的實(shí)例贰锁,你可以使用帶接口類型前綴的lambda表達(dá)式創(chuàng)建它:
val listener = ActionListener { println("clicked") }
枚舉類
kotlin
中的每一個(gè)枚舉常量都是一個(gè)對(duì)象,用逗號(hào)分隔
enum class Direction {
NORTH, SOUTH, WEST, EAST
}
//帶有初始化信息的枚舉類
enum class EnumClass(var x:Int,var y:Int){
NOUTH(0,0),SOUTH(90,90),WEST(180,180),EAST(270,270);
init {
print("init $this ")
x = x+1
y = -y
}
}
//調(diào)用枚舉類型
fun main(args:Array<String>){
//在初次調(diào)用枚舉類型時(shí)初始化所有枚舉常量
println(EnumClass.EAST)
println(EnumClass.NOUTH)
}
//輸出結(jié)果
init NOUTH init SOUTH init WEST init EAST EAST
NOUTH
3.6 擴(kuò)展
在java
當(dāng)中我們經(jīng)常會(huì)寫一些Utils
滤蝠,比如說StringUtils
等來做一些對(duì)String
的擴(kuò)展功能封裝豌熄。在Kotlin
中可以直接對(duì)類的功能進(jìn)行擴(kuò)展,而無需繼承該類物咳。
我們對(duì)MutableList<Int>
類進(jìn)行擴(kuò)展函數(shù)锣险。
//擴(kuò)展函數(shù),擴(kuò)展方法convert()實(shí)現(xiàn)對(duì)列表的倒置
fun MutableList<Int>.convert(){
for(i in 0..(this.size/2)){ // “this”對(duì)應(yīng)該列表
val tmp = this[i]
this[i] = this[this.size-1-i]
this[this.size-1-i] = tmp
}
}
//擴(kuò)展屬性览闰,獲取最后一個(gè)元素芯肤。
val <T> List<T>.lastData: T get() = this[this.size-1]
fun main(args: Array<String>) {
val list = mutableListOf(1, 2, 3, 4, 5)
println(list.lastData)
list.convert()
println(list)
}
//輸出結(jié)果
5
[5, 4, 3, 2, 1]
注意:當(dāng)擴(kuò)展函數(shù)與成員函數(shù)相同時(shí),在調(diào)用時(shí)總是取成員函數(shù)压鉴。
3.7 委托
委托是kotlin
實(shí)現(xiàn)的繼承的一種替代方式,委托的內(nèi)容通過by
關(guān)鍵字實(shí)現(xiàn)崖咨。
/**
* 定義Base接口
*/
interface Base {
val message: String
fun print()
}
/**
* 對(duì)Base接口的實(shí)現(xiàn)
*/
class BaseImpl(val x: Int) : Base {
override val message = "base message"
override fun print() {
println(message)
}
}
/**
* 委托給Base類的具體實(shí)現(xiàn)
*/
class Dervied(b: Base) : Base by b {
//調(diào)用委托對(duì)象b時(shí),不會(huì)訪問到
override val message = "dervied message"
}
fun main(args: Array<String>) {
val b = BaseImpl(1)
//因?yàn)閷rint()函數(shù)的處理委托給Base b來處理油吭,所以當(dāng)訪問不到Dervied()里面的內(nèi)容時(shí)
//直接執(zhí)行b的print方法击蹲,輸出`base message`
val a = Dervied(b)
a.print()
//如果有對(duì)b中的內(nèi)容進(jìn)行重寫,則優(yōu)先使用Dervied中的變量或方法
println(a.message)
}
//輸出結(jié)果
base message
dervied message
委托屬性
Kotlin
本身支持委托屬性婉宰,同樣通過by
關(guān)鍵字實(shí)現(xiàn)歌豺。
val str:String by Delegate()
實(shí)現(xiàn)委托后屬性對(duì)應(yīng)的get()
和 set()
會(huì)被委托給by后面的getValue()
和setValue()
方法,為此接收委托的類需要實(shí)現(xiàn)對(duì)應(yīng)的getValue()
和setValue()
方法.(val 可以只實(shí)現(xiàn)getValue方法心包,因?yàn)関al屬性是只讀的)
class Delegate {
operator fun getValue(thisRef: Any?, property: KProperty<*>): String {
return "$thisRef, thank you for delegating '${property.name}' to me!"
}
operator fun setValue(thisRef: Any?, property: KProperty<*>, value: String) {
println("$value has been assigned to '${property.name}' in $thisRef.")
}
//thisRef表示被委托的對(duì)象自身
//property保存被委托對(duì)象對(duì)于自身的描述
//value表示被賦予的新值类咧,因?yàn)閟tr是String類型,所以這里value只接收String類型
}
Kotlin
提供了幾種標(biāo)準(zhǔn)委托:延遲屬性(lazy)
、可觀察屬性(Observable)
.
延遲屬性(lazy)
接收一個(gè)lambda表達(dá)式作為參數(shù)轮听,第一次調(diào)用 get() 會(huì)執(zhí)行已傳遞給
lazy() 的 lambda 表達(dá)式并記錄結(jié)果骗露,后續(xù)調(diào)用 get() 只是返回記錄的結(jié)果。
//String為lazy后lambda表達(dá)式最后的值,在第一次調(diào)用到lazy變量時(shí)才初始化并調(diào)用lazy后lambda表達(dá)式血巍,之后調(diào)用則直接返回最后的值萧锉。
val lazy: String by lazy {
println("computed!")
"Hello"
}
fun main(args: Array<String>) {
println(a.lazy)
println(a.lazy)
}
//輸出結(jié)果
computed述寡!
Hello
Hello
可觀察屬性(Observable)
接受兩個(gè)參數(shù):初始值
和修改時(shí)處理程序
柿隙。每當(dāng)變量的值在發(fā)生改變時(shí)都會(huì)交給修改時(shí)處理程序
處理。
// Delegates.observable標(biāo)準(zhǔn)委托鲫凶,在修改屬性值時(shí)調(diào)用
// 接收兩個(gè)參數(shù)分別為初始化的值以及對(duì)修改時(shí)的監(jiān)聽(類似Android中的handler)
// 監(jiān)聽方法中的三個(gè)參數(shù)分別為修改的屬性禀崖、舊值、新值
var observable: String by Delegates.observable("initValue") { prop, old, new ->
println("$old ->> $new")
}
// Delegates.vetoable標(biāo)準(zhǔn)委托螟炫,在修改屬性值時(shí)調(diào)用
// 接收兩個(gè)參數(shù)分別為初始化的值以及對(duì)修改時(shí)的監(jiān)聽(類似Android中的handler)波附,并判斷是否對(duì)屬性的值進(jìn)行修改
// 返回true時(shí)對(duì)值進(jìn)行修改操作,返回false時(shí)不對(duì)值進(jìn)行修改
// 監(jiān)聽方法中的三個(gè)參數(shù)分別為修改的屬性昼钻、舊值掸屡、新值
var observable1: String by Delegates.vetoable("initValue") { prop, old, new ->
println("$old ->> $new")
new.equals("bb")
}
fun main(args: Array<String>) {
a.observable = "111"
a.observable = "222"
println(a.observable1)
a.observable1 = "bb"
println(a.observable1)
a.observable1 = "aa"
println(a.observable1)
}
//輸出結(jié)果
initValue ->> 111
111 ->> 222
initValue1
initValue1 ->> bb
bb
bb ->> aa
bb
4. 其他
命名參數(shù)
如果需要調(diào)用的方法有很多參數(shù),就可以使用命名參數(shù)的方式然评。
//函數(shù)定義
fun reformat(str: String,
normalizeCase: Boolean = true,
upperCaseFirstLetter: Boolean = true,
divideByCamelHumps: Boolean = false,
wordSeparator: Char = ' ') {
......
}
//調(diào)用時(shí)可以只修改必要的參數(shù)仅财,對(duì)于默認(rèn)參數(shù)不進(jìn)行修改的可以不須修改
reformat(str, wordSeparator = '_')
高階函數(shù)
Kotlin
支持將函數(shù)作為返回值或者函數(shù)使用,作為函數(shù)類型時(shí)碗淌,使用一個(gè)圓括號(hào)括起來的參數(shù)類型列表以及一個(gè)返回類型:(A, B) -> C 表示接受類型分別為 A 與 B 兩個(gè)參數(shù)并返回一個(gè) C 類 型值的函數(shù)類型盏求。
函數(shù)類型可以有一個(gè)額外的接收者類型,它在表示法中的點(diǎn)之前指定:類型 A.(B) -> C 表示可以在 A 的接收者對(duì)象上以一個(gè) B 類型參數(shù)來調(diào) 用并返回一個(gè) C 類型值的函數(shù)亿眠。帶有接收者的函數(shù)字面值通常與這些類型一起使用碎罚。
//帶接收者類型為String類型,Int為調(diào)用類型即times的類型
val repeat: String.(Int) -> String = { times -> repeat(times) }
//帶與不帶接收者的函數(shù)類型非字面值可以互換缕探,其中接收者可以替代第一個(gè)參數(shù)魂莫,反之亦然
val twoParameters: (String, Int) -> String = repeat
既然函數(shù)可以作為參數(shù)使用,那么帶有函數(shù)類型的函數(shù)被稱為高階函數(shù)爹耗。
//定義函數(shù)example耙考,它的參數(shù)是一個(gè)函數(shù)類型的參數(shù)
fun example(times: Int, computeFoo: () -> String) {
//example內(nèi)部執(zhí)行times次computeFoo函數(shù),并輸出執(zhí)行結(jié)果
var i = 0
while(i<times){
println("${computeFoo()}")
i++
}
}
fun main(agrs: Array<String>) {
//調(diào)用example方法
a.example(2) {
"aaa"
}
}
//輸出結(jié)果
aaa
aaa
內(nèi)聯(lián)函數(shù)
kotlin
引入了內(nèi)聯(lián)函數(shù),內(nèi)聯(lián)函數(shù)就是在程序編譯時(shí),編譯器將程序中出現(xiàn)的內(nèi)聯(lián)函數(shù)的調(diào)用表達(dá)式用內(nèi)聯(lián)函數(shù)的函數(shù)體來直接進(jìn)行替換眼溶,用以消除高階函數(shù)以及函數(shù)類型的不必要開銷毒费。
//定義內(nèi)聯(lián)函數(shù)synchronized用于實(shí)現(xiàn)同步屈呕,它接收兩個(gè)參數(shù)鎖Lock以及同步的內(nèi)容action()
inline fun <T> synchronized(lock:Lock,action:()->T):T{
lock.lock()
try {
return action()
}
finally {
lock.unlock()
}
}
val l=Lock()
//類似于java中的調(diào)用,使用synchronized同步代碼塊
synchronized(l){
...
}
inline
修飾符也可以標(biāo)注獨(dú)立的屬性訪問器
val foo: Foo
inline get() = Foo()
var bar: Bar get() = ......
inline set(v) { ...... }
解構(gòu)聲明
解構(gòu)聲明可以幫助我們把單一對(duì)象分解成多個(gè).
//定義Persion數(shù)據(jù)類以及其主構(gòu)造函數(shù)
//當(dāng)聲明為數(shù)據(jù)類時(shí)不需要提供主構(gòu)造函數(shù)的componentN方法
data class Person(var name:String,var age:Int){
var address = ""
//定義次構(gòu)造函數(shù)
constructor(name:String,age:Int,address:String):this(name,age){
this.address = address
}
//定義componentN方法用于解構(gòu)聲明
operator fun component3():String{
return this.address
}
}
fun main(args:Array<String>){
//聲明Person類
val person = Person("小明",18,"北京市朝陽區(qū)")
//解構(gòu)聲明調(diào)用
//結(jié)構(gòu)聲明在編譯時(shí)會(huì)分解成
//String name = person.component1();
//int age = person.component2();
//String address = person.component3();三個(gè)步驟來做薛夜,所以需要在類中提供這三個(gè)方法
val (name,age,address) = person
}
解構(gòu)聲明也可以在for循環(huán)中使用
//map使用解構(gòu)聲明
var map = HashMap<String, Person>()
map.put("1", personA)
map.put("2", personB)
map.put("3", personC)
map.put("4", personD)
map.put("5", personE)
for ((key, value) in map) {
println("key: $key, value: $value")
}
//對(duì)象使用解構(gòu)聲明
var personA: Person = Person("Door", 22, "ShanDong")
var personB: Person = Person("Green", 30, "BeiJing")
var personC: Person = Person("Dark", 23, "YunNan")
var personD: Person = Person("Tool", 26, "GuanDong")
var personE: Person = Person("Mark", 24, "TianJin")
var pers = listOf(personA, personB, personC, personD, personE)
for ((name, age) in pers) {
println("name: $name, age: $age")
}
This表達(dá)式
為了表示當(dāng)前的 接收者 我們使用 this 表達(dá)式:
- 在類的成員中袱蚓,this 指的是該類的當(dāng)前對(duì)象枚碗。
- 在擴(kuò)展函數(shù)或者帶接收者的函數(shù)字面值中逾一,this 表示在點(diǎn)左側(cè)傳遞的接收者參數(shù)。
要訪問來自外部作用域的this(一個(gè)類 或者擴(kuò)展函數(shù)肮雨,或者帶標(biāo)簽的帶接收者的函數(shù)字面值)我們使用 this@label 遵堵,其中 @label 是一個(gè)代指 this 來源的標(biāo)簽:
class A { // 隱式標(biāo)簽 @A
inner class B { // 隱式標(biāo)簽 @B
fun Int.foo() { // 隱式標(biāo)簽 @foo val a = this@A // A 的 this val b = this@B // B 的 this
val c = this // foo() 的接收者,一個(gè) Int
val c1 = this@foo // foo() 的接收者怨规,一個(gè) Int
val funLit = lambda@ fun String.() {
val d = this // funLit 的接收者
}
val funLit2 = { s: String ->
// foo() 的接收者陌宿,因?yàn)樗?lambda 表達(dá)式 // 沒有任何接收者
val d1 = this
}
}
}
}
結(jié)尾
本篇內(nèi)容只是簡(jiǎn)單介紹了kotlin
的一些常規(guī)用法,借鑒于kotlin中文文檔
波丰,如果需要系統(tǒng)的學(xué)習(xí)這門語言壳坪,建議閱讀官方文檔。