「4.8. 未加工型」の編集履歴(バックアップ)一覧に戻る

4.8. 未加工型 - (2012/09/09 (日) 23:34:57) のソース

*[[4. 型と値と変数]]
**[[4.1. 型と変数の種類]]
**[[4.2. プリミティブ型と値]]
**[[4.3. 参照型と値]]
**[[4.4. 型変数]]
**[[4.5. 引数付き型]]
**[[4.6. 型の抹消]]
**[[4.7. 具象可能型]]
**4.8. 未加工型
非ジェネリックのレガシーコードとのインタフェースを容易にするため、[[引数付き型>4.5. 引数付き型]]の[[抹消>4.6. 型の抹消]]や構成要素の型が引数付き型である[[配列型>10.1. 配列型]]の抹消されたものを型として利用できます。このような型を&i(){未加工型(raw type)}と呼びます。

もう少し正確には、未加工型とは次の1つとして定義されます。:
-型実引数リストを持たないジェネリック型宣言の名前という形式の参照型
-構成要素の型が未加工型である配列型
-未加工型Rの非静的メンバー型、Rのスーパークラスやスーパーインタフェースから継承されていないこと

非ジェネリッククラス型やインタフェース型は未加工型ではありません。

#divstyle(background-color:#f0f0ff;border:1px solid black;padding 4px){{{

&i(){&small(){未加工型の非静的型メンバーがなぜ未加工とみなせるのか、以下の例を考えます。:}}

 class Outer<T>{
     T t;
     class Inner {
         T setOuterT(T t1) { t = t1; return t; }
     }
 }

&i(){&small(){&tt(){Inner}のメンバーの型は&tt(){Outer}の型引数に依存しています。もし&tt(){Outer}が未加工であるなら、&tt(){Inner}は&tt(){T}に対する有効な束縛がないため同様に未加工として扱わなければなりません。

このルールは継承していない型メンバーの場合のみ適用可能です。型変数に依存する継承された型メンバーは未加工型のスーパータイプを消去するというルールにより生まれた未加工型として継承されます。これは後で記述します。

上記のルールともう1つ密接に関係するのが未加工型のジェネリック内部クラスはそれ自身未加工型としてのみ使用できるということです。:

 class Outer<T>{
     class Inner<S> {
         S s;
     }
 }

部分的な未加工型(生焼け型(rare type))である&tt(){Inner}にはアクセスできません。:

 Outer.Inner<Double> x = null;  // illegal
 Double d = x.s;

なぜならば、&tt(){Outer}自身は未加工のため、&tt(){Inner}を含むその全ての内部クラスも未加工となるので、&tt(){Inner}へ型実引数を渡すことができなくなるためです。

}}}

未加工型のスーパークラス(とスーパーインタフェース)はその引数付き呼び出しにおけるスーパークラス(スーパーインタエース)の抹消です。

スーパークラスやスーパーインタフェースから継承されない未加工型Cの[[コンストラクター>8.8. コンストラクター宣言]]、インスタンスメソッド([[8.4.>8.4. メソッド宣言]]、[[9.4.>9.4. 抽象メソッド宣言]])もしくは非静的[[フィールド>8.3. フィールド宣言]]Mの型は未加工型です。その未加工型はCと対応したジェネリック宣言内のその型の抹消に該当するものです。

未加工型Cの静的メソッドや静的フィールドの型はCと対応したジェネリック宣言内のその型と同じです。

&bold(){型実引数を未加工型のスーパークラスやスーパーインタフェースから継承されない非静的型メンバーに渡そうとするとコンパイル時にエラーとなります。}

&bold(){引数付き型の型メンバーを未加工型として使用しようとするとコンパイル時にエラーとなります。}

#divstyle(background-color:#f0f0ff;border:1px solid black;padding 4px){{{

&i(){&small(){これは"生焼け(rare)"型の追放を限定型が引数付きであるケースにも拡張していることを意味していますが、内部クラスを未加工型として使用しようとすると以下のようになります。:}}

 Outer<Integer>.Inner x = null; // illegal

&i(){&small(){これは上で述べたことの逆です。この半分焼けた(half-baked)型の実践的正当化はありません。レガシーコード内では、型実引数は使われません。非レガシーコード内では、ジェネリック型を正しく使い必要とされる全ての型実引数を与えるべきです。}}

}}}

クラスのスーパータイプは未加工型であっても構いません。そのクラスへのメンバーアクセスは通常と同じに扱われ、スーパータイプへのメンバーアクセスは未加工型に対するように扱われます。クラスのコンストラクター内では、&tt(){super}の呼び出しを未加工型のメソッド呼び出しとして扱います。

未加工型の使用はレガシーコードとの互換性を得るための譲歩でしかありません。Javaプログラミング言語にジェネリクスが導入されて以降に書かれたコード内での未加工型の使用は強く反対します。Javaプログラミング言語の将来のバージョンでは未加工型の使用は禁止されるでしょう。

型ルールの潜在的違反を常に廃絶することを確かにするために、未加工型のメンバーへアクセスがあれば、コンパイル時に未検査警告が出力されます。未加工型のコンストラクターのメソッドをアクセスする際のコンパイル時の未検査警告のルールは以下の通りです。:
-フィールドへの代入の時点: 左オペランドの型が未加工型なら、抹消によりフィールドの型が変更されるとして未検査警告をコンパイル時に出力する。
-メソッドやコンストラクターの呼び出しの時点: [[検索>15.12.1. コンパイル時ステップ1: 検索するクラスまたはインタフェースの決定]]するクラスやインタフェースの型が未加工型なら、抹消によりメソッドやコンストラクターの仮引数の型が変更されるとして未検査警告をコンパイル時に出力する。
-抹消があっても仮引数の型に変更がないメソッド呼び出し(戻り型やスロー節に変更があるとしても)について、フィールドの読出しについて、もしくは未加工型のクラスインスタンスの作成については、コンパイル時に未検査警告を出力しない。

&i(){&small(){上記の未検査警告は[[未検査変換>5.1.9. 未検査変換]]や[[キャスト>5.5.2. 検査済みキャストと未検査キャスト]]、メソッド宣言([[8.4.1.>8.4.1. 仮引数]]、[[8.4.8.3.>8.4.8.3. 上書きと隠ぺいの要件]]、[[8.4.8.4.>8.4.8.4. 上書き等価シグネチャー付き継承メソッド]]、[[9.4.1.2.>9.4.1.2. 上書きの要件]])、[[可変項数メソッド呼び出し>15.12.4.2. 引数の評価]]で発生する未検査警告とは異なります。}}

&i(){&small(){ここでの警告はレガシーコード開発者がジェネリックライブラリを使用した場合を想定したものです。例えば、ライブラリは&tt(){Vector<T>}型のフィールド&tt(){f}を持つジェネリッククラス&tt(){Foo<T extends String>を宣言します。しかし開発者は&tt(){Foo}の未加工型である&tt(){e}に対し&(){e.f}で整数のベクターを代入します。これはジェネリックス使用ライブラリのジェネリック使用プログラムが[[ヒープ汚染>4.12.2. 参照型の変数]]を発生させるということでレガシーコード開発者は警告を受け取ります。}}

&i(){&small(){(レガシーコード開発者はライブラリから&tt(){Vector<String>}を自身の&tt(){Vector}変数に警告を受けることなく代入できます。つまり、Javaプログラミン言語の[[派生型付けルール>4.10.2. クラスやインタフェース間の型の派生]]では未加工型の変数に型の引数付けされたどのようなインスタンスに対する値も代入することができます。)}}

&i(){&small(){未検査変換による警告はレガシーライブラリーを使用するジェネリクス開発者が遭遇する2つのケースをカバーします。例えば、ライブラリのメソッドは&tt(){Vector}型の未加工型を返し、使用者側はメソッド呼び出しの結果を&tt(){Vector<String>}型の変数に代入します。これは、未加工ベクターは&{String}以外の要素の型を持っているかもしれないので安全ではありません。しかし、レガシーコードとのインタフェースを保つため未検査変換はまだ使用できるようになっています。未検査変換による警告はジェネリクス開発者がプログラムの違う箇所でヒープ汚染を起こすという問題が発生するかもしれないということを喚起しています。}}

#divstyle(background-color:#f0f0ff;border:1px solid black;padding 4px){{{

&bold(){&aname(ex4.8-1){例4.8-1. 未加工型}}

 class Cell<E> {
     E value;
 
     Cell(E v)     { value = v; }
     E get()       { return value; }
     void set(E v) { value = v; }
 
     public static void main(String[] args) {
         Cell x = new Cell<String>("abc");
         System.out.println(x.value);  // OK, has type Object
         System.out.println(x.get());  // OK, has type Object
         x.set("def");                 // unchecked warning
     }
 }

}}}

#divstyle(background-color:#f0f0ff;border:1px solid black;padding 4px){{{

&bold(){&aname(ex4.8-2){例4.8-2. 未加工型と継承}}

 import java.util.*;
 class NonGeneric {
     Collection<Number> myNumbers() { return null; }
 }
 
 abstract class RawMembers<T> extends NonGeneric
                              implements Collection<String> {
     static Collection<NonGeneric> cng =
         new ArrayList<NonGeneric>();
 
     public static void main(String[] args) {
         RawMembers rw = null;
         Collection<Number> cn = rw.myNumbers();
                               // OK
         Iterator<String> is = rw.iterator();
                               // Unchecked warning
         Collection<NonGeneric> cnn = rw.cng;
                               // OK, static member
     }
 }

&i(){&small(){このプログラムは、&tt(){RawMembers<T>}は以下のメソッドを:}}

 Iterator<String> iterator()

&i(){&small(){スーパーインターフェースであるCollection<String>から継承しています。しかし、&tt(){RawMembers}型は&tt(){Collection<String>}の抹消から&tt(){iterator()}を継承しています。これは&tt(){iterator()}の戻り型は&tt(){Iterator<String>}の抹消である&tt(){Iterator}であることを意味します。}}

&i(){&small(){結果として、&tt(){rw.iterator()}からの代入は&tt(){Iterator<String>}から&tt(){Iterator}への[[未検査変換>5.1.9. 未検査変換]]を要求し、未検査警告が発生します。}}

&i(){&small(){逆に、静的メンバー&tt(){cng}は未加工型のオブジェクトへのアクセスが完全な引数付き型で表されています。(インスタンスへの静的メンバーを通したアクセスは悪いスタイルと考えられており推奨されません。)メンバー&tt(){myNumbers}は&tt(){NonGeneric}クラス(抹消も&tt(){NonGeneric})より継承されていますので完全な引数付き型です。}}

}}}

&i(){&small(){未加工型はワイルドカードと密接に関係しています。両方とも実在型に基づいています。未加工型はレガシーコードとの相互接続を達成するために型規則を故意に不安定にしたワイルドカードとみなせます。歴史的には、未加工型はワイルドカードより先に出現しました。最初に紹介したのはGJで、論文Making the future safe for the past: dding Genericity to the Java Programming Languageで著者はGilad Bracha、Martin Odersky、David Stoutamire、Philip WadlerでObject-Oriented Programming, Systems, Languages and Applications (OOPSLA 98), 1998年10月のACM会議会議録の中で述べられています。}}

**[[4.9. 交差型]]
**[[4.10. 型の派生]]
**[[4.11. 型の使用箇所]]
**[[4.12. 変数]]
目安箱バナー