「こんにちは、アミーゴ!」
「こんにちは、エリー!」
「今日はリシと私がジェネリック医薬品についてお話します。」
「待ってください、私はすでにほとんどすべてを知っていると思います。」
「ほぼすべてですが、すべてではありません。」
「そうですか?はい、聞きます。」
「それでは始めましょう。」
「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 クラスのメソッドを持つことになります。」
「なるほど、それは賢いですね。」
GO TO FULL VERSION