Kotlin作用域函數:掌握apply/let/run/with/also精髓

一、作用域函數詳解

1.?apply:對調用對象進行配置或操作,并返回該對象本身。

  • 接收者引用this(可省略,直接調用接收者成員)
  • 返回值:接收者對象本身(T
  • 核心用途:對對象進行初始化或配置,返回配置后的對象
  • null 安全:不支持(接收者需非 null)
  • 作用域:接收者作用域(this?指向接收者)
open val sessionHandler: Handler by lazy {Handler(HandlerThread(tag).apply { this.start() } // 啟動線程并返回 HandlerThread 實例.looper // 獲取線程的 Looper)
}

????????apply用于啟動HandlerThread線程并返回該線程實例,從而讓后續代碼能順利獲取其looper來初始化Handler

????????具體來看,HandlerThread(tag).apply { this.start() }這部分代碼中,apply的 lambda 表達式里通過this.start()調用了HandlerThreadstart方法來啟動線程,由于apply會返回調用它的對象(即HandlerThread實例),所以后續能繼續通過.looper獲取該線程的消息循環器,進而將其作為參數傳遞給Handler完成初始化。

????????這種寫法的優勢在于通過鏈式調用讓代碼更緊湊,避免了額外的變量聲明,同時利用apply的作用域特性讓this直接指向HandlerThread實例,使代碼邏輯更清晰簡潔。?

2.?let:其核心作用是對非空對象執行操作,并返回 lambda 表達式的結果。

  • 接收者引用it(隱式參數,作為 lambda 的唯一參數)
  • 返回值:lambda 的執行結果(任意類型)
  • 核心用途:處理 null 值,限定變量作用域,返回新計算結果
  • null 安全:支持(配合安全調用符??.
  • 作用域:獨立作用域(it?僅在 lambda 內可見)
extras?.let {// 當 extras 不為空時執行此代碼塊val metadataList = it.getParcelableArrayList<MediaBrowserCompat.MediaItem>(MEDIAITEM_LIST_KEY) ?: emptyList()var itemToPlay = it.getParcelable<MediaBrowserCompat.MediaItem>(MEDIAITEM_KEY)// 當未獲取到媒體項時,從 mediaId 構建默認媒體項if (metadataList.isEmpty() && itemToPlay == null && mediaId != null) {// ... 構建 MediaItem 的邏輯 ...}// 準備播放列表preparePlaylist(mediaId ?: "", metadataList, itemToPlay, true, it)
}

????????extras?.let { ... }?的主要作用是確保?extras?不為空時執行后續邏輯,同時避免了重復的空值檢查。

????????具體來說,let?在這里的工作方式如下:首先,通過安全調用操作符??.,代碼會先檢查?extras?是否為 null,只有當?extras?不為空時,才會執行?let?中的 lambda 表達式,這有效避免了?NullPointerException

????????其次,在 lambda 表達式內部,it?代表非空的?extras?對象,這使得代碼更簡潔,無需重復引用?extras。例如,it.getParcelableArrayList(...)?等價于?extras.getParcelableArrayList(...),但無需重復檢查?extras?是否為空。

????????此外,let?還能控制變量的作用域,在?let?內部定義的變量(如?metadataListitemToPlay)僅在該作用域內可見,避免了變量泄漏。如果不使用?let,代碼需要顯式檢查?extras?是否為空,會更冗長,而?let?讓代碼更緊湊,同時保持空安全。這種寫法符合 Kotlin 的空安全設計理念,使代碼更簡潔、更安全。?

    3.?also:對調用對象執行額外操作后返回該對象本身

    • 接收者引用it(隱式參數,作為 lambda 的唯一參數)
    • 返回值:接收者對象本身(T,同?apply
    • 核心用途:執行副作用操作(如日志、賦值),保持對象鏈式調用
    • null 安全:不支持(接收者需非 null)
    • 作用域:獨立作用域(it?僅在 lambda 內可見)
    override fun setShuffleMode(shuffleMode: Int) {synchronized(mediaSession) {mediaSession.setShuffleMode(shuffleMode)}// 將系統 shuffleMode 轉換為內部 PlayMode 枚舉PlayMode.LOOP.shuffle(shuffleMode)?.also { playMode ->// 執行副作用操作(更新狀態和發送事件)setPlayMode(playMode)           // 更新內部播放模式sendNextModeEvent(playMode)     // 通知模式變更}
    }

    ????????PlayMode.LOOP.shuffle(shuffleMode)?.also { ... }?的主要作用是在將系統隨機模式(shuffleMode)轉換為應用內部的播放模式(PlayMode)后,執行副作用操作(如更新狀態和發送事件),同時保持鏈式調用的流暢性。

    ????????具體來說,also?在這里的工作方式如下:首先,PlayMode.LOOP.shuffle(shuffleMode)?嘗試將傳入的系統隨機模式轉換為對應的?PlayMode?枚舉值,若轉換成功則返回非空值,否則返回 null。通過安全調用操作符??.,代碼確保僅在轉換結果非空時執行?also?內的 lambda 表達式,避免了空指針異常。

    ????????在 lambda 表達式內部,setPlayMode(playMode)?更新應用的內部播放模式狀態,而?sendNextModeEvent(playMode)?則發送模式變更事件通知,這兩個操作均屬于對?playMode?對象的副作用處理。

    ????????最后,also?會返回原始的轉換結果(即?playMode),盡管在這段代碼中沒有后續調用,但這種設計保持了 API 的靈活性。如果不使用?also,代碼需要額外的變量聲明和條件判斷,會更冗長,而?also?讓代碼更緊湊,同時保持空安全,符合 Kotlin 的函數式編程風格。?

    4.?run:接收者作用域的 “全能選手”

    • 接收者引用this(可省略,直接調用接收者成員)
    • 返回值:lambda 的執行結果(任意類型)
    • 核心用途:在接收者作用域內執行代碼塊,混合調用成員方法和外部函數
    • null 安全:不支持(需手動校驗接收者 null)
    • 作用域:接收者作用域(優先訪問接收者成員)
    代碼示例:
    // 計算文件內容長度
    val file = File("data.txt")
    val contentLength = file.run { if (exists()) readText().length else 0
    }// 鏈式函數調用
    "Android".run {toUpperCase()       // 調用接收者方法
    }.run { "$this Kotlin"      // 處理中間結果
    }.run(::println)      // 調用外部函數(打印結果)
    最佳實踐:
    • 成員訪問:簡化接收者成員調用(如?view.run { setText("OK") }
    • 混合邏輯:同時使用接收者方法(length)和外部函數(println

    5.?withrun?的參數化變體

    • 接收者引用:參數傳入(非擴展函數,直接在 lambda 中使用接收者)
    • 返回值:lambda 的執行結果(同?run
    • 核心用途:以非擴展函數形式使用?run,顯式傳入接收者
    • null 安全:不支持(需手動校驗接收者 null)
    • 作用域:接收者作用域(同?run
    代碼示例:
    // 顯式傳入接收者(非擴展函數調用)
    val result = with(ArrayList<String>()) {add("A")add("B")size  // 返回 lambda 結果
    }// 數學計算場景
    val point = Point(3, 4)
    val distance = with(point) { sqrt(x*x + y*y)  // 直接訪問 x/y 屬性(假設 Point 有 x/y 成員)
    }
    最佳實踐:
    • 多對象操作:當接收者不是調用對象時(如?with(list, ::process)
    • 替代 run:習慣參數化調用時使用(與?run?功能完全一致)

    三、對比表格:快速選擇指南

    函數接收者引用返回值核心用途null 安全作用域類型典型場景
    applythis接收者對象對象配置接收者作用域初始化對象、設置屬性
    letitlambda 結果空安全處理、返回新值是(?.獨立作用域處理 nullable 對象、限定作用域
    runthislambda 結果成員操作 + 函數調用接收者作用域混合調用對象方法和外部函數
    with參數傳入lambda 結果非擴展函數形式的?run接收者作用域顯式傳入接收者、多對象操作
    alsoit接收者對象鏈式副作用(日志、賦值)獨立作用域保持對象鏈式調用,執行附加操作

    四、最佳實踐與避坑指南

    1.?對象配置首選?apply

    當需要對對象進行初始化或設置屬性時,apply?能避免臨時變量,使代碼更流暢:

    // 推薦:直接返回配置后的對象
    val button = Button().apply {text = "提交"setOnClickListener { ... }
    }

    2.?null 安全首選?let

    處理可為 null 的對象時,let?配合??.?是最佳選擇:

    // 避免 NPE:安全調用 + let
    networkResponse?.let { handle(it) }  

    3.?成員訪問首選?run/with

    當需要頻繁調用接收者成員(如?file.readText())時,run?或?with?更簡潔:

    // 簡化成員訪問
    file.run {if (exists()) readText() else ""
    }

    4.?鏈式副作用首選?also

    執行日志記錄、變量賦值等非核心操作時,also?能保持對象鏈式調用:

    // 鏈式流程中插入日志
    downloadFile().also { logDownload(it) }.also { saveToCache(it) }

    5.?避免混淆返回值

    • apply/also?返回接收者對象,適合繼續配置(如?.apply(...).also(...)
    • let/run?返回 lambda 結果,適合生成新值(如?val result = obj.let { ... }

    五、總結:選擇的藝術

    Kotlin 的作用域函數是函數式編程與面向對象的完美結合,掌握它們的關鍵在于:

    • 明確目標:配置對象用?apply,處理 null 用?let,混合邏輯用?run
    • 關注返回值:需保持對象鏈式調用選?apply/also,需計算結果選?let/run
    • 代碼風格:習慣擴展函數用?apply/let/run,習慣參數化調用用?with

    ? ? ? ?這些函數并非互斥,而是互補。例如,apply?配合?also?可實現 “配置 + 日志” 的復合操作,let?配合?run?可處理 null 值并執行復雜邏輯。熟練運用這組工具,能讓代碼兼具簡潔性與可讀性,真正體現 Kotlin 的優雅與高效。

    本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
    如若轉載,請注明出處:http://www.pswp.cn/bicheng/86444.shtml
    繁體地址,請注明出處:http://hk.pswp.cn/bicheng/86444.shtml
    英文地址,請注明出處:http://en.pswp.cn/bicheng/86444.shtml

    如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

    相關文章

    Spring Boot監視器:應用監控終極指南

    Spring Boot 監視器詳解 Spring Boot 監視器(Monitor)是用于監控和管理 Spring Boot 應用程序運行狀態的核心組件,主要通過 Spring Boot Actuator 和 Spring Boot Admin 兩大工具實現。 一、核心監視器組件 1. Spring Boot Actuator 功能定位:提供應用程序內部運行狀態的原…

    SpringBoot 中 @Transactional 的使用

    SpringBoot 中 Transactional 的使用 一、Transactional 的基本使用二、Transactional 的核心屬性三、使用避坑&#xff08;失效場景&#xff09;3.1 自調用問題3.2 異常處理不當3.3 類未被 Spring 管理3.4 異步方法內使用失效 四、工作實踐4.1 事務提交之后執行一些操作4.2 事…

    6.26_JAVA_微服務_Elasticsearch

    1、ES文檔中keyword意思是&#xff1a;字符串&#xff0c;但不需要分詞 2、ES細節CreateIndexRequest request new CreateIndexRequest("items");會讓你導包&#xff0c;會有兩個選擇&#xff1a; import org.elasticsearch.action.admin.indices.create.CreateInd…

    Java 大視界 -- 基于 Java 的大數據可視化在智慧城市能源消耗動態監測與優化決策中的應用(324)

    Java 大視界 -- 基于 Java 的大數據可視化在智慧城市能源消耗動態監測與優化決策中的應用&#xff08;324&#xff09; 引言&#xff1a;正文&#xff1a;一、Java 驅動的能源數據采集與預處理基建1.1 多源異構數據合規接入層&#xff08;ISO 50001IEC 61850 雙標準適配&#x…

    C++ 快速回顧(二)

    C 快速回顧&#xff08;二&#xff09; 前言一、友元類二、友元函數三、深淺拷貝淺拷貝深拷貝 前言 用于快速回顧之前遺漏或者補充C知識 一、友元類 友元的優點是可以快速的輕松的訪問的原本由于私有保護的字段和函數&#xff0c;同時這也是它的缺點這樣破壞了原本封裝性。 …

    ldl-DeserializationViewer一款強大的序列化數據可視化工具

    ldl-DeserializationViewer 一款強大的序列化數據可視化工具&#xff0c;能夠將Java序列化的緩存數據轉換為可讀的JSON格式&#xff0c;無需原始DTO類定義。 A powerful visualization tool for serialized data that converts Java serialized cache data to readable JSON f…

    NetworkSecurity SIG成立,助力國產操作系統安全生態發展

    近期&#xff0c;ZeroOnes實驗室團隊成員在OpenAtom openKylin&#xff08;簡稱“openKylin”&#xff09;社區發起成立NetworkSecurity SIG&#xff0c;負責基于openKylin系統開展網絡安全工具的研發與適配&#xff0c;助力國產操作系統安全生態發展。 ZeroOnes實驗室專注于網…

    回歸任務與分類任務的區別

    回歸任務&#xff08;Regression&#xff09;與分類任務&#xff08;Classification&#xff09;是機器學習的兩大核心任務類型&#xff0c;其根本區別在于輸出變量的性質和任務目標。以下是系統性對比&#xff1a; 1. 本質區別&#xff1a;輸出變量類型 任務類型輸出&#xf…

    Webshell工具的流量特征分析(菜刀,蟻劍,冰蝎,哥斯拉)

    Webshell工具的流量特征分析&#xff08;菜刀&#xff0c;蟻劍&#xff0c;冰蝎&#xff0c;哥斯拉&#xff09; 0x00 前言 使用各種的shell工具獲取到目標權限&#xff0c;即可進行數據操作&#xff0c;今天來簡要分析一下目前常使用的各類shell管理工具的流量特診&#xff…

    【linux】全志Tina配置swupdate工具進行分區打包

    一、文件路徑 1、描述文件&#xff1a; .\build\swupdate\sw-description-ab 2、鏡像打包文件&#xff1a; .\build\swupdate\sw-subimgs-ab.cfg 二、文件作用 1、sw-description-ab 用于描述版本信息和ab區中要打包的分區信息以及掛載點。 2、sw-subimgs-ab.cfg 用于…

    MicroPython網絡編程:AP模式與STA模式詳解

    文章目錄 1. MicroPython網絡模塊概述2. 熱點AP模式詳解2.1 什么是AP模式?2.2 AP模式特點2.3 AP模式設置代碼2.4 AP模式適用場景3. 客戶端STA模式詳解3.1 什么是STA模式?3.2 STA模式特點3.3 STA模式設置代碼3.4 STA模式適用場景4. AP與STA模式對比分析5. 實際應用場景與選擇建…

    Ubuntu網絡數據包發送工具大全

    在Ubuntu系統中&#xff0c;有多種工具可以用于發送網絡數據包&#xff0c;包括UDP、TCP、ICMP等協議。以下是一些常用的工具及其簡要介紹&#xff1a; 1. Packet Sender 功能&#xff1a;支持發送和接收TCP、UDP和SSL數據包&#xff0c;提供圖形界面和命令行工具。安裝&…

    小學期前端三件套學習(更新中)

    第一階段 HTML 基礎結構 <!DOCTYPE html> <html><head><title>頁面標題</title></head><body>頁面內容</body> </html>常用內容標簽 文本類標簽 ? <h1>~<h6>&#xff1a;標題&#xff08;h1 每個頁面建…

    高斯混合模型(Gaussian Mixture Model, GMM)

    高斯混合模型&#xff08;Gaussian Mixture Model, GMM&#xff09; 是一種 概率模型&#xff0c;用于表示數據點由多個高斯分布&#xff08;Gaussian Distribution&#xff09;混合生成的過程。它廣泛應用于 聚類分析、密度估計、圖像分割、語音識別 等領域&#xff0c;尤其適…

    MCP Client 開發 -32000 報錯

    在開發 MCP Client 的過程中&#xff0c;發生了 -32000 報錯&#xff0c;源碼如下&#xff1a; import json from typing import Optional from contextlib import AsyncExitStackfrom openai import OpenAIfrom mcp import ClientSession, StdioServerParameters from mcp.cl…

    使用zabbix監控Nginx服務的配置方法

    準備 要監控Nginx的服務狀態&#xff0c;首先需要安裝nginx的status模塊&#xff1a;ngx_http_stub_status_module 首先 查看Nginx是否有安裝該模塊&#xff1a;--with-http_stub_status_module nginx -V 如果沒有安裝的話&#xff0c;安裝方法可以參照&#xff1a;Nginx新…

    簡易服務器(TCP)

    1.簡單介紹以及項目技術和開發環境 本文將通過epoll完成對客戶端請求的處理&#xff0c;通過多線程完成對客戶端發送數據的處理&#xff0c;并提交到遠端mysql 需要的使用到的一些技術有&#xff1a;socket網絡套接字編程、IO多路轉接的epoll、多線程&#xff08;包括互斥鎖和條…

    總結前端三年 理想滾燙與現實的冰冷碰撞

    大家好&#xff0c;我是500佰&#xff0c;技術宅男 目前正在前往獨立開發路線&#xff0c;我會在這里分享關于編程技術、獨立開發、技術資訊以及編程感悟等內容 6月3日的一篇《一個普通人的30歲 他經歷了什么》介紹一篇自己的碎碎念、即回顧自己以前的成長經歷&#xff0c;那么…

    微服務網關/nacos/feign總結

    現在學習到的組件 1.nacos&#xff1a;注冊中心&#xff0c;用于微服務之間交流的第三方管家&#xff0c;與生產者建立心跳契約對其監聽&#xff0c;注冊中心維護一張生產者的活躍表&#xff0c;會將活躍表實時更新并推送給消費者。 2.feign&#xff1a;nacos只是對生產者進行…

    WebSocket 協議詳解

    WebSocket 協議詳解 1. WebSocket 協議的幀數據詳解 1.1 幀結構 0 1 2 30 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1--------------------------------------------------------|F|R|R|R| opco…