「こんにちは、アミーゴ!」

「こんにちは、エリー!」

「今日はリシと私がジェネリック医薬品についてお話します。」

「待ってください、私はすでにほとんどすべてを知っていると思います。」

「ほぼすべてですが、すべてではありません。」

「そうですか?はい、聞きます。」

「それでは始めましょう。」

「Java では、ジェネリックは型パラメーターを持つクラスです。」

「ジェネリックが発明された理由については、コード内のコメントを参照してください。」

ArrayList stringList = new ArrayList();
stringList.add("abc"); // Add a string to the list
stringList.add("abc"); // Add a string to the list
stringList.add( 1 ); // Add a number to the list

for(Object o: stringList)
{
 String s = (String) o; // There will be an exception here when we get to the integer
}

ジェネリックを使用して問題を解決する方法:

ArrayList<String> stringList = new ArrayList<String>();
stringList.add("abc"); // Add a string to the list
stringList.add("abc"); // Add a string to the list
stringList.add( 1 ); // There will be a compilation error here

for(Object o: stringList)
{
 String s = (String) o;
}

「このコードは単純にコンパイルできません。間違ったデータ型を追加することによって発生するエラーはコンパイル中に認識されます。」

「はい、それはもう知っています。」

「分かった、よかった。繰り返しは学習の母だ。」

「しかし、Java の作成者は、ジェネリクスを作成するときに少し怠け者でした。パラメータ付きの本格的な型を作成する代わりに、巧妙な最適化を導入しました。実際には、型パラメータに関する情報をジェネリックに追加しませんでした。代わりに、すべての魔法はコンパイル中に発生します。」

ジェネリックスを使用したコード
List<String> strings = new ArrayList<String>();
strings.add("abc");
strings.add("abc");
strings.add( 1); // Compilation error

for(String s: strings)
{
 System.out.println(s);
}
本当に何が起こるのか
List strings = new ArrayList();

strings.add((String)"abc");
strings.add((String)"abc");
strings.add((String) 1); // Compilation error

for(String s: strings)
{
 System.out.println(s);
}

「それは滑らかですね。」

「はい、しかし、このアプローチには副作用があります。 型パラメータに関する情報はジェネリック クラス内に格納されません。 このアプローチは、後に型消去として知られるようになりました。」

「言い換えれば、型パラメータを持つ独自のクラスがある場合、それらに関する情報をクラス内で使用することはできません。」

ジェネリックスを使用したコード
class Zoo<T>
{
 ArrayList<T> pets = new ArrayList<T>();

 public T createAnimal()
 {
  T animal = new T();
  pets.add(animal)
  return animal;
 }
}
本当に何が起こるのか
class Zoo
{
 ArrayList pets = new ArrayList();

 public Object createAnimal()
 {
  Object animal = new ???();
  pets.add(animal)
  return animal;
 }
}

「コンパイル中に、すべてのパラメータの型は Object に置き換えられます。そして、クラス内には、渡される型に関する情報はありません。」

「はい、同意します、それは最善ではありません。」

「それほど怖いものではありません。この問題を回避する方法は後で説明します。」

しかし、それだけではありません。Java では、型パラメーターの親の型を指定できます。これには extends キーワードが使用されます。例えば:

ジェネリックスを使用したコード
class Zoo<T extends Cat>
{
 T cat;

 T getCat()
 {
  return cat;
 }

 void setCat (T cat)
 {
  this.cat = cat;
 }

 String getCatName()
 {
  return this.cat.getName();
 }
}
本当に何が起こるのか
class Zoo
{
 Cat cat;

 Cat getCat()
 {
  return cat;
 }

 void setCat(Cat cat)
 {
  this.cat = cat;
 }

 String getCatName()
 {
  return this.cat.getName();
 }
}

「2 つの事実に注意してください:」

「まず、任意の型だけをパラメーターとして渡すことはできません。Cat または Cat を継承するクラスのみを渡すことができます。」

「第 2 に、Zoo クラス内で、型 T の変数が Cat クラスのメソッドを呼び出せるようになりました。 右側の列はその理由を説明しています (T があるところはどこでも Cat に置き換えられるため)。」

「はい。Cat または Cat のサブクラスが型引数として渡されるとすると、型 T は常に Cat クラスのメソッドを持つことになります。」

「なるほど、それは賢いですね。」