Kotlin 實現類代理是通過 by
關鍵字,本文嘗試講解類代理在 Kotlin 的具體使用和實現原理。
首先,在一個自定義的 View 當中實現一個接口如下:
interface CanvasCapabilities {
fun draw(circle: Circle)
fun draw(rectangle: Rect)
}
class ArtboardView(context: Context): View(context), CanvasCapabilities
現在我們再創建 CanvasCapabilities
接口的實現:
class ArtboardCanvas: CanvasCapabilities {
override fun draw(circle: Circle) = ...
override fun draw(rectangle: Rect) = ...
}
這個時候,我們一般會使用接口代理方式實現我們的邏輯,比如:
class ArtboardView(context: Context): View(context), CanvasCapabalities {
private val artboardCanvas = ArtboardCanvas() // proxy
override fun draw(circle: Circle) = artboardCanvas.draw(circle)
override fun draw(rectangle: Rect) = artboardCanvas.draw(rectangle)
}
這種方式實現,雖然邏輯已經和 View 分離開,但是 View 里面還是比較混亂。
那應該怎么辦呢?通過關鍵字 by
實現類代理優化代碼
class ArtboardView(context: Context): View(context), CanvasCapabilities by ArtboardCanvas()
代理類 ArtboardCanvas
直接實現 CanvasCapabilities
接口,不需要再實現內部定義 proxy 方式,代碼非常簡潔。
探究原理
通過反編譯成 Java 代碼發現原理非常簡單,Kotlin 并沒有做任何黑魔法,只是在內部再生成對應的 delegate 方法。
public final class ArtboardView extends View implements CanvasCapabilities {
private final ArtboardCanvas $$delegate_0;
public ArtboardView(@NotNull Context context) {
Intrinsics.checkParameterIsNotNull(context, "context");
super(context);
this.$$delegate_0 = new ArtboardCanvas();
}
public void draw(@NotNull Rect rectangle) {
Intrinsics.checkParameterIsNotNull(rectangle, "rectangle");
this.$$delegate_0.draw(rectangle);
}
public void draw(@NotNull Circle circle) {
Intrinsics.checkParameterIsNotNull(circle, "circle");
this.$$delegate_0.draw(circle);
}
}
這種方式實現,代碼非常簡潔。但是如果你有很多方法都需要實現,就會對應生成很多方法,可能會影響一些包大小。
拓展
構造函數傳入類代理
class MySet(private val delegate: Set<Long> = HashSet()) : Set<Long> by delegate
實現多個類代理
class MySetMap : Set<Long> by HashSet(), Map<Long, Long> by HashMap() {
override val size: Int
get() = TODO("not implemented")
override fun isEmpty(): Boolean {
TODO("not implemented")
}
}
結論
當你遇到一些場景,類需要對外提供接口,以及需要實現多個接口,通過類代理這種方式抽取邏輯,可以讓代碼非常簡潔。