無料で使えるシステムトレードフレームワーク「Jiji」 をリリースしました!

・OANDA Trade APIを利用した、オープンソースのシステムトレードフレームワークです。
・自分だけの取引アルゴリズムで、誰でも、いますぐ、かんたんに、自動取引を開始できます。

自己参照するジェネリックタイプの使い方

そんな名前かどうかは知りませんが、「>」みたいな自分で自分を使うジェネリックタイプの使い方。最初見たとき、これはないだろー、と思ったけど、ありみたいです。

どういう時に使うか

  • 自分自身を返すAPIを持つようなクラスで
  • 返す型をパラメータで指定したい場合

に使えます。(きっと他にいい例があるはず・・。こんなのしか思いつかない・・。)

次のようなクラス「Chain」を作りたい。

  • Chainは、任意のChainとつなぐことができる(ことを示す)インターフェイス
    • setNext(), next()を持つ。
  • つなげるChainの型をパラメータタイプで指定できる。
    • パラメータで指定された型にマッチしないChainの実装とはつなげない。
    • パラメータでChain自体が指定された場合、すべてのChainの実装とつながるChainとなる。
  • next()はパラメータで指定された型のChainを返す。

これを実現するため、自己参照するジェネリックタイプを使います。「Chain」を「interface Chain>」と宣言することで、next,setNextで返される値「T」が、「T」を返すChainの派生クラスであるように制限します。

interface Chain<T extends Chain<T>> {
  T next();
  T setNext( T next );
}

利用例は以下。

// Chain の抽象基底クラス
class AbstractChain<T extends Chain<T>>
implements Chain<T>{
    T next;
    public T next () {
        return next;
    }
    public T setNext ( T next ) {
        this.next = next;
        return this.next;
    }
};

// Chainの具象クラス
class ChainImpl  extends AbstractChain<ChainImpl> {};  // ChainImplのみをつなげられるChainImpl
class ChainImpl2 extends AbstractChain<ChainImpl2> {}; // ChainImp2のみをつなげられるChainImp2
class ChainImpl3 extends AbstractChain<Chain> {}; // 任意のchainを受け付けるChainImp3

// ChainImplのみをつなげられるChain
{
    Chain<ChainImpl> c  = new ChainImpl(); // ChainImplのみをつなげられる
    c.setNext( new ChainImpl() );
    //c.setNext( new ChainImpl2()); // コンパイルエラー / 期待通り
    //c.setNext( new ChainImpl3()); // コンパイルエラー / 期待通り
    //c.setNext( new ChainImpl()).setNext( new ChainImpl()).setNext( new ChainImpl3()); // コンパイルエラー / 期待通り

    c.setNext( new ChainImpl() ).setNext( new ChainImpl() ).setNext( new ChainImpl() );
    ChainImpl  next  = c.next();
    ChainImpl  next2 = c.next().next().next().next(); // OK
}

//ChainImpl2のみをつなげられるChain
{
    Chain<ChainImpl2> c  = new ChainImpl2(); // ChainImpl2のみをつなげられる
    //c.setNext( new ChainImpl1() );// コンパイルエラー / 期待通り
    c.setNext( new ChainImpl2());
    //c.setNext( new ChainImpl3()); // コンパイルエラー / 期待通り
    //c.setNext( new ChainImpl2()).setNext( new ChainImpl2()).setNext( new ChainImpl3()); // コンパイルエラー / 期待通り

    c.setNext( new ChainImpl2() ).setNext( new ChainImpl2() ).setNext( new ChainImpl2() );
    Chain<ChainImpl2>  next  = c.next();
    Chain<ChainImpl2>  next2 = c.next().next().next().next(); // OK
}
//任意のchainを受け付けるChain
{
    Chain c = new ChainImpl3(); // 任意のchainを受け付けるChain
    c.setNext( new ChainImpl() );
    c.setNext( new ChainImpl2());
    c.setNext( new ChainImpl3());
    c.setNext( new ChainImpl()).setNext( new ChainImpl2()).setNext( new ChainImpl3());

    Chain  next = c.next();
    Chain  next2 = c.next().next().next().next();
}

試した限りでは期待通りな感じです。

」じゃだめなの?

「interface Chain」では、next,setNextで返される値「T」が、任意のChainの派生クラスであるとしか制限できないため、2回目以降のnext()が「Chain」になってしまうはず。

interface Chain<T extends Chain> {
    T next();
    T setNext( T next );
}
// Chain の抽象基底クラス
class AbstractChain<T extends Chain>
implements Chain<T>{
    T next;
    public T next () {
        return next;
    }
    public T setNext ( T next ) {
        this.next = next;
        return this.next;
    }
};

class ChainImpl<T extends Chain>  extends AbstractChain<T> {};

// Chainの具象クラス
class ChainImpl1 extends AbstractChain<ChainImpl1> {};  // ChainImplのみをつなげられるChainImpl
class ChainImpl2 extends AbstractChain<ChainImpl2> {}; // ChainImp2のみをつなげられるChainImp2
class ChainImpl3 extends AbstractChain<Chain> {}; // 任意のchainを受け付けるChainImp3

// ChainImpl1のみをつなげられるChain
{
    Chain<ChainImpl> c  = new ChainImpl<ChainImpl>(); // ChainImplのみをつなげられる
    c.setNext( new ChainImpl() );
    //c.setNext( new ChainImpl2()); // コンパイルエラー / 期待通り
    //c.setNext( new ChainImpl3()); // コンパイルエラー / 期待通り
    c.setNext( new ChainImpl()).setNext( new ChainImpl()).setNext( new ChainImpl3()); // ★コンパイルエラーにならない。

    c.setNext( new ChainImpl() ).setNext( new ChainImpl() ).setNext( new ChainImpl() );
    ChainImpl  next  = c.next();
    //ChainImpl  next2 = c.next().next().next().next(); // ★コンパイルエラー
}

とおもったけど、ChainImpl1とかは期待通りの動作になった。謎だ。

//ChainImpl1のみをつなげられるChain その2
{
    Chain<ChainImpl1> c  = new ChainImpl1(); // ChainImpl1のみをつなげられる
    c.setNext( new ChainImpl1() );
    //c.setNext( new ChainImpl2()); // コンパイルエラー / 期待通り
    //c.setNext( new ChainImpl3()); // コンパイルエラー / 期待通り
    //c.setNext( new ChainImpl1()).setNext( new ChainImpl1()).setNext( new ChainImpl3()); // コンパイルエラー / 期待通り

    c.setNext( new ChainImpl1() ).setNext( new ChainImpl1() ).setNext( new ChainImpl1() );
    ChainImpl1  next  = c.next();
    ChainImpl1  next2 = c.next().next().next().next(); // OK
}

//ChainImpl2のみをつなげられるChain
{
    Chain<ChainImpl2> c  = new ChainImpl2(); // ChainImpl2のみをつなげられる
    //c.setNext( new ChainImpl1() );// コンパイルエラー / 期待通り
    c.setNext( new ChainImpl2());
    //c.setNext( new ChainImpl3()); // コンパイルエラー / 期待通り
    //c.setNext( new ChainImpl2()).setNext( new ChainImpl2()).setNext( new ChainImpl3()); // コンパイルエラー / 期待通り

    c.setNext( new ChainImpl2() ).setNext( new ChainImpl2() ).setNext( new ChainImpl2() );
    Chain<ChainImpl2>  next  = c.next();
    Chain<ChainImpl2>  next2 = c.next().next().next().next(); // OK
}
//任意のchainを受け付けるChain
{
    Chain c = new ChainImpl3(); // 任意のchainを受け付けるChain
    c.setNext( new ChainImpl1() );
    c.setNext( new ChainImpl2());
    c.setNext( new ChainImpl3());
    c.setNext( new ChainImpl1()).setNext( new ChainImpl2()).setNext( new ChainImpl3());

    Chain  next = c.next();
    Chain  next2 = c.next().next().next().next();
}

というところで今日は力尽きました。