Kotlin-為什么要使用高階函數(shù)?

1、為什么要使用高階函數(shù)?

先來(lái)看看兩段代碼,在Andriod自定義View中的一個(gè)小例子,分別用Java和Kotlin來(lái)實(shí)現(xiàn)

Java

public class DemoView {

    interface OnClickListener {
        void onClick();
    }

    interface OnItemClickListener {
        void onItemClick(int position);
    }

    private OnClickListener onClickListener;
    private OnItemClickListener onItemClickListener;


    public void setOnClickListener(OnClickListener onClickListener) {
        this.onClickListener = onClickListener;
    }

    public void setOnItemClickListener(OnItemClickListener onItemClickListener) {
        this.onItemClickListener = onItemClickListener;
    }
    public void test(){
        onClickListener.onClick();
        onItemClickListener.onItemClick(100);

    }

    public static void main(String[] args) {
        DemoView demoView = new DemoView();
        demoView.setOnClickListener(new OnClickListener() {
            @Override
            public void onClick() {
                System.out.println("onClickListener");
            }
        });
        demoView.setOnItemClickListener(new OnItemClickListener() {
            @Override
            public void onItemClick(int position) {
                System.out.println("onItemClickListener:"+position);
            }
        });
        demoView.test();

    }

}


Kotlin

class TestView {
    //用函數(shù)類型替代接口
    private var onClickListener: (() -> Unit)? = null
    private var onItemClickListener: ((Int) -> Unit)? = null
    fun test() {
        onClickListener?.invoke()
        onItemClickListener?.invoke(100)
    }

    /**
     * 用lambda表達(dá)式作為函數(shù)參數(shù)
     */
    fun setOnClickListener(onClickListener: () -> Unit) {
        this.onClickListener = onClickListener
    }
    fun setOnItemClickListener(onItemClickListener: (Int) -> Unit) {
        this.onItemClickListener = onItemClickListener
    }
}

fun main() {
    val testView = TestView()
    testView.setOnClickListener {
        println("onClickListener")
    }
    testView.setOnItemClickListener {
        println("onItemClickListener${it}")
    }
    testView.test()

}

最終實(shí)現(xiàn)的效果一樣

onClick
onItemClickListener:100

可以看到Kotlin的代碼比Java少很多,Kotlin的設(shè)計(jì)者怎么實(shí)現(xiàn)的呢?實(shí)際分為兩部分:

  • 用函數(shù)類型替代接口
   /**
     * 用lambda表達(dá)式作為函數(shù)參數(shù)
     */
    fun setOnClickListener(onClickListener: () -> Unit) {
        this.onClickListener = onClickListener
    }
  • 用Lambda表達(dá)式作為函數(shù)入?yún)?/li>
//用函數(shù)類型替代接口
  private var onClickListener: (() -> Unit)? = null

以上我們可以小結(jié):
Kotlin引入高階函數(shù),省了兩個(gè)接口的定義,對(duì)于調(diào)用者來(lái)說(shuō),代碼更加簡(jiǎn)潔

2、高階函數(shù)中一些名稱的含義

什么是函數(shù)類型?

函數(shù)類型,顧名思義是函數(shù)的類型,我們知道一個(gè)變量有類型,那么函數(shù)也有類型。例如以下函數(shù):

fun add(a: Int, b: Int): Int {
   return a + b
}

它的類型是(Int,Int)->Int,也就是一個(gè)函數(shù)的類型包含了函數(shù)的入?yún)⒑头祷仡愋徒Y(jié)合在一起,就是函數(shù)的類型。
那么我們就可以類似定義變量的方式定義一個(gè)函數(shù)

        //函數(shù)名稱    函數(shù)類型
    var function: ((Int, Int) -> Int)? = null

當(dāng)然我們也可以直接對(duì)函數(shù)初始化,并執(zhí)行函數(shù)。

fun main() {
    //函數(shù)名稱    函數(shù)類型
    var function: ((Int, Int) -> Int)? = { a, b ->
        a + b
    }
    val result = function?.invoke(1, 2)
    println(result)
}

什么是函數(shù)的引用

我們定義一個(gè)add函數(shù)

fun add(a: Int, b: Int): Int {
    return a + b
}

將我們的add函數(shù)通過(guò)引用的方式,賦給我們定義的函數(shù)類型,其中::add就是函數(shù)的引用

fun main() {
    //函數(shù)名稱    函數(shù)類型             函數(shù)的引用
    var function: ((Int, Int) -> Int) = ::add
}

fun add(a: Int, b: Int): Int {
    return a + b
}

什么是高階函數(shù)?

  • 函數(shù)的參數(shù)中包含了函數(shù)的類型
  • 函數(shù)的返回值是函數(shù)的類型
    滿足以上某一個(gè)條件的函數(shù),稱之為高階函數(shù),如以下的例子:
fun main() {
    add(1, 2) {
        println(it)
    }
    println(get().invoke(2, 1))

}
/**
 * 函數(shù)中的參數(shù)包含了函數(shù)類型
 */
fun add(a: Int, b: Int, f: (Int) -> Unit) {
    f.invoke(a + b)
}

fun del(a: Int, b: Int): Int {
    return a - b
}

/**
 * 返回值是函數(shù)類型
 */
fun get(): ((Int, Int) -> Int) {
    return ::del
}

什么是Lambda表達(dá)式

Lambda表達(dá)式我們可以理解位函數(shù)的簡(jiǎn)寫,分為兩種用途

  • 使用Lambda表達(dá)式聲明創(chuàng)建一個(gè)函數(shù)
  • 使用Lambda表達(dá)式作為函數(shù)類型的入?yún)?br> 如下面的例子:
fun main() {
    //1、使用lambda表達(dá)聲明創(chuàng)建一個(gè)函數(shù)
    val add: (Int, Int) -> Int = { a, b ->
        a + b
    }
    val result = add.invoke(1, 2)
    println(result)
    //2、使用lambda表示作為函數(shù)的入?yún)?    val result2 = add { a, b ->
        a + b
    }
    println(result2)

}

/**
 *使用lambda表示作為函數(shù)的入?yún)? */
fun add(f: (Int, Int) -> Int): Int {
    return f.invoke(3, 2)
}

什么是SAM轉(zhuǎn)換?

SAM表示是Single Abstract Method (簡(jiǎn)單的抽象方法的類或者接口)但是在Kotlin和Java8里,SAM只代表只有一個(gè)抽象方法的接口。因此只要滿足接口中只有一個(gè)方法,我們就可以使用SAM轉(zhuǎn)換,也就是我們可以使用Lambda表達(dá)式來(lái)簡(jiǎn)寫接口類的參數(shù)。如:

轉(zhuǎn)換前
    interface OnClickListener {
        void onClick();
    }

    public void setOnClickListener(OnClickListener onClickListener) {
        this.onClickListener = onClickListener;
    }

        demoView.setOnClickListener(new OnClickListener() {
            @Override
            public void onClick() {
                System.out.println("onClickListener");
            }
        });
轉(zhuǎn)換后
    /**
     * 用lambda表達(dá)式作為函數(shù)參數(shù)
     */
    fun setOnClickListener(onClickListener: () -> Unit) {
        this.onClickListener = onClickListener
    }
  //使用SAM轉(zhuǎn)換
    TestView().setOnClickListener { 
        
    }

我們聲明一個(gè)函數(shù)類型變量,并通過(guò)函數(shù)的引用賦值給此變量

fun main() {
   //函數(shù)的引用add賦值給 addFun函數(shù)變量
    val addFun: (Int, Int) -> Int = ::add
    println(addFun.invoke(1, 2))
}
/**
 * 普通的函數(shù)
 */
fun add(a: Int, b: Int): Int {
    return a + b
}

這樣一來(lái)我們發(fā)現(xiàn)比較麻煩,遍可以通過(guò)SAM轉(zhuǎn)換的方式創(chuàng)建此函數(shù)

fun main() {
   //使用SAM轉(zhuǎn)換
    val addFun: (Int, Int) -> Int = {
        a,b->
        a+b
    }
    println(addFun.invoke(1, 2))
}

我小結(jié)就是

當(dāng)我們的接口中只有一個(gè)實(shí)現(xiàn)函數(shù)的時(shí)候,我們可以通過(guò)Kotlin中的函數(shù)類型替代。而在Kotlin中我們又可以通過(guò)Lambda表達(dá)式來(lái)簡(jiǎn)寫聲明一個(gè)函數(shù),因此我們就可以通過(guò)此方式替代接口。
因此對(duì)于兩種情況都是可以使用SAM轉(zhuǎn)換:
(1)接口中只有一個(gè)實(shí)現(xiàn)函數(shù)
(2)聲明創(chuàng)建一個(gè)函數(shù)類型的實(shí)現(xiàn)

在Kotlin中我們引入了函數(shù)的類型,也就是從此之后不僅僅一個(gè)普通的變量有類型,函數(shù)我們也可以當(dāng)成一個(gè)變量,也擁有類型,稱之為函數(shù)的類型。
這樣一來(lái)函數(shù)就可以擁有了普通變量等同的功能,函數(shù)類型變量的聲明,創(chuàng)建賦值。函數(shù)類型的傳參,函數(shù)類中的返回值。函數(shù)類型變量的使用。

  • 將函數(shù)的參數(shù)類型和返回值類中抽出來(lái),就代表了這個(gè)函數(shù)的類型
  • 如果一個(gè)函數(shù)的參數(shù)或者返回值的類型是一個(gè)函數(shù)類型,那這個(gè)函數(shù)就是高階函數(shù)
  • Lambda表達(dá)式是函數(shù)一種簡(jiǎn)寫

3、分析高階函數(shù)在Kotlin源碼中下實(shí)現(xiàn)

let

fun main() {
    var a = "a"
    a.let {
        val result = "$it bcd"
        println(result)
    }

}

源碼分析

public inline fun <T, R> T.let(block: (T) -> R): R {
    contract {
        callsInPlace(block, InvocationKind.EXACTLY_ONCE)
    }
    return block(this)
}
  • let函數(shù)是對(duì)泛型T擴(kuò)展的一個(gè)函數(shù),因此所有類型的變量都可以調(diào)用此函數(shù)。
  • let函數(shù)的入?yún)lock是一個(gè)函數(shù)函數(shù)類型,因此let是一個(gè)高階函數(shù),block的入?yún)⑹荰,就是被擴(kuò)展的對(duì)象,返回值是R。并且整個(gè)let函數(shù)的返回值由block函數(shù)的返回值決定。

apply

fun main() {
    var a = "a"
    a.apply {
        println(this)
    }
}

源碼分析

@kotlin.internal.InlineOnly
public inline fun <T> T.apply(block: T.() -> Unit): T {
    contract {
        callsInPlace(block, InvocationKind.EXACTLY_ONCE)
    }
    block()
    return this
}

apply和let一樣都是對(duì)T進(jìn)行擴(kuò)展,并且入?yún)⒍际呛瘮?shù)類型,因此都是高階函數(shù)。不同的地方在于apply的入?yún)⒗ㄌ?hào)旁邊多了一個(gè)T.

block: T.() -> Unit

這種函數(shù)類型稱為帶接收者的函數(shù)類型,并且接收者是T,也就是被擴(kuò)展的對(duì)象。因此在block函數(shù)中我們便可以使用該接收者T。因此在以上的代碼中,我們直接可以通過(guò)this使用接收者。

 var a = "a"
    a.apply {
        println(this)
    }

4、使用高階函數(shù)改版抽象模板的單例

abstract class BaseSingleInstance<T> {
    @Volatile
    private var instance: T? = null
    protected abstract val creat: () -> T

    fun getInstance(): T {
        return instance ?: synchronized(this) {
            instance ?: creat.invoke().also { instance = it }
        }
    }
}

class UserManager private constructor() {
    companion object : BaseSingleInstance<UserManager>() {
        override val creat: () -> UserManager = ::UserManager
    }
}

fun main() {
    println(UserManager.getInstance().hashCode())
    println(UserManager.getInstance().hashCode())
    println(UserManager.getInstance().hashCode())
}

5、劇終

為什么要使用高階函數(shù)?

  • 為了簡(jiǎn)潔、代碼更少

`

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • 介紹 Kotlin是函數(shù)式編程,所以可以把函數(shù)作為參數(shù)傳遞給函數(shù),或者作為函數(shù)的返回值使用,我們稱其為高階函數(shù)。本...
    燒傷的火柴閱讀 968評(píng)論 0 0
  • 定義:一個(gè)用函數(shù)作為參數(shù)或者返回值的函數(shù)如何定義:()->Unit 括號(hào)里面代表函數(shù)的參數(shù),箭頭后面代表函數(shù)的返回...
    JuliusL閱讀 1,023評(píng)論 0 1
  • 用過(guò)Kotlin的同學(xué)都知道,那些擴(kuò)展方法用起來(lái)簡(jiǎn)直不要太爽,那么這些擴(kuò)展方法是怎么定義實(shí)現(xiàn)的呢,本文介紹了Kot...
    我是黃教主啊閱讀 12,112評(píng)論 0 21
  • 高階函數(shù)的作用 就是用來(lái)定義函數(shù)式編程里面接收Lambda表達(dá)式的函數(shù)。 高階函數(shù)的定義 如果一個(gè)函數(shù)接收另一個(gè)函...
    在下陳小村閱讀 403評(píng)論 0 0
  • 高階函數(shù) 高階函數(shù)可以將函數(shù)作為參數(shù)或者是返回值 forEach提供遍歷集合的功能,forEach其實(shí)是IntAr...
    Guow110閱讀 449評(píng)論 0 0

友情鏈接更多精彩內(nèi)容