Kotlin 優雅的接口實現

1. 日常遇到的冗余的接口方法實現

日常開發中,經常會要實現接口,但是很多場景中,只需要用到其中一兩個方法,例如 ActivityLifecycleCallbacks,它有很多個接口需要實現,但是很多時候我們只需要用到其中的一兩個

    val myActivityLifecycleCallbacks = object : Application.ActivityLifecycleCallbacks  {/*** 例如我們只需要監聽 Activity 的創建和銷毀,那么 onActivityStarted, onActivityResumed, onActivityPaused* onActivityStopped,onActivityStopped,onActivitySaveInstanceState 這 6 個方法是完全沒必要實現的,* 即使是空實現*/override fun onActivityCreated(activity: Activity, savedInstanceState: Bundle?) {TODO("Not yet implemented")}override fun onActivityStarted(activity: Activity) {TODO("Not yet implemented")}override fun onActivityResumed(activity: Activity) {TODO("Not yet implemented")}override fun onActivityPaused(activity: Activity) {TODO("Not yet implemented")}override fun onActivityStopped(activity: Activity) {TODO("Not yet implemented")}override fun onActivitySaveInstanceState(activity: Activity, outState: Bundle) {TODO("Not yet implemented")}override fun onActivityDestroyed(activity: Activity) {TODO("Not yet implemented")}}

如果有多個不同業務需要實現這個接口,就這樣很容易產生代碼冗余。有沒有一種優雅的方式,只需要實現自己需要的方法而不再需要去關注其他方法?有的,那就是利用 Java 的動態代理和 kotlin 的委托模式

2. 利用 Java 的動態代理和 Kotlin 的委托模式

首先需要實現一個通用的動態代理,新建一個 Kotlin 文件 DelegateObject.kt,這里通過 inlinereified 關鍵字,獲取到泛型的 class 信息

import java.lang.reflect.InvocationHandler
import java.lang.reflect.Proxyinline fun <reified T> noOpDelegate() : T {val javaClass = T::class.javareturn Proxy.newProxyInstance(javaClass.classLoader, arrayOf(javaClass), no_op_invocationHandler) as T
}val no_op_invocationHandler = InvocationHandler { _, _, _ -> }

這樣就可以獲取到任意一個接口的一個對象,只是沒有具體的實現。接著再利用 Kotlin 的 by 關鍵字實現對象委托

    val myActivityLifecycleCallbacks = object : Application.ActivityLifecycleCallbacks by noOpDelegate() {}

由于 Kotlin 委托模式的原理,實際上在編譯期間也是會生成 ActivityLifecycleCallbacks 的所有方法,先來看看轉譯后的實現

   private final Application.ActivityLifecycleCallbacks myActivityLifecycleCallbacks = (Application.ActivityLifecycleCallbacks)(new Application.ActivityLifecycleCallbacks() {// $FF: synthetic fieldprivate final Application.ActivityLifecycleCallbacks $$delegate_0;{int $i$f$noOpDelegate = false;Class javaClass$iv = Application.ActivityLifecycleCallbacks.class;Object var10001 = Proxy.newProxyInstance(javaClass$iv.getClassLoader(), new Class[]{javaClass$iv}, DelegateObjectKt.getNo_op_invocationHandler());if (var10001 == null) {throw new NullPointerException("null cannot be cast to non-null type android.app.Application.ActivityLifecycleCallbacks");} else {this.$$delegate_0 = (Application.ActivityLifecycleCallbacks)var10001;}}public void onActivityCreated(@NonNull @NotNull Activity activity, @Nullable @org.jetbrains.annotations.Nullable Bundle savedInstanceState) {Intrinsics.checkNotNullParameter(activity, "activity");this.$$delegate_0.onActivityCreated(activity, savedInstanceState);}public void onActivityDestroyed(@NonNull @NotNull Activity activity) {Intrinsics.checkNotNullParameter(activity, "activity");this.$$delegate_0.onActivityDestroyed(activity);}public void onActivityPaused(@NonNull @NotNull Activity activity) {Intrinsics.checkNotNullParameter(activity, "activity");this.$$delegate_0.onActivityPaused(activity);}public void onActivityResumed(@NonNull @NotNull Activity activity) {Intrinsics.checkNotNullParameter(activity, "activity");this.$$delegate_0.onActivityResumed(activity);}public void onActivitySaveInstanceState(@NonNull @NotNull Activity activity, @NonNull @NotNull Bundle outState) {Intrinsics.checkNotNullParameter(activity, "activity");Intrinsics.checkNotNullParameter(outState, "outState");this.$$delegate_0.onActivitySaveInstanceState(activity, outState);}public void onActivityStarted(@NonNull @NotNull Activity activity) {Intrinsics.checkNotNullParameter(activity, "activity");this.$$delegate_0.onActivityStarted(activity);}public void onActivityStopped(@NonNull @NotNull Activity activity) {Intrinsics.checkNotNullParameter(activity, "activity");this.$$delegate_0.onActivityStopped(activity);}});

現在已經將 ActivityLifecycleCallbacks 的匿名內部類對象委托給了 noOpDelegate 生成的代理對象。這樣需要用到具體哪個方法時,只需要再次重寫即可,例如文章最開始的例子可以變為

    val myActivityLifecycleCallbacks = object : Application.ActivityLifecycleCallbacks by noOpDelegate() {override fun onActivityCreated(activity: Activity, savedInstanceState: Bundle?) {TODO("Not yet implemented")}override fun onActivityDestroyed(activity: Activity) {TODO("Not yet implemented")}}

經過精簡的代碼可以使代碼更加簡潔,可以更好的聚焦業務實現

3. 注意接口方法存在返回值問題

如果實現的接口中的方法帶有返回值,務必要重寫該方法,不然會報 IllegalArgumentException 異常。 這也算是這種優雅方式中一個缺點。來看個例子,首先定義一個接口

interface TestNoDelegateInterface {fun testFun1()fun testFun2(): Int
}

該接口定義了兩個方法,其中一個方法有 int 類型的返回值,使用 noOpDelegate 實現該接口

val testNoDelegateInterface = object : TestNoDelegateInterface by noOpDelegate() {
}

testNoDelegateInterface 去調用 testFun2() 方法

testNoDelegateInterface.testFun2()

控制臺將打印報錯信息

AndroidRuntime: FATAL EXCEPTION: main
AndroidRuntime: Process: com.example.mydemoapplication, PID: 25135
AndroidRuntime: java.lang.IllegalArgumentException: result has type int, got kotlin.Unit
AndroidRuntime: 	at $Proxy2.testFun2(Unknown Source)

而當 testNoDelegateInterface 去調用 testFun1() 方法時則沒有這個問題

原因是在使用動態代理反射實現 TestNoDelegateInterface 接口的代理對象時,傳入的 InvocationHandler 實際是個空對象,當通過 Kotlin 委托生成的接口方法需要一個返回值,而代理對象在實際執行方法時由于沒有具體實現,導致兩個方法的返回類型不一致,最終報錯。先看下 testNoDelegateInterface 轉譯成的 java 代碼

      final <undefinedtype> testNoDelegateInterface = new TestNoDelegateInterface() {// 委托模式將 testNoDelegateInterface 的能力委托給了由動態代理創建的 $$delegate_0 對象private final TestNoDelegateInterface $$delegate_0;{int $i$f$noOpDelegate = false;Class javaClass$iv = TestNoDelegateInterface.class;Object var10001 = Proxy.newProxyInstance(javaClass$iv.getClassLoader(), new Class[]{javaClass$iv}, DelegateObjectKt.getNo_op_invocationHandler());if (var10001 == null) {throw new NullPointerException("null cannot be cast to non-null type com.example.mydemoapplication.TestNoDelegateInterface");} else {this.$$delegate_0 = (TestNoDelegateInterface)var10001;}}public void testFun1() {this.$$delegate_0.testFun1();}public int testFun2() {/*** 動態代理的對象的方法調用最終都會執行到 InvocationHandler#invoke(java.lang.Object, java.lang.reflect.Method, java.lang.Object[]) * 的方法實現,因為通用動態代理傳入的 no_op_invocationHandler 是個空實現,所以這里調用并不會返回一個期望的返回值*/return this.$$delegate_0.testFun2();}};

綜上,需要實現帶返回值的接口,這樣就不會報錯了

        val testNoDelegateInterface = object : TestNoDelegateInterface by noOpDelegate() {// 重寫帶返回的方法,具體返回的值按照業務需求實現override fun testFun2(): Int {return Int.MIN_VALUE}}

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

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

相關文章

Java List 自定義對象排序 Java 8 及以上版本使用 Stream API

從 Java 8 開始&#xff0c;你可以使用 Stream API 對 List 進行排序&#xff0c;這種方式更加簡潔和靈活。 以下是一個示例代碼&#xff1a; import java.util.ArrayList; import java.util.Comparator; import java.util.List; import java.util.stream.Collectors;// 自定…

【Spring詳解一】Spring整體架構和環境搭建

一、Spring整體架構和環境搭建 1.1 Spring的整體架構 Spring框架是一個分層架構&#xff0c;包含一系列功能要素&#xff0c;被分為大約20個模塊 Spring核心容器&#xff1a;包含Core、Bean、Context、Expression Language模塊 Core &#xff1a;其他組件的基本核心&#xff…

Linux內核讀寫鎖與讀寫信號量的區別及選用

在Linux內核中&#xff0c;讀寫鎖&#xff08;rwlock_t&#xff09;和讀寫信號量&#xff08;struct rw_semaphore&#xff09;是兩種不同的同步機制&#xff0c;適用于不同的場景。以下是它們的區別和選用建議&#xff1a; 核心區別 特性讀寫鎖 (rwlock_t)讀寫信號量 (struct…

用openresty和lua實現壁紙投票功能

背景 之前做了一個隨機壁紙接口&#xff0c;但是不知道大家喜歡對壁紙的喜好&#xff0c;所以干脆在實現一個投票功能&#xff0c;讓用戶給自己喜歡的壁紙進行投票。 原理說明 1.當訪問http://demo.com/vote/時&#xff0c;會從/home/jobs/webs/imgs及子目錄下獲取圖片列表&…

LLaMA 3.1 模型在DAMODEL平臺的部署與實戰:打造智能聊天機器人

文章目錄 前言 一、LLaMA 3.1 的特點 二、LLaMA3.1的優勢 三、LLaMA3.1部署流程 &#xff08;一&#xff09;創建實例 &#xff08;二&#xff09;通過JupyterLab登錄實例 &#xff08;3&#xff09;部署LLaMA3.1 &#xff08;4&#xff09;使用教程 總結 前言 LLama3…

【Python爬蟲(25)】解鎖Python爬蟲:數據存儲的最優選擇與高效策略

【Python爬蟲】專欄簡介&#xff1a;本專欄是 Python 爬蟲領域的集大成之作&#xff0c;共 100 章節。從 Python 基礎語法、爬蟲入門知識講起&#xff0c;深入探討反爬蟲、多線程、分布式等進階技術。以大量實例為支撐&#xff0c;覆蓋網頁、圖片、音頻等各類數據爬取&#xff…

【復現DeepSeek-R1之Open R1實戰】系列8:混合精度訓練、DeepSpeed、vLLM和LightEval介紹

這里寫目錄標題 1 混合精度訓練1.1 FP16和FP321.2 優點1.3 存在的問題1.4 解決辦法 2 DeepSpeed3 vLLM3.1 存在的問題3.2 解決方法3.2.1 PagedAttention3.2.2 KV Cache Manager3.2.3 其他解碼場景 3.3 結論 4 LightEval4.1 主要功能4.2 使用方法4.3 應用場景 本文繼續深入了解O…

使用 FFmpeg 剪輯視頻指南

FFmpeg 是一個功能強大的多媒體處理工具&#xff0c;可以進行視頻和音頻的剪輯、合并、轉碼等操作。本文將詳細介紹如何使用 FFmpeg 進行視頻剪輯&#xff0c;并通過實例幫助你快速掌握剪輯技巧。我們會從最基礎的剪切功能講起&#xff0c;再延伸到一些高級操作&#xff0c;如指…

【分布式理論15】分布式調度1:分布式資源調度的由來與過程

文章目錄 一、操作系統的資源調度&#xff1a;從單核到多核二、 分布式系統的資源調度&#xff1a;從單臺服務器到集群三、 固定資源映射四、 動態資源分配&#xff1a;靈活的任務-資源匹配五、 資源調度過程&#xff1a;從申請到執行 本文主要討論主題&#xff1a; 從操作系統…

【Linux C/C++開發】Linux系統輕量級的隊列緩存mqueue

前言 開發設計時&#xff0c;通常會對業務流程進行模塊化&#xff0c;有些流程之間&#xff0c;不要求同步&#xff0c;但又需要傳遞信息時&#xff0c;如果存儲到數據庫&#xff0c;效率降低很多&#xff0c;如果是存放在內存是最好的。此時可以選擇系統的IPC&#xff08;進程…

Vue 實現通過URL瀏覽器本地下載 PDF 和 圖片

1、代碼實現如下&#xff1a; 根據自己場景判斷 PDF 和 圖片&#xff0c;下載功能可按下面代碼邏輯執行 const downloadFile async (item: any) > {try {let blobUrl: any;// PDF本地下載if (item.format pdf) {const response await fetch(item.url); // URL傳遞進入i…

計算機網絡基礎雜談(局域網、ip、子網掩碼、網關、DNS)

目錄 1. 簡單局域網的構成 2. IP 地址 3. 子網掩碼 4. IP地址詳解自定義IP 5. IP 地址詳解 6. 網關 7. DNS 域名解析 8. ping 1. 簡單局域網的構成 交換機是組建局域網最重要的設備&#xff0c;換句話說&#xff0c;沒有交換機就沒法搭建局域網 交換機不能讓局域網連…

Thor: 統一AI模型網關的革新之選

項目價值 Thor(雷神托爾)作為一個強大的AI模型管理網關&#xff0c;解決了當前AI領域一個關鍵痛點&#xff1a;不同AI服務商的API格式各異&#xff0c;集成成本高。Thor通過將各種AI模型的獨特格式統一轉換為OpenAI格式&#xff0c;顯著降低了開發者的使用門檻和維護成本。 核…

25年2月通信基礎知識補充:多普勒頻移與多普勒擴展、3GPP TDL信道模型

看文獻過程中不斷發現有太多不懂的基礎知識&#xff0c;故長期更新這類blog不斷補充在這過程中學到的知識。由于這些內容與我的研究方向并不一定強相關&#xff0c;故記錄不會很深入請見諒。 【通信基礎知識補充7】25年2月通信基礎知識補充1 一、多普勒頻移與多普勒擴展傻傻分不…

【Python】Python入門——筆記合集

哈哈 00、環境搭建 學習Python&#xff0c;首先需要搭建一個本地開發環境&#xff0c;或是使用線上開發環境&#xff08;各類練習網站&#xff09;&#xff0c;這篇博客里主要記錄了本地開發環境的配置方法。內容包括python解釋器的安裝以及pycharm的安裝、漢化等。 博客地…

為什么mvcc中?m_ids 列表并不等同于 min_trx_id 和 max_trx_id 之間的所有事務 ID

首先我們要明確 m_ids 表示創建 ReadView 時&#xff0c;系統中所有活躍&#xff08;未提交&#xff09;事務的事務 ID 列表。 僅包含當前未提交的事務&#xff0c;與事務 ID 的數值范圍無關。 min_trx_id 是 m_ids 中的最小值。若 m_ids 為空&#xff0c;則 min_trx_id 等于…

使用 Spark NLP 實現中文實體抽取與關系提取

在自然語言處理(NLP)領域,實體抽取和關系提取是兩個重要的任務。實體抽取用于從文本中識別出具有特定意義的實體(如人名、地名、組織名等),而關系提取則用于識別實體之間的關系。本文將通過一個基于 Apache Spark 和 Spark NLP 的示例,展示如何實現中文文本的實體抽取和…

FPGA開發要學些什么?如何快速入門?

隨著FPGA行業的不斷發展&#xff0c;政策的加持和投入的研發&#xff0c;近兩年FPGA行業的薪資也是水漲船高&#xff0c;一些人轉行后拿到了薪資30W&#xff0c;甚至有一些能力強的人可以拿到60W&#xff0c;看到這里想必不少人表示很心動&#xff0c;但又不知道怎么轉&#xf…

使用Python和正則表達式爬取網頁中的URL數據

在數據抓取和網絡爬蟲開發中&#xff0c;提取網頁中的URL是一個常見的需求。無論是用于構建網站地圖、分析鏈接結構&#xff0c;還是進行內容聚合&#xff0c;能夠高效地從HTML文檔中提取URL都是一個重要的技能。Python作為一種強大的編程語言&#xff0c;結合其正則表達式模塊…

人工智能之目標追蹤DeepSort源碼解讀(yolov5目標檢測,代價矩陣,余弦相似度,馬氏距離,匹配與預測更新)

要想做好目標追蹤,須做好目標檢測,所以這里就是基于yolov5檢測基礎上進行DeepSort,叫它為Yolov5_DeepSort。整體思路是先檢測再追蹤,基于檢測結果進行預測與匹配。 一.參數與演示 這里用到的是coco預訓練人的數據集&#xff1a; 二.針對檢測結果初始化track 對每一幀數據都輸出…