Kotlin難點

来源:https://www.cnblogs.com/sixrain/archive/2023/05/25/17432522.html
-Advertisement-
Play Games

[toc] # 高階函數 高階函數是將函數用作參數或返回值的函數,還可以把函數賦值給一個變數。 所有函數類型都有一個圓括弧括起來的參數類型列表以及一個返回類型:(A, B) -> C 表示接受類型分別為 A 與 B 兩個參數並返回一個 C 類型值的函數類型。 參數類型列表可以為空,如 () -> A ...


目錄

高階函數

高階函數是將函數用作參數或返回值的函數,還可以把函數賦值給一個變數。

所有函數類型都有一個圓括弧括起來的參數類型列表以及一個返回類型:(A, B) -> C 表示接受類型分別為 A 與 B 兩個參數並返回一個 C 類型值的函數類型。 參數類型列表可以為空,如 () -> A,Unit 返回類型不可省略。

(Int) -> String

函數類型表示法可以選擇性地包含函數的參數名:(x: Int, y: Int) -> Point。 這些名稱可用於表明參數的含義。
(Button, ClickEvent) -> Unit
如需將函數類型指定為可空,請使用圓括弧:((Int, Int) -> Int)?

    fun a(funParam: (Int) -> String): String {
        return funParam(1)
    }

    fun b(param: Int): String {
        return param.toString()
    }

調用

a(::b)
var d = ::b
b(1) // 調用函數
d(1) // 實際上會調用 d.invoke(1)
(::b)(1) // 用對象 :: b 後面加上括弧來實現 b() 的等價操作, 實際上會調用 (::b).invoke(1)
b.invoke(1) // 報錯

對象是不能加個括弧來調用的,但是函數類型的對象可以。為什麼?因為這其實是個假的調用,它是 Kotlin 的語法糖,實際上你對一個函數類型的對象加括弧、加參數,它真正調用的是這個對象的 invoke() 函數

雙冒號

:: 創建一個函數引用或者一個類引用

函數引用

fun isOdd(x: Int) = x % 2 != 0

我們可以很容易地直接調用它(isOdd(5)),但是我們也可以將其作為一個函數類型的值,例如將其傳給另一個函數。為此,我們使用 :: 操作符:

val numbers = listOf(1, 2, 3)
println(numbers.filter(::isOdd))

這裡 ::isOdd 是函數類型 (Int) -> Boolean 的一個值。

如果我們需要使用類的成員函數或擴展函數,它需要是限定的,例如 String::toCharArray。

    val args: Array<String> = arrayOf("1", "2")
  args.filter(String::isNotEmpty) 
  
  class PdfPrinter {
        fun println(any: Any) {
            kotlin.io.println(any)  //重名了可以用包名調用
        }
    }
      val pdfPrinter = PdfPrinter()
      args.forEach(pdfPrinter::println)

類引用

val c = MyClass::class

該引用是 KClass 類型的值
請註意,Kotlin 類引用與 Java 類引用不同。要獲得 Java 類引用, 請在 KClass 實例上使用 .java 屬性。
平時寫的類,其信息都可以在這個KClass來獲取

屬性引用

data class MediaItem(val title: String, val url: String)

var items= mutableListOf<MediaItem>()
items
    .sortedBy { it.title }
    .map { it.url }
    .forEach { print(it) }

items
    .sortedBy(MediaItem::title)
    .map(MediaItem::url)
    .forEach(::println)

匿名函數

沒有名字的函數
要傳一個函數類型的參數,或者把一個函數類型的對象賦值給變數,除了用雙冒號來拿現成的函數使用,你還可以直接把這個函數挪過來寫:

fun b(param: Int): String {
    return param.toString()
}

a(fun b(param: Int): String {
  return param.toString()
});

val d = fun b(param: Int): String {
  return param.toString()
}

//名字沒意義,省略
a(fun(param: Int): String {
  return param.toString()
});
val d = fun(param: Int): String {
  return param.toString()
}

如果你在 Java 里設計一個回調的時候是這麼設計的:

public interface OnClickListener {
  void onClick(View v);
}
public void setOnClickListener(OnClickListener listener) {
  this.listener = listener;
}

使用的時候是這麼用的:

view.setOnClickListener(new OnClickListener() {
  @Override
  void onClick(View v) {
    switchToNextPage();
  }
});

kotlin寫法

fun setOnClickListener(onClick: (View) -> Unit) {
  this.onClick = onClick
}
view.setOnClickListener(fun(v: View): Unit) {
  switchToNextPage()
})

Lambda寫法:

view.setOnClickListener({ v: View ->
  switchToNextPage()
})

Lambda 表達式

簡化匿名函數,代碼更簡潔

    view.setOnClickListener({ v: View ->
        switchToNextPage()
    })

//如果 Lambda 是函數的最後一個參數,你可以把 Lambda 寫在括弧的外面:
    view.setOnClickListener() { v: View ->
        switchToNextPage()
    }
//而如果 Lambda 是函數唯一的參數,你還可以直接把括弧去了:
    view.setOnClickListener { v: View ->
        switchToNextPage()
    }
//另外,如果這個 Lambda 是單參數的,它的這個參數也省略掉不寫:
//根據上下文推導,根據最後一行代碼來推斷出返回值類型
    view.setOnClickListener {
        switchToNextPage()
    }

Lambda 表達式的完整語法形式如下:

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

多參數例子:
fold函數:將所提供的操作應用於集合元素並返回累積的結果

val items = listOf(1, 2, 3, 4, 5)

// Lambdas 表達式是花括弧括起來的代碼塊。
items.fold(0, { 
    // 如果一個 lambda 表達式有參數,前面是參數,後跟“->”
    acc: Int, i: Int -> 
    print("acc = $acc, i = $i, ") 
    val result = acc + i
    println("result = $result")
    // lambda 表達式中的最後一個表達式是返回值:
    result
})

// lambda 表達式的參數類型是可選的,如果能夠推斷出來的話:
val joinedToString = items.fold("Elements:", { acc, i -> acc + " " + i })

輸出:

acc = 0, i = 1, result = 1
acc = 1, i = 2, result = 3
acc = 3, i = 3, result = 6
acc = 6, i = 4, result = 10
acc = 10, i = 5, result = 15
joinedToString = Elements: 1 2 3 4 5

總結:
函數不能直接傳遞或者賦給某個變數,需要函數類型實例化,有三種方式:

使用已有聲明的可調用引用
1.函數引用

使用函數字面值的代碼塊

2.匿名函數
3.lambda 表達式

例子

實現介面

var onVideoStartCallBack: (() -> Unit)? = null

onVideoStartCallBack?.invoke()

videioView.onVideoStartCallBack = {

}

函數里實現介面

object UploaderListHelper {

    fun startTaskUpload(activity: Activity, startCallBack: ((Int) -> Unit)?) {
        startCallBack.invoke(position)
    }
}

UploaderListHelper.startTaskUpload(activity) {
    refreshProgress(it)
}

作用域函數

Kotlin 標準庫包含幾個函數,它們的唯一目的是在對象的上下文中執行代碼塊。當對一個對象調用這樣的函數並提供一個 lambda 表達式時,它會形成一個臨時作用域。在此作用域中,可以訪問該對象而無需其名稱。這些函數稱為作用域函數。共有以下五種:let、run、with、apply 以及 also。

這些函數基本上做了同樣的事情:在一個對象上執行一個代碼塊。不同的是這個對象在塊中如何使用,以及整個表達式的結果是什麼。
目的:簡潔

 val person = findPerson();
        //person是可null的,所以需要?
        println(person?.age)
        println(person?.name)
        //上面太麻煩,findPerson加了?,所以後面不需要了,減少的判空操作。let可以安全調用
        findPerson()?.let { person ->
            person.work()
            println(person.age)
        }
        //還可以更簡潔,person也不用寫
        findPerson()?.apply {
            work()
            println(age)
        }

使⽤時可以通過簡單的規則作出一些判斷
返回自身
返回值是它本身
從 apply 和 also 中選
作⽤域中使⽤ this 作為參數選擇 apply

val adam = Person("Adam").apply {
    age = 32
    city = "London"        
}
println(adam)

作⽤域中使⽤ it 作為參數選擇 also

val numbers = mutableListOf("one", "two", "three")
numbers
    .also { println("The list elements before adding new one: $it") }
    .add("four")

with 非擴展函數

val numbers = mutableListOf("one", "two", "three")
with(numbers) {
    println("'with' is called with argument $this")
    println("It contains $size elements")
}

不需要返回自身
從 run 和 let 中選擇
作用域中使用 this 作為參數,選擇 run
作用域中使用 it 作為參數,選擇 let, 適合配合空判斷的時候

val service = MultiportService("https://example.kotlinlang.org", 80)

val result = service.run {
    port = 8080
    query(prepareRequest() + " to port $port")
}

// 同樣的代碼如果用 let() 函數來寫:
val letResult = service.let {
    it.port = 8080
    it.query(it.prepareRequest() + " to port ${it.port}")
}

it作為參數的好處
let 允許我們自定義參數名字,使可讀性更強,如果傾向可讀性可以選擇 T.let

參考文章
Kotlin 的高階函數、匿名函數和 Lambda 表達式
Kotlin官網


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • ### GC 優化 #### 1.防止大對象Buffer到記憶體中 **現象**:當大包請求時,YGC 耗時嚴重 **原因**:預設情況下 Zuul2 並不會緩存請求體(DirectByteBuffer),也就意味著它會先發送接收到的請求 Headers 到後端服務,之後接收到請求體再繼續發送到後端服 ...
  • **我是 javapub,一名 `Markdown` 程式員從👨‍💻,八股文種子選手。** **面試官: 上個面試官對你的基礎有了一定瞭解,聽說你小子很不錯!下麵我們聊點有深度的。** **面試官: 簡單介紹下 CAS 你瞭解什麼?** **候選人:** CAS是Compare And Swap ...
  • NameServer是一個註冊中心,提供服務註冊和服務發現的功能。NameServer可以集群部署,集群中每個節點都是對等的關係(沒有像ZooKeeper那樣在集群中選舉出一個Master節點),節點之間互不通信。 **服務註冊** Broker啟動的時候會向所有的NameServer節點進行註冊, ...
  • 本篇帶你走進AIGC的基本使用,一步一步註冊ChatGPT,申請自己的API進行使用,解決代理的問題,最後介紹如何本地部署ChatGPT,以及通過免費雲平臺搭建代理轉發,從而不需要使用魔法就可以訪問。 ...
  • JVM(Java虛擬機)是Java程式的運行環境,它可以通過一些系統參數進行配置和優化。以下是一些常用的JVM系統參數: 1. -Xmx: 用於設置JVM堆的最大記憶體大小。例如,-Xmx1g表示將堆的最大大小設置為1GB。 2. -Xms: 用於設置JVM堆的初始記憶體大小。例如,-Xms512m表示 ...
  • 基本數據類型和字元串類型的自動轉換<%@ taglib prefix="form" uri="http://www.springframework.org/tags/form" %> <%@ page contentType="text/html;charset=UTF-8" language="j ...
  • [toc] 你好!我是[@馬哥python說](https://www.zhihu.com/people/13273183132),一名10年程式猿,正在試錯用pyecharts開發可視化大屏的非常規排版。 以下,我用8種ThemeType展示的同一個可視化數據大屏,可視化主題是分析**“淄博燒烤” ...
  • 來源:https://www.duidaima.com/Group/Topic/JAVA/11942 ## **1、什麼是狀態機** ### 1.1 什麼是狀態 先來解釋什麼是“狀態”( State )。現實事物是有不同狀態的,例如一個自動門,就有 open 和 closed 兩種狀態。我們通常所說 ...
一周排行
    -Advertisement-
    Play Games
  • Timer是什麼 Timer 是一種用於創建定期粒度行為的機制。 與標準的 .NET System.Threading.Timer 類相似,Orleans 的 Timer 允許在一段時間後執行特定的操作,或者在特定的時間間隔內重覆執行操作。 它在分散式系統中具有重要作用,特別是在處理需要周期性執行的 ...
  • 前言 相信很多做WPF開發的小伙伴都遇到過表格類的需求,雖然現有的Grid控制項也能實現,但是使用起來的體驗感並不好,比如要實現一個Excel中的表格效果,估計你能想到的第一個方法就是套Border控制項,用這種方法你需要控制每個Border的邊框,並且在一堆Bordr中找到Grid.Row,Grid. ...
  • .NET C#程式啟動閃退,目錄導致的問題 這是第2次踩這個坑了,很小的編程細節,容易忽略,所以寫個博客,分享給大家。 1.第一次坑:是windows 系統把程式運行成服務,找不到配置文件,原因是以服務運行它的工作目錄是在C:\Windows\System32 2.本次坑:WPF桌面程式通過註冊表設 ...
  • 在分散式系統中,數據的持久化是至關重要的一環。 Orleans 7 引入了強大的持久化功能,使得在分散式環境下管理數據變得更加輕鬆和可靠。 本文將介紹什麼是 Orleans 7 的持久化,如何設置它以及相應的代碼示例。 什麼是 Orleans 7 的持久化? Orleans 7 的持久化是指將 Or ...
  • 前言 .NET Feature Management 是一個用於管理應用程式功能的庫,它可以幫助開發人員在應用程式中輕鬆地添加、移除和管理功能。使用 Feature Management,開發人員可以根據不同用戶、環境或其他條件來動態地控制應用程式中的功能。這使得開發人員可以更靈活地管理應用程式的功 ...
  • 在 WPF 應用程式中,拖放操作是實現用戶交互的重要組成部分。通過拖放操作,用戶可以輕鬆地將數據從一個位置移動到另一個位置,或者將控制項從一個容器移動到另一個容器。然而,WPF 中預設的拖放操作可能並不是那麼好用。為瞭解決這個問題,我們可以自定義一個 Panel 來實現更簡單的拖拽操作。 自定義 Pa ...
  • 在實際使用中,由於涉及到不同編程語言之間互相調用,導致C++ 中的OpenCV與C#中的OpenCvSharp 圖像數據在不同編程語言之間難以有效傳遞。在本文中我們將結合OpenCvSharp源碼實現原理,探究兩種數據之間的通信方式。 ...
  • 一、前言 這是一篇搭建許可權管理系統的系列文章。 隨著網路的發展,信息安全對應任何企業來說都越發的重要,而本系列文章將和大家一起一步一步搭建一個全新的許可權管理系統。 說明:由於搭建一個全新的項目過於繁瑣,所有作者將挑選核心代碼和核心思路進行分享。 二、技術選擇 三、開始設計 1、自主搭建vue前端和. ...
  • Csharper中的表達式樹 這節課來瞭解一下表示式樹是什麼? 在C#中,表達式樹是一種數據結構,它可以表示一些代碼塊,如Lambda表達式或查詢表達式。表達式樹使你能夠查看和操作數據,就像你可以查看和操作代碼一樣。它們通常用於創建動態查詢和解析表達式。 一、認識表達式樹 為什麼要這樣說?它和委托有 ...
  • 在使用Django等框架來操作MySQL時,實際上底層還是通過Python來操作的,首先需要安裝一個驅動程式,在Python3中,驅動程式有多種選擇,比如有pymysql以及mysqlclient等。使用pip命令安裝mysqlclient失敗應如何解決? 安裝的python版本說明 機器同時安裝了 ...