kotlin進階學習記錄

前言

使用kotlin已經有一段時間了,可是看一些開源項目總以爲本身只是學了點皮毛。正好組內還沒人熟悉kotlin進階相關知識,所以也是決定以此文來作個分享,開闊下技術視野java

操做符

雙引號::

Kotlin 中 雙冒號操做符 表示把一個方法當作一個參數,傳遞到另外一個方法中進行使用,通俗的來說就是引用一個方法。express

println(::methon2)
 fun methon2() {
    println("methon2")
}
結果:
fun methon2(): kotlin.Unit
複製代碼

函數

可變數量的參數(Varargs)

函數的參數(一般是最後一個)能夠用 vararg 修飾符標記:編程

fun main(args: Array<String>) {
    hello(1,2,string = "hello")
    hello(1,2,3,string = "world")
    val asList = asList("2", "3")
    println(asList.toString())
}
//這裏並不知道有幾個int的  vararg表示變長參數
fun hello(vararg intParameter:Int,string: String){
    for (i in intParameter) {
        println(i)
    }
    println(string)
}

fun <T> asList(vararg ts: T): List<T> {
    val result = ArrayList<T>()
    for (t in ts) // ts is an Array
        result.add(t)
    return result
}
結果:
1
2
hello
1
2
3
world
[2, 3]
複製代碼

lambda表達式

也可稱爲閉包,本質是匿名函數,這裏從最基礎的點擊事件講起bash

lambda結構

{ 參數1: 類型, 參數2: 類型  -> 表達式 }
複製代碼

lambda特色

  • lambda表達式老是被大括號括着
  • 其參數(若是存在)在 -> 以前聲明(參數類型能夠省略)
  • 函數體(若是存在)在 -> 後面。

lambda寫點擊事件

功能:點擊隱藏該控件
    // java代碼:
    tv.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                v.setVisibility(View.GONE);
            }
        });
       
      // kotlin 代碼:
      
      // 一、kotlin 最原始寫法
        tv.setOnClickListener(object : View.OnClickListener{
            override fun onClick(v: View) {
                v.visibility=View.GONE
            }
        })

        // 二、匿名函數改爲lambda寫法
        tv.setOnClickListener({ v:View -> v.visibility=View.GONE })

        // 三、當lambda是函數的惟一實參,就能夠去掉空的小括號對
        tv.setOnClickListener { v:View -> v.visibility=View.GONE }

        //四、若是lambda的參數的類型能夠被編譯器推導出來,就能夠省略它
        tv.setOnClickListener { v -> v.visibility=View.GONE }

        // 五、若是lambda只有一個參數,那麼這個參數也能夠省略掉。代碼中引用這個參數的地方能夠經過編譯器自動生成的名稱it來替代
        tv.setOnClickListener { it.visibility=View.GONE}      
複製代碼

閉包

閉包的做用

  • 讓函數成爲編程語言中的一等公民(通常來講,若是某程序設計語言中的一個值能夠做爲參數傳遞,能夠從子程序中返回,能夠賦值給變量,就稱它爲一等公民)
  • 讓函數具備對象全部的能力(封裝)
  • 讓函數具備狀態
定義一個比較測試閉包
val test = if (5 > 3) {
    println("yes")
} else {
    println("no")
}

  methon1 { methon2() }
  fun methon1(body: () -> Unit) {
    body()
}
運行結果:
fun methon2(): kotlin.Unit
methon2

fun main(args: Array<String>) {
    val method = makeFun()
    method()
    method()
    method()
}

// 函數返回值是一個lambda表達式
fun makeFun(): ()->Unit{
    var count = 0
    return fun(){
        println(++count)
    }
}
// 運行結果
1
2
3

複製代碼

面向對象

方法重載與默認參數

擴展函數

聲明一個擴展函數,咱們須要用一個 接收者類型 也就是被擴展的類型來做爲他的前綴閉包

print("100".appendStr("%"))
 
/**
 * this 就是.前面的String,即這裏是100
 * @receiver String
 * @param string String
 * @return String
 */
fun String.appendStr(string: String):String{
    return this+string
}
運行結果:
100%
複製代碼

在Android中一般能夠寫在BaseActivity/BaseFragment中,至關於給Activity、Fragment增長新功能app

open class BaseActivity : Activity() {
    fun Context.toast(msg: String) {
        if (TextUtils.isEmpty(msg)) {
            return
        }
        Toast.makeText(this, msg, Toast.LENGTH_SHORT)
    }
}
實現類調用
toast("這波操做666")

複製代碼

屬性代理

延遲屬性 Lazy

lazy() 方法接收一個lamda做爲參數並返回一個 Lazy實例, 能夠實現延遲加載: 第一次調用 get()時執行傳入 lazy()的lambda表達式並保存結果, 後續對get()的調用只返回保存的結果.編程語言

監控屬性 Observable

Delegates.observable()有兩個參數: 初始值和變化觀察器. 每次代理屬性被賦予值的時候都會調用觀察器(在賦值操做以後).觀察器有三個參數:屬性類型, 舊值和新值.ide

自定義屬性代理

定義方法:
val/var <property name>:<Type> by <expression>
代理者須要實現相應的getValue/setValue方法

class AttrDelegate{
    // val,不可變的,第一次訪問name才賦值
    val name by lazy {
        println("這句話只會打印一次")
        "888"
    }

    val age by MyDelegate()
    var age2 by MyDelegate()
}
fun main(args: Array<String>) {
    var attrDelegate=AttrDelegate()
    println(attrDelegate.name)
    println(attrDelegate.name)
    println(attrDelegate.age)
    println(attrDelegate.age2)
    attrDelegate.age2="male"
    println(attrDelegate.age2)

}
//  爲何不是繼承lazy
class MyDelegate{ //重載操做符的函數須要用 operator 修飾符標記。
    private var value: String? = null

    operator fun getValue(thisRef: Any?, property: KProperty<*>): String {
        println("getValue: $thisRef -> ${property.name}")
        return value?: ""
    }

    /**
     * 要代理var的屬性,除了實現getValue以外還須要再實現setValue
     */
    operator fun setValue(thisRef: Any?, property: KProperty<*>, value: String){
        println("setValue, $thisRef -> ${property.name} = $value")
        this.value = value
    }
}
運行結果:
這句話只會打印一次
888
888
getValue: com.sunkeding.kotlin.delegate.AttrDelegate@238e0d81 -> age

getValue: com.sunkeding.kotlin.delegate.AttrDelegate@238e0d81 -> age2

setValue, com.sunkeding.kotlin.delegate.AttrDelegate@238e0d81 -> age2 = male
getValue: com.sunkeding.kotlin.delegate.AttrDelegate@238e0d81 -> age2
male
複製代碼

密封類

密封類用來表示受限的類繼承結構:當一個值爲有限幾種的類型, 而不能有任何其餘類型時。在某種意義上,他們是枚舉類的擴展:枚舉類型的值集合 也是受限的,但每一個枚舉常量只存在一個實例,而密封類 的一個子類能夠有可包含狀態的多個實例。函數

聲明一個密封類,使用 sealed 修飾類,密封類能夠有子類,可是全部的子類都必需要內嵌在密封類中。測試

sealed 不能修飾 interface ,abstract class(會報 warning,可是不會出現編譯錯誤)

sealed class Student{
    abstract fun speak()

    class Boy:Student() {
        override fun speak() {

        }
    }

    class Girl:Student() {
        override fun speak() {
        }
    }

}
複製代碼

高階函數

概念:高階函數是將函數用做參數或返回值的函數。

函數類型

  • 全部函數類型都有一個圓括號括起來的參數類型列表以及一個返回類型:(A, B) -> C 表示接受類型分別爲 A 與 B 兩個參數並返回一個 C 類型值的函數類型。

infix函數

infix函數必須知足如下要求

  • 它們必須是成員函數或擴展函數;
  • 它們必須只有一個參數;
  • 其參數不得接受可變數量的參數且不能有默認值。
println(1.add(2))
println(1 add 2) 
infix fun Int.add(x: Int): Int {
    return this + x
}
運行結果:
3
3
複製代碼

函數複合

概念:f(g(x))

/**定義兩個函數*/
val add5 = {i: Int -> i + 5} //加5
val multiplyBy2 = {i: Int -> i * 2}  //乘2

fun main(args: Array<String>) {
//    println(multiplyBy2(add5(8)))  //(5 + 8) * 2
//
//    val add5AndMultiplyBy2 = add5 andThen multiplyBy2
//    val add5ComposeMutiplyBy2 = add5 compose multiplyBy2
//    println(add5AndMultiplyBy2(8))  //m(x)= f(g(x))    (8+5)*2
//    println(add5ComposeMutiplyBy2(8)) //m(x) = g(f(x))   8*2+5

    val add5AndMultiplyBy2 = add5.andThen(multiplyBy2)
    val add5ComposeMutiplyBy2 = add5.compose(multiplyBy2)
    println(add5AndMultiplyBy2(8))
    println(add5ComposeMutiplyBy2(8))
}

/**定義一個複合函數*/
/**
 * p一、p2是參數
 * R是返回值
 * andThen擴展函數
 * 參數:Function1<P2,P2>,第一參數是參數類型,第二個參數是返回值類型
 * 返回值:Function1<P1,R>
 * infix中綴表達式
 */
infix fun <P1,P2,R> Function1<P1,P2>.andThen(function: Function1<P2,R>): Function1<P1,R>{
    //進行復合
    //返回了一個函數
    return fun (p1: P1):R{
        //函數裏面function.invoke把這個function又調用了一遍
        //而後又把本身的返回值傳給了上去
        return function.invoke(this.invoke(p1))
    }
}

infix fun <P1,P2,R> Function1<P2,R>.compose(function: Function1<P1,P2>):Function1<P1,R>{
    return fun (p1: P1):R{
        return this.invoke(function.invoke(p1))
    }
}

運行結果:
26
21

複製代碼

反射

Kotlin文件中用java思惟反射Kotlin代碼

class Person( var name: String, var age: Int){
    private fun eat(){
        println("eat")
    }
    fun speak(string: String){
        println(string)
    }
    override fun toString(): String {
        return "Person(name='$name', age=$age)"
    }
}

    val clazz = Class.forName("com.study.reflections.Person")
    val newInstance = clazz.getConstructor(String::class.java,Int::class.java).newInstance("zhangsan",10)
    println(newInstance)
    val method = newInstance.javaClass.getDeclaredMethod("eat").apply { isAccessible=true }
    val method2 = newInstance.javaClass.getDeclaredMethod("speak", String::class.java)
    method.invoke(newInstance)
    method2.invoke(newInstance,"說個666吧")
    val newInstance = clazz.getConstructor(String::class.java,Int::class.java).newInstance("zhangsan",10)
    println(newInstance)
    val method = newInstance.javaClass.getDeclaredMethod("eat").apply { isAccessible=true }
    val method2 = newInstance.javaClass.getDeclaredMethod("speak", String::class.java)
    method.invoke(newInstance)
    method2.invoke(newInstance,"說個666吧")
運行結果:
Person(name='zhangsan', age=10)
eat
說個666吧


複製代碼
相關文章
相關標籤/搜索