Kotlin實(shí)戰(zhàn)開發(fā)總結(jié)

本文的demo地址(https://github.com/dingjinwen/kotlin_tips

Tip1-Kotlin和Java相互查看

1. Kotlin轉(zhuǎn)化為Java代碼查看

kotlin文件是不能直接轉(zhuǎn)化為Java文件的抒和,但是可以將Kotlin代碼轉(zhuǎn)化成Java語言去理解歉眷,步驟如下:在Android Studio中選擇Tools ---> Kotlin ---> Show Kotlin Bytecode 這樣就把Kotlin轉(zhuǎn)化為Class字節(jié)碼了,Class字節(jié)碼閱讀不太友好,點(diǎn)擊左上角的Decompile就轉(zhuǎn)化為Java

2. Java文件直接轉(zhuǎn)化為Kotlin文件

再介紹一個(gè)小竅門,在前期對Kotlin語法不熟悉的時(shí)候,可以先用Java寫好代碼,再利用AndroidStudio工具將Java代碼轉(zhuǎn)化為Kotlin代碼,步驟如下:在Android Studio中選中要轉(zhuǎn)換的Java代碼 ---> 選擇Code ---> Convert Java File to Kotlin File

Tip2-屬性

1. 什么時(shí)候?qū)傩灶愋筒荒苁÷裕?/h3>
a. 屬性的修飾符储狭,和方法類似的,屬性也有多種修飾符(public,protected,private)捣郊。
如果可以根據(jù)屬性的值推斷出屬性類型辽狈,則可省略類型,比如:
var aa: String = ""

b. 不能根據(jù)屬性的值推斷出屬性類型,則不可省略類型,比如:
abstract修飾的屬性,自身不能初始化模她,要在子類進(jìn)行初始化稻艰,不能省略類型
lateinit延遲初始化的,在使用之前再初始化的侈净,不能省略類型

2. 屬性的組成部分

對屬性的訪問尊勿,并不是像Java里面一樣僧凤,直接訪問屬性的本身,而是默認(rèn)調(diào)用了 get 和 set 方法元扔,保證了屬性的閉合性. 一般屬性包含三個(gè)部分躯保,set , get 和 backing filed.

var aa: String = "" 
    set(value) {
         field = value //field是(Backing field)幕后字段
    }
    get() {
        return field
    }

3. 自定義set 和 get 方法

var,val修飾的變量默認(rèn)是 public 的,編譯器會自動(dòng)生成 set 和 get 方法,也可以手動(dòng)重寫他的 set 和 get 方法,val只有 get 方法

var aa: String = "ding jin wen " 
    set(value) {
        field = value + "  123  "
    }
    get() {
        return field + "大地零一"
    }

    Log.d("text", "aa : $aa")
    aa = "zhang san"
    Log.d("text", "aa : $aa")

    //輸出
    aa:ding jin wen 大地零一
    aa:zhang san 123 大地零一

4. 幕后字段(Backing Field)

kotlin的 get和 set 是不允許訪問本身的局部變量的澎语,因?yàn)閷傩缘恼{(diào)用也是對get的調(diào)用途事,因此會產(chǎn)生遞歸,造成內(nèi)存溢出擅羞。


屬性1.jpg

5. 延遲初始化lateinit 和 懶初始化by lazy

lateinit var aa:String 

a.lateinit只能用于var聲明的類變量尸变,并且屬性沒有自定義get或set方法。

b.屬性的類型必須是非空的
val aa:String by lazy { }

a.lazy只能作用在val屬性,應(yīng)用于單例模式,當(dāng)且僅當(dāng)屬性被第一次調(diào)用的時(shí)候,委托方法才會執(zhí)行减俏。

b.lazy()是接受一個(gè) lambda 并返回一個(gè) Lazy <T> 實(shí)例的函數(shù),返回的實(shí)例可以作為實(shí)現(xiàn)延遲屬性的委托, 第一次調(diào)用 get() 會執(zhí)行已傳遞給 lazy() 的 lambda 表達(dá)式并記錄結(jié)果,后續(xù)調(diào)用 get() 只是返回記錄的結(jié)果召烂。
屬性2.jpg

6. 編譯器常數(shù)值

如果在編譯期間,屬性值就能被確定娃承,該類屬性值使用const 修飾符奏夫,類似Java里面的靜態(tài)常量用法。 這類屬性必須滿足以下條件:
a. 必須是頂級屬性历筝,或者是一個(gè)object的成員
b. 值被初始化為 String 類型酗昼,或基本類型(primitive type)
c. 只能修飾val常量
d. 不存在自定義的取值方法

const val key = "key"
object Config {
    const val name="name"
}

7. 委托屬性

有一種屬性,在使用的時(shí)候每次都要手動(dòng)實(shí)現(xiàn)它梳猪,但是可以做到只實(shí)現(xiàn)一次麻削,并且放到庫中,一直使用春弥,這種屬性稱為委托屬性碟婆。

委托屬性包括:
a. 延遲屬性(lazy properties):上面第5點(diǎn)中已經(jīng)提到了。
b. 可觀察屬性(observable properties):監(jiān)聽得到屬性變化通知惕稻。

//看下面例子,快速雙擊退出頁面
var mBackPressedTime by Delegates.observable(0L) {
    prop, old, new ->//三個(gè)參數(shù)蝙叛,分別是:被賦值的屬性俺祠,舊值和新值。
        if (new - old > 1000) {
            Toast("再按一次返回就退出")
        }
        if (new - old in 1..1000) {
            finish()
        }
    }

override fun onBackPressed() {
    mBackPressedTime = System.currentTimeMillis()
}

c. Map委托屬性(Storing Properties in a Map):將所有屬性存在Map中借帘。

class Person2(private val maps: Map<String, String>) {
        val name: String by  maps
        val company: String by  maps
        val address: String by  maps
        val email: String by  maps
    }

    fun main(args: Array<String>) {
        val data = mapOf("name" to "Jack", "company" to "JetBrains")
        val p = Person2(data)
        println(p.name) // Jack
    }

Tip3-函數(shù)

1. 變參函數(shù)

//在Java中蜘渣,我們這么表示一個(gè)變長函數(shù)
public boolean hasEmpty(String... strArray){
    for (String str : strArray){
        if ("".equals(str) || str == null)
            return true;
    }
    return false;
}

//在Kotlin中,使用關(guān)鍵字vararg來表示
fun hasEmpty(vararg strArray: String?): Boolean{
    for (str in strArray){
        if (str.isNullOrEmpty())
            return true
    }
    return false
}

2. 擴(kuò)展函數(shù)

  • 聲明一個(gè)擴(kuò)展函數(shù)肺然,我們需要在函數(shù)的名稱前加上一個(gè)接收者類型并且加上.符號,在擴(kuò)展函數(shù)中的this關(guān)鍵字表示接收者對象
fun Activity.toast(message: CharSequence, duration: Int = Toast.LENGTH_SHORT) {
    Toast.makeText(this, message, duration).show()
}
  • 擴(kuò)展是靜態(tài)解析
open class Animal

class Dog : Animal()

fun Animal.bark() = "animal"

fun Dog.bark() = "dog"

fun printBark(animal: Animal) {
    println(animal.bark())
}

printBark(Animal()) 和 printBark(Dog())打印的都是 animal ,因?yàn)樵诙x擴(kuò)展函數(shù)的時(shí)候接收對象類型是Animal類蔫缸,而Kotlin的擴(kuò)展是靜態(tài)解析的,所以即使調(diào)用的時(shí)候是傳了Animal類的子類Dog類進(jìn)去际起,還是會執(zhí)行定義的時(shí)候的類型的函數(shù)拾碌。

3. 構(gòu)造函數(shù)

kotlin里面的構(gòu)造函數(shù)吐葱,分為主構(gòu)造函數(shù)和次構(gòu)造函數(shù),主構(gòu)造函數(shù)寫在類頭中,跟在類名后面,次構(gòu)造函數(shù)寫在類體中,關(guān)鍵字是constructor,次構(gòu)造函數(shù)必須直接或者間接地委托到主構(gòu)造函數(shù)

class Student(private var name: String) {
    private var age: Int? = null
    private var classId: Int? = null

    constructor(name: String, age: Int) : this(name) {
        this.age = age
        this.name = name
    }

    constructor(classId: Int, name: String, age: Int) : this(name, age) {
        this.classId = classId
        this.age = age
        this.name = name
    }

    fun sayHello() {
        Log.e("test", "hello $name")
        Log.e("test", "hello $classId")
        Log.e("test", "hello $age")
    }
}

btn.setOnClickListener {
    Student(1, "zhang san", 20).sayHello()
    // 打印test: hello zhang san
    // 打印test: hello 1
    // 打印test: hello 20
}

4. 單例怎么寫校翔?

  • 不帶參數(shù)(兩種寫法)
  1. 伴生對象更多的用途是用來創(chuàng)建一個(gè)單例類弟跑。只是簡單的寫,直接用伴生對象返回一個(gè)val修飾的外部類對象就可以了
class Single private constructor() {
    companion object {
        val instance = Single()
    }
}
  1. 但是更多的時(shí)候我們希望在類被調(diào)用的時(shí)候才去初始化他的對象防症。以下代碼將線程安全問題交給虛擬機(jī)在靜態(tài)內(nèi)部類加載時(shí)處理孟辑,是一種推薦的寫法:
class Single private constructor() {
    companion object {
        val instance: Single by lazy { Holder.INSTANCE }
    }

    private object Holder {
        val INSTANCE = Single()
    }
}
  • 帶參數(shù)
/**
* 簡單寫法
*/
class Single private constructor(name:String) {
    companion object {
        @Volatile
        private val instance:Single?=null
        fun getInstance(c:String):Single{
            if (instance == null) {
                synchronized(Single::class) {
                    if (instance == null) {
                        instance = Singleton(c)
                    }
                }
            }
            return instance!!
        }
    }
}

/**
 * 帶參數(shù)的單例,去掉斷言蔫敲,google推薦的寫法
 */
class SingleInstance private constructor(name: String) {
    companion object {
        @Volatile
        private var instance: SingleInstance? = null

        fun getInstance(name: String): SingleInstance {
            return instance ?: synchronized(this) {
                instance ?: SingleInstance(name).also {
                    instance = it
                }
            }
        }
    }
}

5. 靜態(tài)方法

Kotlin 沒有靜態(tài)方法,在項(xiàng)目中常用的兩種方法:

  • 用@jvmStatic注解,和Java的static類似
object StringUtils {
    @JvmStatic fun isEmpty(str: String): Boolean {
        return "" == str
    }
}
  • 使用伴生對象
class StringUtils {
    companion object {
    fun isEmpty(str: String): Boolean {
            return "" == str
        }
    }
}

6. let,apply,with,run,also的用法和區(qū)別

先看下面這個(gè)例子饲嗽,打印字母表函數(shù)

/*
*打印字母表函數(shù),在函數(shù)內(nèi)result變量在好幾處有使用到
*/
fun alphabet(): String {
    val result = StringBuilder()
    result.append("START\n")
    for (letter in 'A'..'Z') {
        result.append(letter)
    }
    result.append("\nEND")
    return result.toString()
}

在上面的函數(shù)中奈嘿,result變量出現(xiàn)了5次貌虾,下面分別用apply,with,let來簡化這個(gè)函數(shù),可以將這5次都不用再出現(xiàn)了

  • apply用法:
/**
* 打印字母表函數(shù)指么,apply函數(shù)酝惧,調(diào)用某對象的apply函數(shù),在函數(shù)范圍內(nèi)伯诬,可以任意調(diào)用該對象的任意方法晚唇,并返回該對象
*/
private fun alphabetApply(): String {
    // fun <T> T.apply(f: T.() -> Unit): T { f(); return this }
    return StringBuilder().apply {
        append("START\n")
        for (letter in 'A'..'Z') {
            append(letter)
        }
        append("\nEND")
    }.toString()
}

btn.setOnClickListener {   
    Log.d("test", StringUtils.alphabetApply())
}
//打印 
//START
//ABCDEFGHIJKLMNOPQRSTUVWXYZ
//END
  • with用法:
/**
* 打印字母表函數(shù),with函數(shù)是一個(gè)單獨(dú)的函數(shù)盗似,并不是Kotlin中的extension哩陕,所以調(diào)用方式有點(diǎn)不一樣,返回是最后一行
*/
private fun alphabetWith(): String {
    // fun <T, R> with(receiver: T, f: T.() -> R): R = receiver.f()
    return with(StringBuilder()) {
        append("START\n")
        for (letter in 'A'..'Z') {
            append(letter)
        }
        append("\nEND")
        toString()
    }
}

btn.setOnClickListener {   
    Log.d("test", StringUtils.alphabetWith())
}
//打印 
//START
//ABCDEFGHIJKLMNOPQRSTUVWXYZ
//END
  • let用法:
/**
 * 打印字母表函數(shù)赫舒,let函數(shù)悍及,默認(rèn)當(dāng)前這個(gè)對象作為閉包的it參數(shù),返回值是函數(shù)里面最后一行接癌,或者指定return
 * XX?.let{}這種寫法在代碼中心赶,比較常見
 * XX?:let{}
 */
private fun alphabetLet(): String {
    // fun <T, R> T.let(f: (T) -> R): R { f(this)}
    return StringBuilder().let {
        it.append("START\n")
        for (letter in 'A'..'Z') {
            it.append(letter)
        }
        it.append("\nEND")
        it.toString()
        //  return it.toString()
    }
}
btn.setOnClickListener {   
    Log.d("test", StringUtils.alphabetLet())
}
//打印 
//START
//ABCDEFGHIJKLMNOPQRSTUVWXYZ
//END
  • also用法:
/**
* also()函數(shù)和let()函數(shù)很像,但是返回值為該對象自己
* 字面理解:做缺猛。缨叫。。的同時(shí)也做荔燎。耻姥。。
*/
fun testAlso() {
    // fun <T> T.also(block: (T) -> Unit): T { block(this); return this }
    "testAlso".apply {
        println("this = " + this)
    }.also { println(it) }
}

btn.setOnClickListener {   
    Log.d("test", StringUtils.testAlso())
}
//打印 
//System.out: this = testAlso
//System.out: testAlso
  • run用法:
/**
* run函數(shù)和apply函數(shù)很像有咨,只不過run函數(shù)是使用最后一行的返回琐簇,apply返回當(dāng)前自己的對象。
*/
fun testRun() {
    // fun <T, R> T.run(f: T.() -> R): R = f()
    "testRun".run {
        println("this = " + this)
    }.let { println(it) }
}
btn.setOnClickListener {   
    Log.d("test", StringUtils.testRun())
}
//打印 
//System.out: this = testRun
//System.out: kotlin.Unit

8.中綴函數(shù)

中綴表達(dá)式是操作符以中綴形式處于操作數(shù)的中間(例:3 + 4)座享,先來看一下Kotlin中的中綴函數(shù):

  • mapOf()方法中的to就是個(gè)中綴函數(shù)
// public infix fun <A,B> A.to(that:B):Pair<A,B> = Pair(this,that)
val map: Map<Int,Int> = mapOf(1 to 1,2 to 2)
  • Range里面的downTo也是個(gè)中綴函數(shù):
(10 downTo 1).forEach{print(it)}

使用中綴符號infix修飾函數(shù)婉商,但必須符合一些條件:

  • 使用infix關(guān)鍵字表示
  • 必須是成員方法或者擴(kuò)展函數(shù)
  • 函數(shù)只有一個(gè)參數(shù)

下面來寫個(gè)中綴函數(shù):

// 定義擴(kuò)展函數(shù)
infix fun Int.add(x: Int): Int  = this + x

fun main(args: Array<String>) {
    // 用中綴符號表示的擴(kuò)展函數(shù)
    println("2 add 1:${2 add 1}") // 打铀朴恰:2 add 1:3
    // 與下面是相同的
    println("2.add(1):${2.add(1)}") // 打印:2.iInfix(1):3
}

Tip4-自定義屬性委托

1. 只讀屬性實(shí)現(xiàn)委托

只讀屬性(使用val定義),委托類需實(shí)現(xiàn)getValue函數(shù)

interface ReadOnlyProperty<in R, out T> {
    operator fun getValue(thisRef: R, property: KProperty<*>): T
}

2. 可變屬性實(shí)現(xiàn)委托

可變屬性(使用var定義),委托類需實(shí)現(xiàn)getValue函數(shù)和setValue函數(shù)

interface ReadWriteProperty<in R, T> {
    operator fun getValue(thisRef: R, property: KProperty<*>): T
    operator fun setValue(thisRef: R, property: KProperty<*>, value: T)
}

3. 一個(gè)委托實(shí)例

下面來看一個(gè)自定義的Delegate据某,用來訪問SharedPreference橡娄,這段代碼是Kotlin for Android Developer的示例:

class Preference<T>(val context: Context, val name: String, val default: T) : ReadWriteProperty<Any?, T> {

    val prefs by lazy { context.getSharedPreferences("default", Context.MODE_PRIVATE) }

    override fun getValue(thisRef: Any?, property: KProperty<*>): T {
        return findPreference(name, default)
    }

    override fun setValue(thisRef: Any?, property: KProperty<*>, value: T) {
        putPreference(name, value)
    }

    private fun <U> findPreference(name: String, default: U): U = with(prefs) {
        val res: Any = when (default) {
            is Long -> getLong(name, default)
            is String -> getString(name, default)
            is Int -> getInt(name, default)
            is Boolean -> getBoolean(name, default)
            is Float -> getFloat(name, default)
            else -> throw IllegalArgumentException("This type can be saved into Preferences")
        }

        res as U
    }

    private fun <U> putPreference(name: String, value: U) = with(prefs.edit()) {
        when (value) {
            is Long -> putLong(name, value)
            is String -> putString(name, value)
            is Int -> putInt(name, value)
            is Boolean -> putBoolean(name, value)
            is Float -> putFloat(name, value)
            else -> throw IllegalArgumentException("This type can be saved into Preferences")
        }.apply()
    }
}

使用的時(shí)候:

class ExampleActivity : AppCompatActivity(){
    var a: Int by Preference(this, "a", 0)
    
    fun whatever(){
        println(a)//會從SharedPreference取這個(gè)數(shù)據(jù)
        aInt = 9 //會將這個(gè)數(shù)據(jù)寫入SharedPreference
        println(a)//會從SharedPreference取這個(gè)數(shù)據(jù)
    }
}

Tip5-委托模式

Kotlin支持委托模式,與java的動(dòng)態(tài)代理類似癣籽,有兩個(gè)對象參與處理同一個(gè)請求挽唉,接受請求的對象將請求委托給另一個(gè)對象來處理。java里面動(dòng)態(tài)代理是用反射來實(shí)現(xiàn)的筷狼,還要添加額外的很多代碼瓶籽,相比較kotlin的動(dòng)態(tài)代理就簡單很多了。

interface Animal{
    fun bark()
}

class Dog :Animal {
    override fun bark() {
        Log.e("test","Wang Wang")
    }
}

class Cat(animal: Animal) : Animal by animal {
}

fun main(args: Array<String>) {
   Cat(Dog()).bark()
}

//輸出:wangwang
//這個(gè)實(shí)例中埂材,用狗代理了貓,幫貓?zhí)幚砹私新暤牟僮?
class CountingSet2<T>(val innerSet: MutableCollection<T> = HashSet<T>()) :MutableCollection<T> by innerSet{

    var objectAdded = 0

    init {
        objectAdded += innerSet.size
    }

    override fun add(element: T): Boolean {
        objectAdded++
        return innerSet.add(element)
    }

    override fun addAll(elements: Collection<T>): Boolean {
        objectAdded += elements.size
        return innerSet.addAll(elements)
    }
}

val hashSet = hashSetOf("1", "2", "3", "4")
val set = CountingSet2(hashSet)
set.addAll(listOf("5", "6", "7"))
Log.e("test", "set-----${set.size}----${set.objectAdded}")

//輸出:set-----7----7
//這個(gè)實(shí)例中塑顺,用innerSet代理了CountingSet2,幫CountingSet2處理了add,addAll的操作

Tip6-Lambda表達(dá)式與高階函數(shù)

1. Lambda 表達(dá)式的語法

一個(gè)Lambda表達(dá)式通常使用{ }包圍,參數(shù)是定義在()內(nèi)俏险,可以添加類型注解严拒,實(shí)體部分跟在“->”后面;如果Lambda的推斷返回類型不是Unit竖独,那么Lambda主體中的最后一個(gè)(或單個(gè))表達(dá)式將被視為返回值裤唠。
先來看下面的例子:

val sum: (Int, Int) -> Int = { x, y -> x + y }  

// val printMsg: (String) -> Unit = { msg: String -> println(msg)}
val printMsg = { msg: String -> 
    println(msg) 
}

fun main(args: Array<String>) {
  sum(2,3)
  printMsg.invoke("hello")
}

//輸出:hello

2. Lambda 表達(dá)式的約定

  • 當(dāng)參數(shù)只有一個(gè)的時(shí)候莹痢,聲明中可以不用顯示聲明參數(shù)种蘸,在使用參數(shù)時(shí)可以用 it 來替代那個(gè)唯一的參數(shù)。
class Num {
    fun oneParams(one : (Int) -> Int){
        println("oneParams : ${one(5)}")
    }
}

fun main(args : Array<String>){
    val num = Num()
    // num.oneParams({ it -> it * 2 })
    num.oneParams{it * 2}
}

// 輸出oneParams : 10
  • 當(dāng)有多個(gè)用不到的參數(shù)時(shí)竞膳,可以用下劃線來替代參數(shù)名(1.1以后的特性)航瞭,但是如果已經(jīng)用下劃線來省略參數(shù)時(shí),是不能使用 it 來替代當(dāng)前參數(shù)的坦辟。
class Num {
    fun unusedParams(unused : (Int,Int) -> Int){
        println("unusedParams : ${unused(5,10)}")
    }
}

fun main(args : Array<String>){
    val num = Num()
    num.unusedParams { _, used -> used * 2 }
    // num.unusedParams { _, used -> it * 2 }  這種寫法是編譯不過的刊侯,不能使用 it 來替代當(dāng)前參數(shù)的。
}

// 輸出 unusedParams : 20
  • 如果函數(shù)的最后一個(gè)參數(shù)是一個(gè)函數(shù)锉走,那么我們在用Lambda表達(dá)最后一個(gè)函數(shù)參數(shù)的時(shí)候滔吠,可以把它放在括號()外面,所以下面的寫法是等價(jià)的挠日。
class Num {
    fun logic(a: Int, b: Int, calc: (Int, Int) -> Int){
        println("calc : ${calc(a,b)}")
    }
}

fun main(args : Array<String>){
    val num = Num()
    // num.logic(1, 2,{x,y -> x+y})
    num.logic(1, 2){x,y -> x+y}
}
  • Lambda 最后一條語句的執(zhí)行結(jié)果表示這個(gè) Lambda 的返回值。
// 寫法2
num.unusedParams { _, used ->
    println("print first")
    used * 2
    // 下面這種寫法是等價(jià)的
    // return@unusedParams used * 2
}
  • Lambda表達(dá)式來簡化OnClickListener的寫法:
interface OnClickListener {
    fun onClick()
}

class View {
    var listener: OnClickListener? = null;

    /*
    * 傳統(tǒng)方式
    * 
    */
    fun setOnClickListener(listener: OnClickListener) {
        this.listener = listener
    }

    fun doSth() {
        // some case:
        listener?.onClick()
    }
    
    // 但是這種方式僅適用于有一個(gè)回調(diào)函數(shù)的情況

    /*
    * 聲明lambda方式翰舌,listener: () -> Unit
    * 函數(shù)可以是一種類型,一個(gè)變量可以是函數(shù)類型的
    */
    var listener1:()->Unit

   
    fun setOnClickListener(listener: () -> Unit) {
        this.listener1=listener
    }

    fun doSth() {
        // some case:
        listener1?.invoke()
    }
}

3. 高階函數(shù)

  • Lambda 表達(dá)式最大的特點(diǎn)是可以作為參數(shù)傳遞嚣潜。當(dāng)定義一個(gè)閉包作為參數(shù)的函數(shù),稱這個(gè)函數(shù)為高階函數(shù)椅贱。(通常情況下懂算,我們所說的閉包是 Lambda 表達(dá)式)
fun main(args: Array<String>) {
    log("world", printMsg)
}

val printMsg = { str: String ->
    println(str)
}

val log = { str: String, printLog: (String) -> Unit ->
    printLog(str)
}
//輸出:world
//log 有兩個(gè)參數(shù)只冻,一個(gè)str:String,一個(gè)printLog: (String) -> Unit计技。
  • 函數(shù)作為返回值
    函數(shù)作為返回值也非常實(shí)用喜德,例如我們的需求是根據(jù)不同的快遞類型返回不同計(jì)價(jià)公式,普通快遞和高級快遞的計(jì)價(jià)規(guī)則不一樣垮媒,這時(shí)候我們可以將計(jì)價(jià)規(guī)則函數(shù)作為返回值:
enum class Delivery {
    STANDARD, EXPEDITED
}

/*
* 根據(jù)不同的運(yùn)輸類型返回不同的快遞方式
* */
fun getShippingCostCalculator(delivery: Delivery): (Int) -> Double {
    if (delivery == Delivery.EXPEDITED) {
        return { 6 + 2.1 * it }
    }
    return { 1.3 * it }
}

fun test05() {
    val calculator1 = getShippingCostCalculator(Delivery.EXPEDITED)
    val calculator2 = getShippingCostCalculator(Delivery.STANDARD)
    println("Ex costs ${calculator1(5)}")
    println("St costs ${calculator2(5)}")
}

如果是普通快遞舍悯,采用1.3 * it的規(guī)則計(jì)算價(jià)格,如果是高級快遞按照6 + 2.1 * it計(jì)算價(jià)格睡雇,根據(jù)不同的類型返回不同的計(jì)價(jià)函數(shù)萌衬。

Tip7-數(shù)據(jù)類

1. 用法和重要方法介紹

  • data class User(var name: String, var age: Int)
    會自動(dòng)生成gettersetter方法,還有componentN()它抱,對應(yīng)按聲明順序出現(xiàn)的所有屬性秕豫,如name就是component1()age就是component2()观蓄。
    當(dāng)然還有 equals()混移、hashCode()、和 toString()(輸出的格式為User(name=..., age=...))

  • 如果構(gòu)造函數(shù)參數(shù)中沒有聲明是val或者var侮穿,這些函數(shù)就不會生成

  • 主構(gòu)造函數(shù)需要有至少一個(gè)參數(shù)

  • 數(shù)據(jù)類不能有abstract歌径、open、sealed和inner修飾

  • 在1.1版本之前撮珠,數(shù)據(jù)類只能實(shí)現(xiàn)接口

  • 在構(gòu)造函數(shù)那里也說過沮脖,如果生成的類需要一個(gè)無參數(shù)的構(gòu)造函數(shù),則必須指定所有屬性的默認(rèn)值
    data class User(var name: String = "lily", var age: Int = 0)
    eg.比如適用fastjson解析json串為對象時(shí)芯急,要求數(shù)據(jù)類必須有一個(gè)無參的構(gòu)造函數(shù)勺届,就要用到上面的寫法了

  • 復(fù)制(copy)
    數(shù)據(jù)類在創(chuàng)建的時(shí)候,除了會生成上面的幾個(gè)方法外娶耍,還會生成一個(gè)copy()函數(shù)免姿,copy()能夠復(fù)制一個(gè)對象改變它的一些屬性情況下,又要保持其余的不變榕酒,如上面的User類胚膊,copy()函數(shù)的實(shí)現(xiàn):

fun copy(name: String = this.name, age: Int = this.age) = User(name, age)
  • 在使用copy()之后,就可以修改數(shù)據(jù)類的一些屬性了:
val jack = User(name = "jack", age = 1)
val olderJack = jack.copy(age = 2)

2.Pair和Triple

Kotlin提供了PairTriple作為標(biāo)準(zhǔn)數(shù)據(jù)類想鹰,命名數(shù)據(jù)類是更好的設(shè)計(jì)選擇紊婉。
兩個(gè)參數(shù)的時(shí)候使用Pair數(shù)據(jù)類
三個(gè)參數(shù)的時(shí)候使用Triple數(shù)據(jù)類

/**
* 元組(Tuple),給多個(gè)變量同時(shí)賦值辑舷,分二元(Pair)和三元(Triple)
 */
val (year, month, day) = Triple(2017, "6月", "14號")
Log.e("test", "${year}年$month$day")

val date = Triple(2017, "6月", "14號")
Log.e("test", "${date.first}年${date.second}${date.third}")
//二元同上喻犁,把Triple換成Pair

3.解構(gòu)聲明

在Kotlin中創(chuàng)建變量的話是這樣的

data class Person(var name: String, var age: Int)

fun main(args: Array<String>) {
    val person = Person("jowan", 1)
    var name = person.name
    var age = person.age
    println(name) // 打印jowan
    println(age) // 打印1
}
  • 使用解構(gòu)變量,同時(shí)創(chuàng)建多個(gè)變量
data class Person(var name: String, var age: Int)

fun main(args: Array<String>) {
    val (name, age) = Person("person", 1)
    println(name) // 打印person
    println(age) // 打印1
}

Anko

Anko是 JetBrains 公司開發(fā)的一個(gè)強(qiáng)大的庫,主要的目的是用來替換之前用XML的方式肢础,來使用代碼生成UI布局

1. Anko四個(gè)組成部分內(nèi)容

  1. Anko Commons
    輕量級的一些幫助類还栓,比如 intent,dialog传轰,logging 等等剩盒,其實(shí)就是對安卓一些類:Activity、Fragment慨蛙、Intent 等添加擴(kuò)展函數(shù)辽聊。
  2. Anko Layouts
    動(dòng)態(tài)布局用的最主要的庫,將許多 Android 的控件 View 轉(zhuǎn)換成了 Anko 加載的形式股淡。
    由于 Android 還有其他的控件庫身隐,因此 Anko 也對那些庫進(jìn)行了拓展支持,可以選擇添加對應(yīng)的依賴庫唯灵。
    當(dāng)然贾铝,還可以根據(jù)需要對自定義 View 進(jìn)行改造,讓它們也支持 Anko 加載的形式埠帕。
  3. Anko SQLite
    用于 Android SQLite 數(shù)據(jù)庫的查詢的庫
  4. Anko Coroutines
    基于 kotlinx.coroutines 協(xié)程的一個(gè)工具庫垢揩。

2. Anko用法示例

  • Anko Toast的簡單用法
toast("大地零一")
longToast("大地零一")
  • Anko Dialog的簡單用法
alert("確定刪除嗎?"){
yesButton { toast("確定") }
noButton { toast("取消") }
}.show()
  • Anko Intent的簡單用法
startActivity(intentFor<MainActivity().singleTop())
  • Anko Coroutines
    Anko還提供了協(xié)程的用來做一些耗時(shí)的操作,提供的操作為bg{},具體代碼如下:
async(UI){//UI線程
    val data: Deferred<MyBean> = bg {//后臺線程
        // Runs in background
        MyBean()
    }

    showData(data.await()) //await方法將一直等待bg返回的數(shù)據(jù)
}

為了防止內(nèi)存泄漏我們常會使用弱引用敛瓷,在Anko中使用弱引用方法如下

val ref: Ref<AnkoActivity05> = this.asReference()
    async(UI){
        //ref替代了this@AnkoActivity05
        ref().showData()
    }
  • Anko Layout
<LinearLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_height="match_parent"
    android:padding="30dp"
    android:layout_width="match_parent">

    <EditText
        android:id="@+id/todo_title"
        android:layout_width="match_parent"
        android:layout_heigh="wrap_content"
        android:hint="@string/title_hint" />

    <!-- Cannot directly add an inline click listener as onClick delegates implementation to the activity -->
    <Button
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="@string/add_todo" />

</LinearLayout>

用 Anko 描述的同樣的視圖

verticalLayout {
    padding = dip(30)
    var title = editText {
        id = R.id.todo_title
        hintResource = R.string.title_hint
    }
    button {
        textResource = R.string.add_todo
        onClick { view -> {
                // do something here
                title.text = "Foo"
            }
        }
    }
}

3. 不足

  • Anko 好是好叁巨,但是依舊不夠完美。

  • 在 XML 中能夠設(shè)置的控件屬性更多呐籽,更精確的控制布局狀態(tài)锋勺,而 Anko 在構(gòu)建簡單界面的時(shí)候才顯得快速、便捷狡蝶。

  • 而且 Anko 支持的控件有限庶橱,加載自定義的控件還得添加額外的代碼,在更復(fù)雜的應(yīng)用中應(yīng)該不太會廣泛的使用贪惹。

總結(jié)

  • Kotlin是一門相對比較新的JVM語言苏章,JetBrains自2011年以來一直在積極地開發(fā)。多年來奏瞬,該語言在Android社區(qū)受到的關(guān)注度越來越高枫绅,并在Google IO 2017大會之后成為Android開發(fā)領(lǐng)域最熱門的話題。這次大會宣布硼端,Android正式支持Kotlin并淋。Kotlin在Java以及多種語言的基礎(chǔ)上,去掉了冗余代碼珍昨,代碼更加簡潔县耽,可讀性更強(qiáng)订咸,Kotlin還為已有的Java類提供一組好用的擴(kuò)展,絕大部分Java能實(shí)現(xiàn)的功能kotlin也是可以實(shí)現(xiàn)的,以后開發(fā)中酬诀,推薦大家能使用Kotlin的地方盡量使用。
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末骆撇,一起剝皮案震驚了整個(gè)濱河市瞒御,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌神郊,老刑警劉巖肴裙,帶你破解...
    沈念sama閱讀 211,348評論 6 491
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異涌乳,居然都是意外死亡蜻懦,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,122評論 2 385
  • 文/潘曉璐 我一進(jìn)店門夕晓,熙熙樓的掌柜王于貴愁眉苦臉地迎上來宛乃,“玉大人,你說我怎么就攤上這事蒸辆≌髁叮” “怎么了?”我有些...
    開封第一講書人閱讀 156,936評論 0 347
  • 文/不壞的土叔 我叫張陵躬贡,是天一觀的道長谆奥。 經(jīng)常有香客問我,道長拂玻,這世上最難降的妖魔是什么酸些? 我笑而不...
    開封第一講書人閱讀 56,427評論 1 283
  • 正文 為了忘掉前任,我火速辦了婚禮檐蚜,結(jié)果婚禮上魄懂,老公的妹妹穿的比我還像新娘。我一直安慰自己熬甚,他們只是感情好逢渔,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,467評論 6 385
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著乡括,像睡著了一般肃廓。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上诲泌,一...
    開封第一講書人閱讀 49,785評論 1 290
  • 那天盲赊,我揣著相機(jī)與錄音,去河邊找鬼敷扫。 笑死哀蘑,一個(gè)胖子當(dāng)著我的面吹牛诚卸,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 38,931評論 3 406
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼查库!你這毒婦竟也來了缭付?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 37,696評論 0 266
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體睛约,經(jīng)...
    沈念sama閱讀 44,141評論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,483評論 2 327
  • 正文 我和宋清朗相戀三年哲身,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了辩涝。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 38,625評論 1 340
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡勘天,死狀恐怖怔揩,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情误辑,我是刑警寧澤沧踏,帶...
    沈念sama閱讀 34,291評論 4 329
  • 正文 年R本政府宣布,位于F島的核電站巾钉,受9級特大地震影響翘狱,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜砰苍,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,892評論 3 312
  • 文/蒙蒙 一潦匈、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧赚导,春花似錦茬缩、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,741評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至圈暗,卻和暖如春掂为,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背员串。 一陣腳步聲響...
    開封第一講書人閱讀 31,977評論 1 265
  • 我被黑心中介騙來泰國打工勇哗, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人寸齐。 一個(gè)月前我還...
    沈念sama閱讀 46,324評論 2 360
  • 正文 我出身青樓欲诺,卻偏偏與公主長得像抄谐,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個(gè)殘疾皇子扰法,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,492評論 2 348

推薦閱讀更多精彩內(nèi)容

  • 前言 人生苦多蛹含,快來 Kotlin ,快速學(xué)習(xí)Kotlin塞颁! 什么是Kotlin挣惰? Kotlin 是種靜態(tài)類型編程...
    任半生囂狂閱讀 26,163評論 9 118
  • Google在今年的IO大會上宣布,將Android開發(fā)的官方語言更換為Kotlin殴边,作為跟著Google玩兒An...
    藍(lán)灰_q閱讀 76,794評論 31 489
  • 面向?qū)ο缶幊蹋∣OP) 在前面的章節(jié)中锤岸,我們學(xué)習(xí)了Kotlin的語言基礎(chǔ)知識、類型系統(tǒng)板乙、集合類以及泛型相關(guān)的知識是偷。...
    Tenderness4閱讀 4,425評論 1 6
  • Kotlin的優(yōu)勢 代碼簡潔高效、強(qiáng)大的when語法募逞,不用寫分號結(jié)尾蛋铆,findViewById光榮退休,空指針安全...
    Windy_816閱讀 1,282評論 1 6
  • 聚合分析對應(yīng)數(shù)據(jù)庫中的聚合函數(shù)放接。在 Elasticsearch 中使用 aggs 標(biāo)簽表示刺啦。 計(jì)算所有商品的價(jià)格總...
    與蟒唯舞閱讀 2,808評論 0 2