1. 程式人生 > >Kotlin學習筆記(四)

Kotlin學習筆記(四)

本文章借鑑了其他網路文章資源,僅做個人筆記

1、開篇
以下在main函式中定義了一個名為codeBlock的程式碼塊,可以理解成是一個函式,這個函式接收一個名為f的Int型別引數,函式內部進行了列印操作,同時返回了一個3作為返回值,那麼也就是說,codeBlock的返回值型別是Int型別(Kotlin型別自動推斷機制),呼叫該函式,傳入2,並用result接收函式的返回值,再列印result,可以發現整體列印結果不難理解

fun main(args: Array<String>) {

    val codeBlock = { f: Int ->
        println("f=$f")
        3
    }

    var result = codeBlock(2)
    println("result = $result")
    
    // 呼叫結果:
    // f=2
    // result = 3
}

2、原理
我們嘗試看一下codeBlock在Java位元組碼中的型別,發現是一個匿名內部類

println(codeBlock.javaClass) // class MyLambdaDemoKt$main$codeBlock$1

遍歷該匿名內部類所有的方法,經過和普通類對比,發現多出3個方法:invoke、invoke、getArity

for (m in codeBlock.javaClass.methods) {
        println(m.name)
    }
//    invoke
//    invoke
//    toString
//    getArity
//    wait
//    wait
//    wait
//    equals
//    hashCode
//    getClass
//    notify
//    notifyAll

我們這樣找到invoke方法,然後進入invoke原始碼觀看,我們發現,我的天吶~!進入了Java的Method.java類中的public Object invoke(Object obj, Object… args)方法,瞬間一切好像都明白了些什麼,沒錯,就是反射裡常用的method.invoke方法

for (m in codeBlock.javaClass.methods) {
    if ("invoke" == m.name) {
        m.invoke(codeBlock, 10)
    }
}

// Java的Method.java類中的invoke方法
@CallerSensitive
public Object invoke(Object obj, Object... args)
    throws IllegalAccessException, IllegalArgumentException,
       InvocationTargetException
{
    if (!override) {
        if (!Reflection.quickCheckMemberAccess(clazz, modifiers)) {
            Class<?> caller = Reflection.getCallerClass();
            checkAccess(caller, clazz, obj, modifiers);
        }
    }
    MethodAccessor ma = methodAccessor;             // read volatile
    if (ma == null) {
        ma = acquireMethodAccessor();
    }
    return ma.invoke(obj, args);
}

不多說了,直接調一下試試吧(咦?為什麼有兩個一模一樣的invoke方法,有興趣的同學可以跟一下原始碼,歡迎留言)

for (m in codeBlock.javaClass.methods) {
    if ("invoke" == m.name) {
        m.invoke(codeBlock, 10)
        // 呼叫結果:
        // f=10
        // f=10
    }
}

我們把兩個invoke方法的一些變數打印出來

for (m in codeBlock.javaClass.methods) {
    if ("invoke" == m.name) {
        println("--------------------")
        println(m.defaultValue)
        println(m.genericReturnType)
        println(m.isBridge)
        println(m.isDefault)
        println(m.returnType)
    }
}

// 列印結果:
--------------------
null
class java.lang.Object
true
false
class java.lang.Object
--------------------
null
int
false
false
int

3、再舉個栗子

// 1、你提前給我了"1瓶神油"和"1粒偉哥"和"會呼吸的杜蕾斯",然後你讓我在走廊等你,你把302房間鑰匙給了我,我有權利給你放東西,於是我開始放東西,我先放入你給我的印度神油(帶包裝盒)和杜蕾斯(帶包裝盒):_302房間(印度神油, 杜蕾斯)
// 2、你在房間裡一頓折騰,你先判斷道具1是不是1瓶神油,道具2是不是會呼吸的杜蕾斯,然後如果是,就告訴我OK(你太忙,只告訴我true或者false,沒有多說一句話)
// 3、於是我拿到了你的反饋意見:var result = _302房間(印度神油, 杜蕾斯)
// 4、我根據你的反饋意見,得出結論,到底印度神油好用不好用
// 5、具體邏輯和場景,請自行腦補,歡迎留言
	
// 定義
fun myfun(印度神油: String, 偉哥: String, 杜蕾斯: String, _302房間: (String, String) -> Boolean): () -> Unit {
    var result = _302房間(印度神油, 杜蕾斯)
    var a = { println("印度神油好用!!!") }
    var b = { println("印度神油不好用,換偉哥!!!") }
    return if (result) a else b
}

// 呼叫1(正常情況)
var result = myfun("1瓶神油", "1粒偉哥", "會呼吸的杜蕾斯") { 道具1, 道具2 ->
    if (道具1 == "1瓶神油" && 道具2 == "會呼吸的杜蕾斯") true else false
}
result() // 印度神油好用!!!

// 呼叫2(你給我的是假的印度神油)
var result = myfun("假的印度神油", "1粒偉哥", "會呼吸的杜蕾斯") { 道具1, 道具2 ->
    if (道具1 == "1瓶神油" && 道具2 == "會呼吸的杜蕾斯") true else false
}
result() // 印度神油不好用,換偉哥!!!


// 定義
fun myfun(印度神油: String, 偉哥: String, 杜蕾斯: String, _302房間: (String, String) -> Boolean): () -> Unit {
    var result = _302房間("假的印度神油", 杜蕾斯)
    var a = { println("印度神油好用!!!") }
    var b = { println("印度神油不好用,換偉哥!!!") }
    return if (result) a else b
}

// 呼叫(你雖然給了我1瓶神油,但是,我給你的是假的印度神油)
var result = myfun("1瓶神油", "1粒偉哥", "會呼吸的杜蕾斯") { 道具1, 道具2 ->
    if (道具1 == "1瓶神油" && 道具2 == "會呼吸的杜蕾斯") true else false
}
result() // 印度神油不好用,換偉哥!!!