Kotlin學習快速入門(8)—— 委托

来源:https://www.cnblogs.com/stars-one/archive/2022/07/21/16501954.html
-Advertisement-
Play Games

原文地址:Kotlin學習快速入門(8)—— 屬性委托 - Stars-One的雜貨小窩 委托其實是一種設計模式,但Kotlin把此特性編寫進了語法中,可以方便開發者快速使用 委托對應的關鍵字是by 屬性委托 先講下屬性委托吧,首先,複習下kotlin中設置set和get方法 預設的set和get我 ...


原文地址:Kotlin學習快速入門(8)—— 屬性委托 - Stars-One的雜貨小窩
委托其實是一種設計模式,但Kotlin把此特性編寫進了語法中,可以方便開發者快速使用

委托對應的關鍵字是by

屬性委托

先講下屬性委托吧,首先,複習下kotlin中設置set和get方法

預設的set和get我們可以隱藏,實際上一個簡單的類代碼如下:

class Person {
	var personName = ""
	// 這是預設的 get/set(預設是隱藏的)
	get() = field
	set(value) {
		field = value
	}
}

這裡具體知識點可以查看之前所說Kotlin學習快速入門(3)——類 繼承 介面 - Stars-One的雜貨小窩

當然,如果是數據bean類,我們會將get和set方法隱藏(或者使用data關鍵字來聲明一個數據類)

若我們需要在get或set方法的時候做一下邏輯處理,比如說上面的personName欄位,我們只允許接收長度小於等於10的字元串,超過10長度的字元串就不接收(即不設置新數值),則是應該這樣寫:

class Person{
    var personName = ""
        // 這是重寫的 get/set
        get() = "PersonName $field"
        set(value) {
            field = if (value.length <= 10) value else field
        }
}

然後,我們再延伸出來,如果此規則不止應用於personName欄位,還可用到其他類的欄位中,這個時候就是使用到屬性委托。

簡單描述: 我們將此規則抽取出來,需要應用到此規則的欄位的get/set方法委托給規則去做,這就叫屬性委托

延遲載入(懶載入)

在開始講屬性委托之前,先說明下延遲載入

Kotlin中提供了lazy方法,使用by+lazy{}聯用,我們就實現延遲載入(也可稱作懶載入)

fun main() {

    val demo = Demo()
    val textContent = demo.textContent
    val result = demo.textContent.substring(1)
    println(result)
    println("列印:$textContent")
}

class Demo{

    val textContent by lazy { loadFile() }

}
fun loadFile(): String {
    println("讀取文件...")
    //模擬讀取文件返回數據
    return "讀取的數據"
}

這裡的關鍵詞by出現在屬性名後面,表示屬性委托,即將屬性的讀和寫委托給另一個對象,被委托的對象必須滿足一定的條件:

  • 對於 val 修飾的只讀變數進行屬性委托時,被委托的對象必須實現getValue()介面,即定義如何獲取變數值。
  • 對於 var 修飾的讀寫變數進行屬性委托時,被委托對象必須實現getValue()setValue()介面,即定義如何讀寫變數值。

lazy()方法,接收一個lambda函數,返回值是一個Lazy對象,所以就可以簡寫成上面的樣子,其只實現了getValue()介面,所以,當你嘗試將textContent改為var類型,IDE會提示報錯!!

也是因為這點,屬於延遲載入的欄位,是不可被再次修改了,所以採用lazy懶載入的方式,其實就是單例模式

Delegates.vetoable

還記得上述我們要實現的規則嗎,其實Kotlin中已經有了幾個預設的委托規則供我們快速使用(上述的lazy其實也是一個)

Delegates.vetoable()的規則就是上述規則的通用封裝,解釋為:

但會在屬性被賦新值生效之前會傳遞給Delegates.vetoable()進行處理,依據Delegates.vetoable()的返回的布爾值判斷要不要賦新值。

如下麵例子:

class Person {
    var personName by Delegates.vetoable("") { property, oldValue, newValue ->
        //當設置的新值滿足條件,則會設置為新值
        newValue.length <= 10
    }
}

Delegates.notNull

設置欄位不能為null,不過想不到具體的應用情景

class Person {
    var personName by Delegates.notNull<String>()
}

Delegates.observable

使用Delegates.observable可以幫我們快速實現觀察者模式,只要欄位數值發生改變,就會觸發

class Person{
    var age by Delegates.observable(0){ property, oldValue, newValue ->
        //這裡可以寫相關的邏輯
        if (newValue >= 18) {
            tip = "已成年"
        }else{
            tip = "未成年"
        }
    }

    var tip =""
}

上面的例子就比較簡單,設置age同時更新提示,用來判斷是否成年

 val person = Person()
    person.age = 17
    println(person.tip)

補充-自定義委托

上述都是官方定義好的一些情形,但如果不滿足我們的需求,這就需要自定義委托了

官方提供了兩個基礎類供我們自定義委托使用:

ReadWriteProperty 包含get和set方法,對應var關鍵字
ReadOnlyProperty 只有get方法,對應val關鍵字

PS:實際上,我們自己隨意創建個委托類也是可以的,不過這樣寫不太規範,所以我們一般直接實現官方給的上述兩個類即可

ReadWriteProperty和ReadOnlyProperty都需要傳兩個泛型,分別為R,T

  • R 持有屬性的類型
  • T 欄位類型

可能上面描述不太明白,下麵給個簡單例子,Person類中有個name欄位(String),首字母需要大寫:

class Person {
    var name by NameToUpperCase("")
}

class NameToUpperCase(var value:String) :ReadWriteProperty<Person,String>{
    //NameToUpperCase類中預設有個屬性欄位,用來存數據
    
    override fun getValue(thisRef: Person, property: KProperty<*>): String {
        return this.value
    }

    override fun setValue(thisRef: Person, property: KProperty<*>, value: String) {
        //在設置數值的時候,將第一個字母轉為大寫,一般推薦在setValue里編寫邏輯
        this.value = value.substring(0,1).toUpperCase()+value.substring(1)
    }
}

個人看法,一般在setValue的時候進行設置數值比較好,因為getValue作操作的話,會觸發多次,處理的邏輯複雜的話可能會浪費性能...

當然,再提醒下,上面的邏輯也可以直接去欄位里的setValue()裡面改,委托只是方便抽取出去供其他類應用同樣的規則

PS: 如果你的委托不是針對特定的類,R泛型可以改為Any

類委托

這個一般與多態一起使用,不過個人想不到有什麼具體的應用情景,暫時做下簡單的記錄

interface IDataStorage{
    fun add()
    fun del()
    fun query()
}

class SqliteDataStorage :IDataStorage{
    override fun add() {
        println("SqliteDataStorage add")
    }

    override fun del() {
        println("SqliteDataStorage del")
    }

    override fun query() {
        println("SqliteDataStorage query")
    }

}

假如現在我們有個MyDb類,查詢的方法與SqliteDataStorage這個裡的方法有所區別,但其他方法都是沒有區別,這個時候就會用到類委托了

有以下幾種委托的使用方式

1.委托類作為構造器形參傳入(常用)

class MyDb(private val storage:IDataStorage) : IDataStorage by storage{
    override fun add() {
        println("mydb add")
    }
}
val db = MyDb(SqliteDataStorage())
db.add()
db.query()

輸出結果:

mydb add
SqliteDataStorage query

如果是全部都是委托給SqliteDataStorage的話,可以簡寫為這樣:

class MyDb(private val storage:IDataStorage) : IDataStorage by storage

2.新建委托類對象

class MyDb : IDataStorage by SpDataStorage(){
    override fun add() {
        println("mydb add")
    }
}

這裡測試的效果與上文一樣,不在重覆贅述

參考


提問之前,請先看提問須知 點擊右側圖標發起提問 聯繫我 或者加入QQ群一起學習 Stars-One安卓學習交流群 TornadoFx學習交流群:1071184701
您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 背景 在生產過程中,由於磁碟空間、保留周期等因素,會對系統、應用等日誌提出要求,要求系統日誌定期進行輪轉、壓縮和刪除,從而減少開銷,而系統自帶的logrotate 則是一個簡單又實用的小工具,下麵著重介紹一下,滿足日常需求。 語法 Usage: logrotate [OPTION...] <conf ...
  • 六、DSL操作ES 6.1、RESTful風格介紹 REST(Representational State Transfer 表述性狀態轉移),是一組架構約束條件和原則,滿足這些約束條件和原則的應用程式或設計就是RESTful。就是一種定義介面的規範。有以下特征: 基於HTTP 可以使用XML格式定 ...
  • 一、ElasticSearch的倒排索引 1.1、資料庫存在的問題 問題1: 查詢title中包含"手機"的信息 SELECT * FROM goods WHERE title LIKE '%手機%' 使用模糊搜索,左邊有通配符,不會走索引,會全表掃描,性能低 問題2:查詢title中包含"我要買一 ...
  • 首先明確一下定義: 內連接:在多表查詢的時候,只檢索出表之間相互匹配的行(滿足匹配條件的行),結果中不包括兩個表之間不匹配的行。 一個不太準確但易懂的例子:比如有兩個表,表1存儲了 (1,2,3,4,5 )這五個數字。表2存儲了(3,4,5,6,7,8)這6個數字。多表查詢時兩個表的關聯條件是 “表 ...
  • 分享嘉賓:管正雄 阿裡雲 高級演算法工程師 出品平臺:DataFunTalk **導讀:**面對海量的用戶問題,有限的支持人員該如何高效服務好用戶?智能QA生成模型給業務帶來的提效以及如何高效地構建演算法服務,為業務提供支持。本文將介紹:阿裡雲計算平臺大數據產品答疑場景;基於達摩院AliceMind預訓 ...
  • 原文鏈接:當我們在聊「開源大數據調度系統Taier」的數據開發功能時,到底在討論什麼? 課件獲取:關註公眾號__ “數棧研習社”,後臺私信 “Taier”__ 獲得直播課件 視頻回放:點擊這裡 Taier 開源項目地址:github 丨 gitee 喜歡我們的項目給我們點個__ STAR!STAR! ...
  • Postico for Mac是Mac平臺上一款功能強大的PostgreSQL客戶端,Postico Mac版憑藉著簡單易用的操作界面深受專業人員和新手的喜愛。 詳情:Postico for Mac(超好用的PostgreSQL客戶端) 功能特色 1、查看你的資料庫,然後根據需要編輯 Postico ...
  • 本文包含SQL基礎語法、函數、索引、sql編程、事務、事務原則、備份恢復,許可權資料庫設計、三大範式、JDBC、SQL註入、資料庫連接池等所有知識都在這裡了 ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...