動的なメソットのオーバーロード


こんなことがしたい

例えば以下のようなクラスがあった場合。

public interface Base {  }

public class Sub1 implements Base { }

public class Sub2 implements Base { }


以下のようにすると test(new Sub1()) のような呼び出しで method(Sub1 sub1) のメソッドが呼ばれるのではなかろうか?

    private test(Base b) {
        Class type = Class.forName(b.getClass().getCanonicalName());
        method(type.cast(b));
    }

    private void method(Base base) {
        System.out.println("base");
    }
    private void method(Sub1 sub1) {
        System.out.println("sub1");
    }
    private void method(Sub2 sub2) {
        System.out.println("sub2");
    }

と思っていましたが、メソッドのオーバーロードは、オーバーライドと異なり、コンパイル時に呼び出し対象のメソッドを静的に決定するため、上記コードは無効です。
普通はポリモーフィズムで必要なメソッドを該当のクラス内で定義しますが、対象のクラスがいじれない場合は instanceof にてしこしこと条件分岐する必要があります。

リプレクションを使って

無理やり対応しようとすると以下のような感じになります。ちゃんとやろうとすると、もう少し作り込む必要ありますが・・

private void test(Base b) throws Exception {
    invokeMethod(new Sub1());
    invokeMethod(new Sub2());
}
private void method(Sub1 sub1) {
    System.out.println("sub1");
}
private void method(Sub2 sub2) {
    System.out.println("sub2");
}

private void invokeMethod(Object obj) {
    Method targetMethod = null;
    for (Method method : getClass().getDeclaredMethods()) {
        if (!method.getName().equals("method")) {
            continue;
        }
        
        for(Type argType : method.getParameterTypes()) {
            if (argType == obj.getClass()) {
                targetMethod = method;
            } else {
                targetMethod = null;
                break;
            }
        }
        
        if (targetMethod != null) {
            try {
                targetMethod.invoke(this, obj);
                return; 
            } catch (Exception e) {
                throw new IllegalArgumentException(e);
            }
        }
    }
}

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

sub1
sub2

でも、ポリモーフィズムで対応するのが正解ですね・