KotlinでサードパーティのAPIを美化する


Scalaは「pimp my library」パターンを普及させました.

This is just a fancy expression to refer to the ability to supplement a library using implicit conversions.

-- Pimp My Library Pattern in Scala


Kotlinは同じ能力を提供します.しかし、拡張機能を介して実現します.生成されたバイトコードはJavaの静的メソッドに似ていますが、開発者の経験は既存の型への関数の追加と同じです.
しかし、このアプローチには限界がある.型階層を更新できません.
オープン/クローズライフサイクルを持つコンポーネントを提供するライブラリを想像してください.コンポーネントをインスタンス化するときに開きますが、ファイル、ストリームなどの使用後に閉じる必要があります.
Java 7の前に、実際にコンポーネントを閉じる必要がありました.
Component component;
try {
    component = new Component();
    // Use component
} finally {
    component.close();
}
Java 7は、リソースステートメントを使ってtryを導入し、次のように記述できます.
try (Component component = new Component()) {
    // Use component
}                                                // 1
  • Component ここで生成されるfinally ブロック
  • しかしComponent 実装すべきAutoCloseable .

    コリンが提供するuse() 拡張機能Closeable .
    したがって、tryを単純な関数呼び出しでリソースステートメントと置き換えることができます.
    Component().use {
      // Use component as it
    }                                                // 1
    
  • Component ここで閉鎖finally ブロック
  • これは、上記のライブラリを実装していないと想像してCloseable nor AutoCloseable . 使えないuse() . コトリン代表団救出!
    委任パターンはオブジェクト指向言語で広く普及しています.

    In delegation, an object handles a request by delegating to a second object (the delegate). The delegate is a helper object, but with the original context. With language-level support for delegation, this is done implicitly by having self in the delegate refer to the original (sending) object, not the delegate (receiving object). In the delegate pattern, this is instead accomplished by explicitly passing the original object to the delegate, as an argument to a method.

    -- Wikipedia


    Javaでのデリゲートパターンを実装するには、多くのboilerplateコードを書く必要があります.元のクラスが持つより多くのメソッドは、より退屈です.
    interface class Component {
        void a();
        void b();
        void c();
    }
    
    public class CloseableComponent extends Component implements Closeable {
    
        private final Component component;
    
        public CloseableComponent(Component component) {
            this.component = component;
        }
    
        void a() { component.a(); }
        void b() { component.b(); }
        void c() { component.c(); }
        public void close() {}
    }
    
    Kotlinは、箱からの代表団パターンをサポートしますby キーワード.上記のコードを書き換えることができます.
    interface Component {
        fun a() {}
        fun b() {}
        fun c() {}
    }
    
    class CloseableComponent(component: Component) : Component by component,
                                                     Closeable {                  // 1
        override fun close() {}
    }
    
  • すべての呼び出しをa() , b() , and c()component
  • 最後に希望するコードを書くことができます.
    CloseableComponent(RealComponent()).use {
        // Use component as it
    }
    
    さらに良い、それはサードパーティ製のコードでは、このアプローチで外部ライブラリを改善するために動作します.
    ケーキのアイシングは、リソースJavaステートメントを使用してコードを呼び出すこともできます.
    try (CloseableComponent component = new CloseableComponent(new RealComponent())) {
        // Use component
    }
    
    上で書いたように、Javaでもできる.しかし、一般的には、委任を実行するために書く必要がある船体板コードの量は重要な障害です.琴は風になる.
    我々は、我々のコードを書くのが簡単にする1つの最後のステップを逃します.どうやってCloseableComponent ? 拡張機能を作成しましょうComponent :
    fun Component.toCloseable() = CloseableComponent(this)
    
    そして現在、使用方法は流暢です.
    RealComponent().toCloseable().use {
        // Use component
    }
    
    このポストでは、サードパーティライブラリによって提供されるAPIを改善する方法を見ました.kotlin拡張機能と委任を組み合わせて達成した.
    さらに進む
  • Extending third-party APIs in different languages
  • Extension functions
  • The try-with-resources Statement
  • Kotlin's use
  • Delegation pattern
  • Kotlin's Delegation
  • 当初公開A Java Geek 2021年12月19日