Java SE8 Lambda式に関して

Lambda式は、Java SE8に追加された新しい機能です。
Lambda式は、1つのメソッドインターフェイスの処理を簡素的な方法で、記述できます。
これが導入されたことにより、一番、影響を受けているのは、
コレクションライブラリからのデータ操作だと思います。
コレクションライブラリの記述が大幅に簡素に記述出来る様になっています。
コレクションライブラリの操作は、以前は、シングルスレッドの処理だけで、
並列処理をしたい場合は、自分で、並列処理出来るように記述する必要がありましたが、
Lambda式で記述すると、簡単に並列処理出来る様に記述することができます。
これにより、マルチコア環境でのパフォーマンスを向上させることができます。
また、コレクションライブラリ以外も内部クラスの記述に関しても
Lambda式で、簡素的に記述することが出来る様になっています。

内部クラスの記述方法から書こうと思います。
内部クラスですが、今までは、内部クラスを書く場合、数行ぐらい記述する必要がありました。
それを単一のステートメントで記述出来るようになり、
内部クラスを使う場合の長い定義から解放されます。
例えば、下の用なインターフェイスがあるとします。

@FunctionalInterface
public interface Hello {
	String getMessage();
}

これを内部クラスとして、実装した場合、
以前は、こんな感じで定義する必要がありました。

public class HelloTest {
	public static void main(String[] args) {
		Hello hello = new Hello() {
			@Override
			public String getMessage() {
				return "Hello Lambda Expressions ";
			}
		};
		System.out.println(hello.getMessage());
	}
}

Lambda式で書くとこうなります。

public class HelloTest {
	public static void main(String[] args) {
		Hello hello = () -> {return "Hello Lambda Expressions";};
		System.out.println(hello.getMessage());
	}
}

引数をとるインターフェイスの場合

@FunctionalInterface
public interface Check {
	boolean isCheck(int a, int b);
}

Lambda式で書くとこうなります。

public class CheckTest {
	public static void main(String[] args) {
		Check check = (a, b) -> {return a == b;};
		System.out.println(check.isCheck(100, 100));
	}
}

ちょっと、今までと結構違いますが、
1つのメソッドインターフェイスの定義を元に、型、引数などを内部で、
自動的に判断されます。
Lambda式で書く場合は、メソッド名などは、必要なく、
そのメソッドの引数とその処理を記述するだけで、すみます。
基本構文は、下の様な定義になっています。

(型 引数名, 型 引数名,...) -> {処理};

また、省略系の場合は、このような定義になります。

(引数名, 引数名,...) -> 処理;

Lambda式のざっくりしたパターンは、こんな感じです。

引数リスト アロー演算子 本文
() -> "hello"
(String s -> {return s;}
(s) -> s
(int a, int b) -> {return a == b;}
(a, b) -> a == b

Lambda式で、再帰処理も可能です。
なんらかの文字にある文字を設定した回数付加するような処理をLambda式で、再帰的に書く場合は、
下の様な感じになります。

@FunctionalInterface
public interface Recursive {
	String append(String str, String addStr, int start, int end);
}
public class Main {
	private Recursive recursive;
	public static void main(String[] args) {
		new Main().run();
	}
	public void run() {
		recursive = (str, addStr, start, end) -> start > end?str:recursive.append(str+addStr, addStr, ++start, end);
		System.out.println(recursive.append("A", "B",0, 10));
		
	}
}

Genericsを使ったインターフェイスにもLambda式を利用することもできます。
下の様なGenericsを使ったインターフェイスを定義します。

@FunctionalInterface
public interface Generics<T, V> {
	T test(V v);
}

Lambda式で、上記のインターフェイスを利用した例は、下記の定義です。

public class GenericsTest {
	public static void main(String[] args) {
		Generics<String, String> generics1 = obj -> obj.toLowerCase();
		System.out.println(generics1.test("BBBB"));
		Generics<String, Integer> generics2 = obj -> obj.intValue() > 100?"UP":"DOWN";
		System.out.println(generics2.test(90));
	}
}

最後に、Lambda式で、いろいろなことが出来ますが、制約がないわけでは、ありません。
Lambda式から外側の変数へのアクセスは、問題ありませんので、下記の様な記述は、できますが、

@FunctionalInterface
public interface Sample {
	String test();
}
public class SampleTest {
	public static void main(String[] args) {
		int arg = 13;
		Sample sample = () -> arg > 100?"up":"down";
		sample.test();
	}
}

下の様な記述は、できません。

public class SampleTest {
	public static void main(String[] args) {
		int arg = 13;
		String msg = "";
		Sample sample = () -> arg > 100?msg = "up":msg = "down";
		sample.test();
	}
}

Lambda式では、Lambda式から外側の変数へのアクセスは、出来ても、
Lambda式から外側の変数への変更は、出来なようになっています。

コレクションライブラリでのLambda式は、次のエントリーで書こうかと思います。