SSブログ

ラムダ式(2) [Java 8]

ラムダ式に実際に渡されているオブジェクトが何であるかを調べるためのテストコードです。
import java.util.Arrays;
import java.util.function.Consumer;

public class Lambda02 {
    public static void main(String[] args) {
        showInfoOfLambda(msg -> System.out.println(msg));
    }

    private static void showInfoOfLambda(Consumer<String> consumer) {
        consumer.accept("Hello World");

        Class<?> consumerClass = consumer.getClass();
        System.out.printf("class name : %s%n", consumerClass.getName());
        showImplementedInterfaces(consumerClass);
        showSuperclass(consumerClass);
    }

    private static void showImplementedInterfaces(Class<?> consumerClass) {
        Class<?>[] interfaces = consumerClass.getInterfaces();

        if (interfaces.length == 0) {
            System.out.println("no interface is implemented");
            return;
        }

        System.out.print("implements : ");
        Arrays.asList(interfaces).forEach(
            cl -> System.out.printf("%s ", cl));
        System.out.println();
    }

    private static void showSuperclass(Class<?> consumerClass) {
        System.out.printf("superclass : %s%n",
                         consumerClass.getSuperclass());
    }
}
ラムダ式を受け付けるメソッドとしてshowInfoOfLambdaメソッドを定義して、mainメソッドで引数を表示するだけのラムダ式を渡しています。

showInfoOfLambdaでは、最初に、Consumerインタフェースとして渡された引数に対して、acceptメソッドを呼び出しています。次に渡されたオブジェクトのクラス、実装しているインタフェース、スーパークラスと順に表示しています。

このプログラムをコンパイルして実行すると、次の結果になります。
shibata-yoshiki-no-macbook:java8study yoshiki$ javac Lambda02.java
shibata-yoshiki-no-macbook:java8study yoshiki$ java Lambda02
Hello World
class name : Lambda02$$Lambda$1
implements : interface java.util.function.Consumer 
superclass : class java.lang.invoke.MagicLambdaImpl
shibata-yoshiki-no-macbook:java8study yoshiki$ ls -l *.class
-rw-r--r--  1 yoshiki  staff  2313  3 17 11:07 Lambda02.class
shibata-yoshiki-no-macbook:java8study yoshiki$ 
実際に渡されているオブジェクトのクラスは、Lambda02$$Lambda$1、実装しているインタフェースはjava.util.function.Consumer、スーパークラスは java.lang.invoke.MagicLambdaImplと表示されています。

また、コンパイル結果の.classファイルは、Lambda02.classしかありません。したがって、Lambda02$$Lambda$1は、実行時に動的に生成されていることが推測されます。

ラムダ式(1) [Java 8]

Java 8では、Java言語の仕様が大きく変わります。新たな言語仕様にキャッチアップするために学習を始めたので、メモ程度ですが、分かったことを順不同に今後書いていきたいを思います。したがって、新たな言語仕様のチュートリアルではありませんので注意してください。

ラムダ式は、現在、EarlyAccess版のOpenJDKで使用でき、以下のサイトからダウンロードできます。

http://jdk8.java.net/lambda/

Java 8ではIterableインタフェースにforEach()メソッドが追加されていますので、Iterableインタフェースを実装しているコレクションを使用して、次のようなコードを書くことができます。
import java.util.List;
import java.util.ArrayList;

public class Lambda01 {
    public static void main(String[] args) {
        List<String> messages = new ArrayList<>();

        messages.add("Hello");
        messages.add("World");

        messages.forEach(msg -> System.out.printf("%s ", msg));
        System.out.println();
    }
}
forEach()メソッドには、引数msgを受け取り、それを出力するという簡単なラムダ式が渡されています。コンパイルして実行した結果は、次の通りです。
C:\Users\Yoshiki Shibata\Desktop\java8study>javac Lambda01.java

C:\Users\Yoshiki Shibata\Desktop\java8study>java Lambda01
Hello World
ここで不思議に思うのは、forEach()メソッドの引数の型はIterableインタフェースではどのように定義されているかということです。調べて見ると次のようになっています(コメントは削ってあります)。
package java.lang;

import java.util.Iterator;
import java.util.function.Consumer;

@FunctionalInterface
public interface Iterable {
    Iterator<T> iterator();

    public default void forEach(Consumer<? super T> consumer) {
        for (T t : this) {
            consumer.accept(t);
        }
    }
}
インタフェースのなのにメソッドの実装が書かれていることは今回は無視して、引数の型はConsumerインタフェース型となっています。Java 6までの言語仕様の知識から、「Consumberインタフェースを実装した無名クラスをコンパイラが生成しているのでは」と単純に推測できます。

しかし、コンパイル結果としては、Lambda01.classファイルしか存在しません。それで、javapで調べると次のような結果となります。
C:\Users\Yoshiki Shibata\Desktop\java8study>javap -p Lambda01
Compiled from "Lambda01.java"
public class Lambda01 {
  public Lambda01();
  public static void main(java.lang.String[]);
  private static void lambda$0(java.lang.String);
}
forEach()メソッドに渡したラムダ式の実体は、どうもこの lambda$0(java.lang.String)のようです。でも、acceptという名前でないし、一体どうやってConsumerインタフェースとして渡されているのかが不思議になります。(続く)