#導入
こんにちは。けちょんです。
皆さん、ジェネリクス使ってますか?
私は今日初めて学びました。
というか今まで見て見ぬ振りしてましたが、ついに学ぶ場に出会いました。
##ジェネリクスと出会う場面
一番出会うのは、リストだと思います。
以下のような宣言したことあるかと思います。
List<String> list = new ArrayList<>();
可変長な配列ということしか私は知りませんでした。
この<>を使うクラスをジェネリッククラスと言います。
##どういう役割なの?
List<String> list
まずこの部分。
Listはそもそもどのようなものかというと、複数の要素を配列のように保持して扱うインターフェースです。
Stringに限らず、様々な型のオブジェクトを格納できます。(一種類だけだけど。)
で、今回Stringを格納すると定めているのが上の宣言です。
new ArrayList<>();
次にこの部分。<>の中が空になってますが、これは省略してるだけです。(左辺のStringから型推測できる)
#一般的なジェネリクス
ではリストに限らず、より広い観点でジェネリクスに触れてみます。
簡単な定義は以下ですね。
public class Gene<T> {
private T t;
public void setT(T t) {
this.t = t;
}
public T getT() {
return t;
}
}
使い方は以下
Gene<String> g1 = new Gene<>(); // String型で固定
Gene<Integer> g2 = new Gene<>(); // Integer型で固定
g1.setT("test");
System.out.print(g1.getT()); // test
g2.setT(24);
System.out.print(g2.getT()); // 24
##何が嬉しいのか
今回の例だと、同一のクラスに対して、複数のクラスを使用できることができました。
##いやいや、これでも良くない?
public class GeneObj {
Object t;
public void setT(Object t) {
this.t = t;
}
public Object getT() {
return t;
}
}
型Tで限定していた部分に対して、Objectクラスを使ってみました。
では使ってみましょう。
GeneObj g1 = new GeneObj();
GeneObj g2 = new GeneObj();
g1.setT("test");
System.out.print(g1.getT()); // test
g2.setT(24);
System.out.print(g2.getT()); // 24
同じようなコードで書けましたね。
#まとめ
ジェネリクスは使わなくても良い???
#いやいや
今回の例ではどちらも動きますが、Objectを使用すると問題があります。
簡単に言うと、このクラスは使いにくいです。
##実行時エラーとなる使用例
GeneObj g1 = new GeneObj(); // String型で固定
g1.setT("1");
Integer res = (Integer) g1.getT() + 1; //ClassCastExceptionの発生
System.out.print(res);
上では、誤ってInteger型ではなくStringを詰め込んでしまっています。
そのためIntegerクラスにキャストする際ClassCastExceptionが発生してしまいました。
使い手からすれば、自分がsetTした型を把握しておかなければならず、バグの温床になってしまいます。
ジェネリクスを使えば、そもそもキャストを使用する必要がなく、コンパイルエラーにて気づくことができます。
#まとめ
型の柔軟性とコードの保守性を両立させるためにジェネリクスは必要