Javaで動的型付け的なこと

Javaは静的型付け言語とはいえ、リフレクションがあるので、その気になれば動的型付けだって簡単に実現できるよなと思いました。

例えば、以下のようにユーティリティクラスを書いておけば、

package test;

public class Dynamic {
    private static final Map<Class<?>, Class<?>> nonPrimitiveMap;

    static {
        nonPrimitiveMap = new HashMap<Class<?>, Class<?>>();
        nonPrimitiveMap.put(boolean.class, Boolean.class);
        nonPrimitiveMap.put(byte.class, Byte.class);
        nonPrimitiveMap.put(char.class, Character.class);
        nonPrimitiveMap.put(short.class, Short.class);
        nonPrimitiveMap.put(int.class, Integer.class);
        nonPrimitiveMap.put(long.class, Long.class);
        nonPrimitiveMap.put(float.class, Float.class);
        nonPrimitiveMap.put(double.class, Double.class);
    }

    public static Object invoke(Object obj, String methodName,
            Object... args) {
        for (Method method : obj.getClass().getMethods()) {
            if (!method.getName().equals(methodName)) {
                continue;
            }
            Class<?>[] types = method.getParameterTypes();
            if (types.length != args.length) {
                continue;
            }
            boolean match = true;
            for (int i = 0; i < types.length; i++) {
                Object arg = args[i];
                Class<?> nonPrimitive = nonPrimitiveMap.get(types[i]);
                if (nonPrimitive != null) {
                    if (!nonPrimitive.isInstance(arg)) {
                        match = false;
                        break;
                    }
                } else if (arg != null && !types[i].isInstance(arg)) {
                    match = false;
                    break;
                }
            }
            if (match) {
                try {
                    return method.invoke(obj, args);
                } catch (IllegalArgumentException e) {
                } catch (IllegalAccessException e) {
                } catch (InvocationTargetException e) {
                }
            }
        }
        return null;
    }
}

次のようにして動的型付けっぽく呼び出すことができます。

package test;

import static test.Dynamic.*;

class Add {
    public int run(int a, int b) { return a + b; }
}

class Sub {
    public int run(int a, int b) { return a - b; }
}

class Mul {
    public int run(int a, int b) { return a * b; }
}

public class Test {
    public static void main(String[] args) {
        Object[] calcs = { new Add(), new Sub(), new Mul() };
        for (Object calc : calcs) {
            Object result = invoke(calc, "run",
                    invoke(calc, "run", 1, 2),
                    invoke(calc, "run", 3, 4));
            System.out.println(result);
        }
    }
}

実行結果は以下のようになります。

10
0
24

Swingには、個別定義されているけど共通のシグネチャを持つメソッドが多いので、動的な呼び出しが有効になるケースも多いと思います。例えば、JFrame, JDialog, JAppletはそれぞれ個別にgetGlassPane() / setGlassPane()を持っているんですけど、上記のような呼び出しを使えば、汎用のGlassPane制御メソッドをスマートに書くことができます。