読者です 読者をやめる 読者になる 読者になる

seratch's weblog in Japanese

About Scala, Java and Ruby programming in Japaense. If you need English information, go to http://blog.seratch.net/

Java 8 で Scala のような何かを使う

Java Scala

Java 8 では JSR 335: Lambda Expressions が取り入れられます。

これを使うと Pure Java でありながら「どう見ても Scala」というコードが書けるようになります。

主に私がメンテナンスしている ScalaFlavor4J というライブラリがあります。

https://github.com/m3dev/scalaflavor4j

Java 7 まではこんな感じで使う必要がありました。

Seq.apply(1, 2, 3, 4, 5).filter(new F1<Integer, Boolean>() {
  public Boolean apply(Integer i) { return i > 2; }
}); 
// -> Seq.apply(3, 4, 5)

しかし、Java 8 ではこんなにシンプルに書けてしまいます。

Seq.apply(1, 2, 3, 4, 5).filter( (Integer i) -> i > 2 );

高階関数の引数型は省略できるので、さらにシンプルに書くとこうなります。

Seq.apply(1, 2, 3, 4, 5).filter( i -> i > 2 );

これ、Java のコードなんですよ。ちょっと感動すら覚えますね。

ちなみに Scala で書くとこうなります。

Seq(1, 2, 3, 4, 5).filter { i => i > 2 }

foldLeft の例です。これまで非常に煩雑でしたが

String s = Seq.apply('b', 'c', 'd').foldLeft("a", new FoldLeftF2<String, Character>() { 
  public String apply(String z, Character c) { return z + c; }
});
// -> s : "abcd"

これもこのように簡潔になります。

String s = Seq.apply('b', 'c', 'd').foldLeft("a", (String z, Character c) -> z + c);

String s = Seq.apply('b', 'c', 'd').foldLeft("a", (z, c) -> z + c);

高階関数の処理を複数行で書く場合は、こんな感じで最後は return を省略せずに書きます。

SInt.apply(1).to(1000).par().map(i -> {
  print(Thread.currentThread().getId() + ",");
  return i * i;
});

JSR 335 では functional interface を以下のように定めています。

A functional interface is an interface that has just one abstract method, and thus represents a single function contract.

http://cr.openjdk.java.net/~dlsmith/jsr335-0.6.1/A.html

ちょっとイマイチな例ですが、例えばこんなようなのを

interface F<A, R> {
  R apply(A a);
}

こんな感じで受け取るメソッドであれば

class Util {
  static Integer toInt(String str, F<String, Integer> f) { 
    return f.apply(str); 
  }
}

こんなのが書けます。

Integer i = Util.toInt("foo", (str) -> str.length());

あと java.lang.FunctionalInterface というアノテーションがあって、コンパイル時にそのインタフェースが妥当かをチェックすることができます。

@FunctionalInterface
public interface F<A, R> {
  R apply(A a);
  R map(A a);
}

このコードをコンパイルすると以下のようなコンパイルエラーになります。

F.java:1: error: Unexpected @FunctionalInterface annotation
@FunctionalInterface
^
  F is not a functional interface
    multiple non-overriding abstract methods found in interface F

以下は Java 8 が用意されている前提で試すためのサンプルです。

mkdir work
cd work
wget https://oss.sonatype.org/content/repositories/releases/com/m3/scalaflavor4j/1.1.0/scalaflavor4j-1.1.0.jar
echo 'import com.m3.scalaflavor4j.*;
import static com.m3.scalaflavor4j.Predef.*;
public class Main {
  interface F<A, R> {
    R apply(A a);
  }
  static class Util {
    static Integer toInt(String str, F<String, Integer> f) { return f.apply(str); }
  }
  public static void main(String[] args) {
    println(Util.toInt("foo", (str) -> str.length()));
  }
}
' > Main.java
javac -cp scalaflavor4j-1.1.0.jar Main.java
java -cp scalaflavor4j-1.1.0.jar:. Main

Java 8 のリリースが待ち遠しいですね!