Fight the Future

Java言語とJVM、そしてJavaエコシステム全般にまつわること

君は合成メソッドを知っているか!(Java)

僕は知らなかった。。。まだまだJava初心者だな。

出会い

S2のBeanDescImpl*1クラスを読んでいたらこういうソースがあった。

    private void setupPropertyDescs() {
            ...
            if (MethodUtil.isBridgeMethod(m) || MethodUtil.isSyntheticMethod(m)) {
                continue;
            }
            ...

Bridge??Synthetic??

J2SE1.5から!?

で、MethodUtil*2のメソッドはこれ。

    private static Method getIsSyntheticMethod() {
        try {
            return Method.class.getMethod("isSynthetic", null);
        } catch (final NoSuchMethodException e) {
            return null;
        }
    }
    private static Method getIsBridgeMethod() {
        try {
            return Method.class.getMethod("isBridge", null);
        } catch (final NoSuchMethodException e) {
            return null;
        }
    }

リフレクションのMethodクラスにisSynthetic、isBridgeメソッドがあるか調べてる。
そんなメソッドあったっけ。。。ある!しかもJ2SEの1.5から!http://java.sun.com/javase/ja/6/docs/ja/api/java/lang/reflect/Method.html#isBridge()
橋渡し役のメソッド?合成メソッド?謎が深まる。。。

共変戻り値の実装にかかわっていた

この2つのメソッドはJ2SE1.5から導入された、共変戻り値にかかわっていた。
(共変戻り値はなんとか知ってたぜ!)
共変戻り値とは単純に言うと、オーバーライドするときに、スーパークラスで定義した戻り値の型のサブクラスの型を、オーバーライドしたメソッドで定義すること。
要はこういうこと。

public interface ReturnNumber {

	Number value();

}

public class ReturnInteger implements ReturnNumber {

	public Integer value() {
		return Integer.valueOf(0);
	}

}

インタフェースで定義したメソッドvalue()の戻り値はNumber。
それを実装したクラスでは戻り値をNumberのサブクラスであるIntegerで定義する。
これが共変戻り値。


もちろんコンパイルも実行も例外なくできる。

で、合成メソッドって?

こういう共変戻り値を使ったときなど、コンパイラが自動的にメソッドを作成することがあるらしい。
この自動的に作成したメソッドを合成メソッドという。


たとえば、共変戻り値を使ったときは、スーパークラスと同じ戻り値の型のメソッド(今までのオーバーライドのイメージ)がコンパイラによって作られる(戻り値の型を変えて実装したメソッドとは別に)。これが合成メソッド。


さらに、共変戻り値での合成メソッドは、内部的には新たに戻り値の型を変えたメソッドに処理を委譲する、という実装になっている。
なるほど!こうして共変戻り値を実現しているんだ!非常に納得。

最後にコードで実証

では本当に合成メソッドが作られているか、確認。
リフレクションを使う。さっきの2つのクラスで。

	public static void main(String[] args) {
		Method[] methods = ReturnInteger.class.getMethods();
		for (int i = 0; i < methods.length; i++) {
			Method method = methods[i];
			System.out.println(method);
			System.out.println("\tisSynthetic:" + method.isSynthetic());
			System.out.println("\tisBridge:" + method.isBridge());
		}
	}

実行するとこんな出力。

public java.lang.Integer ReturnInteger.value()
	isSynthetic:false
	isBridge:false
public volatile java.lang.Number ReturnInteger.value()
	isSynthetic:true
	isBridge:true
...

(volatileはsynchronizedのゆるいのやつね。)
たしかにvalue()メソッドが2つあり、インタフェースで定義した方はisSynthetic()もisBridge()もtrueを返してる。


まだまだJavaで知らないことがたくさんあるんだな。

参考にさせていただいたサイト

http://www.ne.jp/asahi/hishidama/home/tech/java/covariant.html

*1:org.seasar.framework.beans.impl.BeanDescImpl

*2:org.seasar.framework.util.MethodUtil