Unknown Region

プログラムでハマったエラーとその解決方法についての備忘録メモ

【Java】[Generics]はしっかり設定して[RawType]を防ごう

[Generics]はしっかり設定して[RawType]を防ごう(大事なことなので2回ry)

以下のプログラムはコンパイルエラーになる。

注: Java8にて確認。

例: Hoge.java

import java.util.HashMap;
import java.util.Map;
import java.util.Set;
import java.util.stream.Stream;

public class Hoge {
  public static void main(String[] args) {
    Map map = new HashMap();
    Stream.of(map)
        .map(Map::entrySet)
        .flatMap(Set::stream)
        .forEach(e -> {
          System.out.println("key = " + e.getKey() + ", value = " + e.getValue());
        });
  }
}

なぜコンパイルエラーになるかといえば、タイトルの通り[java.util.Map]にGenericsが設定されていないから。

[java.util.Map.entrySet()][java.util.Set<java.util.Map.Entry<K, V>>]を返す様になっていて、この[<K, V>]は[java.util.Map]の設定を引き継ぐ。

ただしこのケースではGenericsの[<K, V>]が設定されていないため[java.util.Map][RawType]として扱われ、[java.util.Map.entrySet()][Generics無し(RawType)][java.util.Set]を返却する。

そして[java.util.Set::stream]もまた、同じく[Generics無し(RawType)]の[java.util.stream.Stream]を返却する。

[RawType]の場合は格納される値は全て[java.lang.Object]として扱われるので、[java.util.stream.Stream][java.lang.Object]を扱うと認識されていることになる。

最後の[java.util.stream.Stream.forEach(java.util.function.Consumer)]においても[java.util.function.Consumer.accept(T t)]Genericsの[T][java.lang.Object]として認識されている。

つまり[java.lang.Object]には[getKey()][getValue()]なんてメソッドは存在しないので、コンパイルエラーになるというワケ。

 

似た様なケースは多々発生するので、[RawType]にならない様にしっかりGenericsは設定しようねということになるのだけれど、場合によってはどうしても防げないケースもある。

それは自分の管理していないモジュール、例えばParserなどで[java.lang.Class]を引数に渡して内部でキャストする様なケース

そういう場合には、仕方がないので自前でキャストしてしまうのが一番手っ取り早い。

ちなみに先ほどのコード(Hoge.java)で無理やりコンパイルエラーを消すためにキャストを仕込むと以下のコード(Fuga.java)になる。

 

Fuga.java

import java.util.HashMap;
import java.util.Map;
import java.util.Set;
import java.util.stream.Stream;

public class Fuga {
  public static void main(String[] args) {
    Map map = new HashMap();
    Stream.of(map)
        .map(map -> (Map<Object, Object>) map)
        .map(Map::entrySet)
        .flatMap(Set::stream)
        .forEach(e -> {
          System.out.println("key = " + e.getKey() + ", value = " + e.getValue());
        });
  }
}

これでコンパイルエラーは出なくなる。

ちなみにメソッド参照で[java.util.Map.class::cast]を使いたくなるが、これだとやはりGenericsは欠落してしまうので我慢。

 

連絡先: plugout777★yahoo.co.jp (クローラー対策のため★を@に変更してください)