Java8 を触ってみる
Java8 が正式リリースされたので、新機能がどんなものか触ってみた。
Java8 をインストール
Oracle のサイトから Java SE 8 をダウンロードしてインストール。
http://www.oracle.com/technetwork/java/javase/overview/index.html
Java8 をデフォルトにはしたくないので、.bash_profile に↓を追記して Java7 がデフォルトになるようにしておく。
#!shell
export JAVA_HOME=$(/usr/libexec/java_home -v 1.7)
export PATH=$JAVA_HOME/bin:$PATH
IntelliJ IDEA をインストール
これまで使ったことが無かったのだが、Java8 に導入されたラムダ式のサポートが一番進んでいるっぽいので IntelliJ IDEA をインストール。 よさ気だったらライセンス買おうかな。
ラムダ式
まずはよく例にあがる Comparator を使ってみる。
list#sort の引数には Comparator を渡す必要があるが、Comparator には @FunctionalInterface が定義されているので、ラムダ式で書けるようになった。また、ラムダ式の引数は型が自明の場合は省略できるらしい。(この場合は String を省略できる)
#!java
List<String> list = Arrays.asList("deep impact", "heart's cry");
list.sort((s1, s2) -> s2.compareTo(s1));
// [heart's cry, deep impact]
System.out.println(list);
複数行の場合は中括弧で囲む。 なお、ラムダ式の中でスコープ外の変数の参照を変更することは出来ない。(実質的 final)
#!java
list.sort((s1, s2) -> {
int compareTo = s2.compareTo(s1);
return compareTo;
});
java.util.logging.Logger のメソッドにも関数を渡せるらしい。 Logger#info の場合、Supplier という引数無し・戻り値ありの関数を渡すことできる。 関数は実際にログ出力される場合に評価されるので、よくある isLoggable でロギング対象化チェックをすることで無駄なログ文字列生成をしない、といった対処が不要となるらしい。 (関数オブジェクトの生成コストは気にするレベルではない、ということなのかな)
#!java
private static Logger logger = Logger.getLogger("error");
public static void main(String[] args) {
logger.info(() -> "info");
}
「::」という記述で、ラムダ式の代わりにメソッドを参照することが可能。
#!java
List<String> list = Arrays.asList("deep impact", "heart's cry");
// Consumer<String> consumer = System.out::println;
list.forEach(System.out::println);
インターフェース
default 修飾子を付けることで、デフォルト実装を定義することができるようになった。 また、static メソッドを定義することも可能となった。
#!java
private static interface Interface {
default public String hoge() {
return "foo";
}
}
つまり Mix-in ができるようになったということ。 ちなみに、全く同じ名前・引数のデフォルト実装をした場合はコンパイルエラーとなった。
コレクションAPI
諸々刷新されているよう。 例えば List -> Map のような処理。getOrDefault のおかげで初期化処理を簡略化することができる。
#!java
List<User> users = Arrays.asList(
new User(1, 100, "イチロー"), // id, groupId, name
new User(2, 200, "ジロー"),
new User(3, 100, "サブロー")
);
Map<Integer, List<User>> byGroup = new HashMap<>();
for (User user : users) {
List<User> list = byGroup.getOrDefault(user.getGroupId(), new ArrayList<>());
list.add(user);
byGroup.put(user.getGroupId(), list);
}
もう一つの目玉、Stream を使って List を絞り込む。
#!java
LocalDate now = LocalDate.now();
List<Data> dataList = Arrays.asList(
new Data(1, now.minusDays(1), now.plusDays(1)), // id, startDate, endDate
new Data(2, now.plusDays(2), now.plusDays(3)),
new Data(3, now.minusDays(1), now.plusDays(1))
);
dataList.stream()
.filter((data) -> data.isValid(now))
.forEach((data) -> {
// 1, 3
System.out.println(data.getId());
});
collect を使用することで、フィルター結果の List を返すことも可能。
#!java
List<Data> collected = dataList.stream()
.filter((data) -> data.isValid(now))
.collect(Collectors.toList());
さっきの List -> Map もできちゃう。
#!java
users.stream()
.collect(Collectors.groupingBy(user -> user.getGroupId()))
.forEach((k, v) -> {
System.out.println(k);
System.out.println(v);
});
とりあえずここまで。 ラムダ式、Stream 周りはまだまだ勉強しなければ。
参考サイト
大刷新リリース Java 8の新機能 http://news.mynavi.jp/special/2014/java8/
初心者のためのJavaラムダ式入門とJDKのインストール、IDEの環境構築 http://www.atmarkit.co.jp/ait/articles/1402/18/news010.html#05
社内Java8勉強会 ラムダ式とストリームAPI http://dev.ariel-networks.com/wp/archives/4207