自己参照するジェネリックタイプの使い方
そんな名前かどうかは知りませんが、「
例
次のようなクラス「Chain」を作りたい。
- Chainは、任意のChainとつなぐことができる(ことを示す)インターフェイス。
- setNext(), next()を持つ。
- つなげるChainの型をパラメータタイプで指定できる。
- パラメータで指定された型にマッチしないChainの実装とはつなげない。
- パラメータでChain自体が指定された場合、すべてのChainの実装とつながるChainとなる。
- next()はパラメータで指定された型のChainを返す。
これを実現するため、自己参照するジェネリックタイプを使います。「Chain」を「interface 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
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(); }
というところで今日は力尽きました。