Generics - 複数のインタフェースを実装していることを示す
Generics では型パラメータに extends を使用することで、指定可能な型に制約を課すことが出来ます。
例えば以下の例だと、Sample#invokeFoo メソッドで宣言されている型パラメータ T には、 IFoo インタフェースを実装したクラスのみを指定できます。
// IFoo インタフェース
public interface IFoo {
public void foo();
}
// Generics により、キャストの必要なくメソッドを呼び出せる例 public class Sample { public <T extends IFoo> void invokeFoo(T t) { t.foo(); // IFoo のメソッドを使用可能 } }
<T extends IFoo> の記述により、 T の 型が少なくとも IFoo であることが保証されます。
このため、メソッド引数の t に対して明示的なキャストを行なうことなく、IFoo のメソッドを呼び出すことが可能です。
Generics を使用しなくてもメソッド引数 t の型を IFoo として宣言すれば、上記と同様のことは実現できます。
// 引数の型を IFoo にすることで、Generics を使用せずともキャストが要らない例 public class Sample { public void invokeFoo(IFoo t) { t.foo(); // Generics でなくとも可能 } }
しかし、Generics を使用しない場合は一つの型しか、引数の型として指定できません。このため、引数のオブジェクトが IFoo 以外に IBar インタフェースも実装していて、その両方のメソッドを呼び出す場合に、明示的なキャストが必要になります。
// IBar インタフェース
public interface IBar {
public void bar();
}
// IFoo と IBar 両方の型であることを引数型では宣言できない public class Sample { public void invokeFooBar(IFoo t) { t.foo();t.bar();// コンパイルエラー ((IBar)t).bar(); // 明示的なキャストが必要 } }
上記例では、引数に渡された型が確かに IBar を実装していることを、コンパイル時に保証できません。そのため、実行時にキャストが必要になり、ClassCastException の発生する可能性があります。
キャストを行なわないようにするには、IFoo および IBar の両方を継承したインタフェース IFooBar を作成し、引数の型を IFooBar とするか、引数を二つに分ける必要があります。
// IFooBar インタフェースを作成して引数の型とする public interface IFooBar extends IFoo, IBar { } public class Sample { public void invokeFooBar(IFooBar fooBar) { fooBar.foo(); fooBar.bar(); } } // あるいは引数を二つに分ける public class Sample { public void invokeFooBar(IFoo ifoo, IBar ibar) { ifoo.foo(); ibar.bar(); } } // しかし引数を二つに分けると・・・ public void useSampleClass() { FooBarImpl fooBarImpl = new FooBarImpl();// FooBarImpl は IFoo と IBar が実装されている (new Sample()).invokeFooBar(fooBarImpl, fooBarImpl);// 同じオブジェクトを2回引数に渡す必要がある; }
Generics を使用すると、このような問題をスマートに解決可能です。Generics では <T extends IFoo & IBar> のように & を使用することで、型パラメータが複数の型を実装していることを宣言できます。このため、明示的なキャストを行なうことなく、IFoo および IBar のメソッドを呼び出すことが可能です。
// Generics を使用すれば、IFoo と IBar 両方の型であることを宣言できる public class Sample { public <T extends IFoo & IBar> void invokeFooBar(T t) { // t は IFoo および IBar であることが保証されている t.foo(); // IFoo のメソッドを呼び出せる t.bar(); // IBar のメソッドもそのまま呼び出せる } }