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
,這里通過 inline
和 reified
關鍵字,獲取到泛型的 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}}