teratail header banner
teratail header banner
質問するログイン新規登録

回答編集履歴

3

append link

2016/08/12 02:38

投稿

yohhoy
yohhoy

スコア6191

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
- 生成されるバイトコードは下記に相当します。しかし、ジェネリクスに指定した型パラメータ(`T = Number`)に関係なく`num.create()`は常に`Object`型インスタンスを返すため、`Number`型へのキャストは常に失敗し`ClassCastExcetion`例外がスローされるという危険かつ無意味な処理が行われます。このため、Javaコンパイラはのコードをエラーとして扱います。
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

2016/08/12 02:38

投稿

yohhoy
yohhoy

スコア6191

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

2016/08/12 01:50

投稿

yohhoy
yohhoy

スコア6191

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メソッド戻り値は`Number`型であり(型消去結果)、呼び出し側に`Integer`型へのキャスト処理が(コンパイラにより自動的に埋め込まれます。
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`クラスのバイトコード中には、型消去により「実際に指定された型」情報が既に失われています。このためインスタンス生成不可能です。