型パラメータのスコープとシャドウ化の実験
Javaのジェネリクスの復習をしてヘンなコードを書いてみた。「やってはいけない」のカタマリみたいなコードである。
package x; class A {} class X {} // ...(1) public class Container<X extends A> { // ...(2) private X content; // ...(3) public <X> Container(X content) { // ...(4) // this.content = content; ...(5) assignHelper(content); // ...(6) } private void assignHelper(Object x) { this.content = (X) x; // ...(7) } public X get() { // ...(8) return content; } public boolean isEmpty() { return content == null; } public static<X extends A> Container<X> wrap(X wrappable) { // ...(9) return new Container<>(wrappable); // ...(10) } }
- ただのpackageプライベートなクラスで、型名は
X
。 - クラス・スコープの型パラメータ
X
。クラスA
を拡張した任意の型を示す。もちろんすぐ上で宣言されているクラスX
との間には何の関係もない。 - 同じ型のフィールド。クラス
A
を拡張した任意の型のインスタンスへの参照を格納できる。 - コンストラクタ・スコープの型パラメータ
X
。クラスA
うんぬんの制約はなし。クラス・スコープの同名のパラメータをシャドウ化してしまう。もちろん引数content
の型X
もコンストラクタ・スコープの型パラメータX
。 - 結果このコンストラクタ内ではクラス・スコープの型パラメータ
X
と同じ型のフィールドthis.content
への代入が不可能になる。 - しかたないので
asignHelper(Object)
を呼び出して代入を行う。 - この
X
はクラス・スコープの型パラメータX
なのでキャストも代入も成功する。 - この
X
もクラス・スコープの型パラメータX
。 - このstaticメソッドのシグネチャに登場するいずれもメソッド・スコープの型パラメータ
X
で、前述のいずれのX
とも無関係。 - しかしその宣言部で
X extends A
と規定されているためContainer<X>
が型として有効になりメソッド・スコープの型パラメータX
の引数wrappable
をともないコンストラクタを呼び出すことができる。