回答編集履歴
3
append link
answer
CHANGED
@@ -54,7 +54,7 @@
|
|
54
54
|
|
55
55
|
`T`のコンストラクタを呼び出すコード、つまりジェネリクス型`Illegal`クラスのバイトコード中には、型消去により「実際に指定された型」情報が既に失われています。そのため、ジェネリクス型を作成したプログラマが期待する動作「`create`メソッド中で`T`型としてインスタンス生成する」ことは不可能であり、Javaコンパイラはこのソースコードをエラーとして拒絶します。
|
56
56
|
|
57
|
-
**追記:** 仮に`Illegal<T>`クラスのコンパイルを通した場合、下記
|
57
|
+
**追記:** 仮に`Illegal<T>`クラスのコンパイルを通した場合、型消去により下記相当のバイトコードに展開されるでしょう。
|
58
58
|
```
|
59
59
|
class Illegal {
|
60
60
|
public Object create() {
|
@@ -62,12 +62,14 @@
|
|
62
62
|
}
|
63
63
|
}
|
64
64
|
```
|
65
|
-
ジェネリクス型の利用側ソースコードは次のようなるでしょうが、
|
65
|
+
ジェネリクス型の利用側ソースコードは次のようになるでしょうが、
|
66
66
|
```
|
67
67
|
Illegal<Number> num = new Illegal<Number>();
|
68
68
|
Number n = num.create();
|
69
69
|
```
|
70
|
-
生成されるバイトコードは下記に相当します。
|
70
|
+
生成されるバイトコードは下記に相当します。ジェネリクスに指定した型パラメータ(`T = Number`)に関係なく`num.create()`は常に`Object`型インスタンスを返すため、`Number`型へのキャストは常に失敗し`ClassCastExcetion`例外がスローされるという危険かつ無意味な処理となっています。このため、Javaコンパイラは`Illigal`クラスのようなソースコードをエラー扱いします。
|
71
71
|
```
|
72
72
|
Number n = (Number) num.create();
|
73
73
|
```
|
74
|
+
|
75
|
+
Javaのジェネリクスで実際に何が行われるかを深く理解したければ、[Java Generics FAQs - Under The Hood Of The Compiler ](http://www.angelikalanger.com/GenericsFAQ/FAQSections/TechnicalDetails.html) をお勧めします。
|
2
append
answer
CHANGED
@@ -52,4 +52,22 @@
|
|
52
52
|
> このコードのようにしてインスタンスを生成することはできないというのはなぜでしょうか。
|
53
53
|
> 呼び出す側できちんと情報はあるはずなので、インスタンスを生成し、返すことは可能だと考えられるのですが。。
|
54
54
|
|
55
|
-
`T`のコンストラクタを呼び出すコード、つまりジェネリクス型`Illegal`クラスのバイトコード中には、型消去により「実際に指定された型」情報が既に失われています。
|
55
|
+
`T`のコンストラクタを呼び出すコード、つまりジェネリクス型`Illegal`クラスのバイトコード中には、型消去により「実際に指定された型」情報が既に失われています。そのため、ジェネリクス型を作成したプログラマが期待する動作「`create`メソッド中で`T`型としてインスタンス生成する」ことは不可能であり、Javaコンパイラはこのソースコードをエラーとして拒絶します。
|
56
|
+
|
57
|
+
**追記:** 仮に`Illegal<T>`クラスのコンパイルを通した場合、下記に相当するバイトコードに展開されるでしょう。
|
58
|
+
```
|
59
|
+
class Illegal {
|
60
|
+
public Object create() {
|
61
|
+
return new Object();
|
62
|
+
}
|
63
|
+
}
|
64
|
+
```
|
65
|
+
ジェネリクス型の利用側ソースコードは次のようなるでしょうが、
|
66
|
+
```
|
67
|
+
Illegal<Number> num = new Illegal<Number>();
|
68
|
+
Number n = num.create();
|
69
|
+
```
|
70
|
+
生成されるバイトコードは下記に相当します。しかし、ジェネリクスに指定した型パラメータ(`T = Number`)に関係なく`num.create()`は常に`Object`型インスタンスを返すため、`Number`型へのキャストは常に失敗し`ClassCastExcetion`例外がスローされるという危険かつ無意味な処理が行われます。このため、Javaコンパイラはこのコードをエラーとして扱います。
|
71
|
+
```
|
72
|
+
Number n = (Number) num.create();
|
73
|
+
```
|
1
refine
answer
CHANGED
@@ -11,7 +11,7 @@
|
|
11
11
|
> ...
|
12
12
|
> これは呼び出すときに、<Integer extends Number>としているので、JVMが返り値がInteger型であることを認識してくれるからですね。
|
13
13
|
|
14
|
-
技術的に正
|
14
|
+
技術的にはあまり正確ではありません。「コンパイル時に型情報が消える」の意味を誤解しているように見うけられます。
|
15
15
|
|
16
16
|
ジェネリクスによる型消去は、ジェネリクス型(この例では`Container`)のコンパイルで行われます。一方、ジェネリクス型を利用する側のコンパイル時には、ダウンキャスト処理(型パラメータに指定した`Integer`へのキャスト)が暗黙に生成されます。
|
17
17
|
|
@@ -21,7 +21,7 @@
|
|
21
21
|
> ```
|
22
22
|
> そうすると、getメソッドの返り値はInteger型になると思います。
|
23
23
|
|
24
|
-
なりません。getメソッド
|
24
|
+
なりません。型消去の結果としてJVMから見えるgetメソッド戻り値型は`Number`となっています。一方の呼び出し側では、コンパイラにより`Integer`型へのダウンキャスト処理が自動的に埋め込まれており、JVMはこれに従って`Number`→`Integer`へと変換します(コンパイル時に型検査済みのため、このダウンキャストは必ず成功する)。
|
25
25
|
|
26
26
|
型消去後の`Container`クラスバイトコードは下記に相当し(質問者の理解で正しい):
|
27
27
|
```
|
@@ -52,4 +52,4 @@
|
|
52
52
|
> このコードのようにしてインスタンスを生成することはできないというのはなぜでしょうか。
|
53
53
|
> 呼び出す側できちんと情報はあるはずなので、インスタンスを生成し、返すことは可能だと考えられるのですが。。
|
54
54
|
|
55
|
-
`T`のコンストラクタを呼び出すコード、つまりジェネリクス型`Illegal`クラスのバイトコード中には、型消去により「実際に指定された型」情報が既に失われています。このためインスタンス生成不可能です。
|
55
|
+
`T`のコンストラクタを呼び出すコード、つまりジェネリクス型`Illegal`クラスのバイトコード中には、型消去により「実際に指定された型」情報が既に失われています。このためインスタンス生成は不可能です。
|