ジェネリクスを使いこなそうゼ

というネタでエントリ書けよゴルァ、とid:happy_ryoに脅されて書くエントリー。

ジェネリクスご存知ですか。使ってますか。アノテーションと共にJava5から現れた、<> で囲まれた変な奴。

全くの余談ですが、アノテとジェネリクスの事を理解していなかったその昔。漠然と「たまに見かけるあの@とか、<> とかって何だろう」と思った訳ですよ。で、どうしようもなくて某所で聞いてみたんですね。そしたらさー、「ググれカス」って言われちゃってさwwww

Java @」とか「Java <>」で検索してみるわけですよw 無理だろww

はい、話し始める前から脱線してる都元ですコンバンワ。

ジェネリクス初心者がまず触れるのは、コレクションですかね。Listとかでお世話になる訳ですね。意図しない型のインスタンスを突っ込まれる心配もなく、取り出し時にキャストする必要もなくなる、なんか嬉しい奴、というイメージだと思います。

じゃあ、自分でジェネリクスを定義して使ってみようぜ、というのが今日のテーマ。いささか初歩的なんじゃなかろか、とか思ってたけど、某skypeチャットルームで世界の「Ryo Iwama」に脅されたので書いてみる。

ジェネリクスってgeneralの派生単語で、「一般」とかいう意味ですね。Java的な訳は「総称型」とか言ったりするようです。ではコイツは何を一般化して、何を総称してるのか。

ちょっと List のコードを見てみましょか。

public interface List<E> {
    boolean add(E o);
    E get(int index);
}

かなり省略しましたがw こんな感じです。ここで、add/getされる対象を「一般化」「総称」する役割を担ってるのがジェネリクスインターフェイスでは一般的(色んなモノに使えるように)に書いておいて、実際に使う時に用途を限定する、という使い方をします。

List<String> list = new ArrayList<String>();

って奴ですね。こうすることによって、一般的に書かれたListが、「Stringしか入れられないList」に特殊化する。

もうちょっと応用すると、こんな事もできる。

class StringList implements List<String> {
  // ...
}

こうして作られたStringList型にはStringしか入れられない。

じゃーさじゃーさ、Listインターフェイスに倣って、自分で色々クラスを定義してみようよ。

interface Processor<T> {
  T process(T input);
}

class StringProcessor implements Processor<String> {
  public String process(String input) { ... }
}

class IntegerProcessor implements Processor<Integer> {
  public Integer process(Integer input) { ... }
}

入力した何かを「処理(process)」するクラス。型が違っても一般的に書けました。

複数のジェネリクスを定義することもできる。

interface Converter<S, D> {
  D convert(S source);
}

class IntegerToStringConverter implements Converter<Integer, String> {
  public String convert(Integer source) {
    retrun source.toString();
  }
}

class MapToCollectionConverter implements Converter<Map, Collection> {
  public Collection convert(Map source) {
    retrun source.values();
  }
}

こんな感じか。というわけで「型だけ違うんだけど、似たような型を抽象化したい」って時に、ジェネリクスを自分で定義してみる、というのはいかがだろーか。

最後にオマケ。昔書いたネタだけど。

interface Hoge<T extends Exception> {
  void process() throws T;
}

class Fuga implements Hoge<FugaException> {
  public void process() throws HogeException { ... }
}

class Piyo implements Hoge<RuntimeException> {
  public void process() { ... }
}

ジェネリクスによって、投げる例外を一般化してる。しかも「Runtimeであれば、throws句がいらないの法則」を利用して、「チェック例外は投げません」ということを表現してみた。これはひどい

ココまで来ると、オシゴト的には誰も理解してくれなくなるので、やめましょうw

いじょ。

追記

そういえば、ふと思った。genericsで使う extends って、↑みたいな使い方するよね。で、コイツの仲間に super って居るよね…。

extendsは「コイツのサブクラス」という意味。superは「コイツの基底クラス」って意味…。今まで俺 generics の super って使ったことないんですが、どんな時に使うモンなんだろう…。有効な使い方が思いつきません><