ProxyとInvokationHandlerをつかうと都度クラスを定義することなくSAMから特定のメソッドへの移譲を実現できます。
欠点はめちゃくちゃ遅いこと! ふつうに new Runnable() { ... }
するのにくらべて数百倍遅いです。
もうちょっと速ければ、RetroLambdaやJackの内部実装のオプションとして使うのもありかもしれないとおもっ検証してみたのですが、これはちょっと使いものにならなそうです。メソッド数を増やさないというのはそれなりには重要ですが、multidexがある今となってはそこまで優先すべきものでもないですし。
実装
たとえばこんなクラスを定義します。
public class InvocationDelegator<T> implements InvocationHandler {
final T receiver;
final Method delegate;
public static <U, F> F create(U receiver, Class<F> samClass, String delegate) {
for (Method method : samClass.getMethods()) {
if (Modifier.isAbstract(method.getModifiers())) {
Class<?>[] interfaces = {samClass};
InvocationHandler handler = new InvocationDelegator<>(receiver, delegate, method.getParameterTypes());
return samClass.cast(Proxy.newProxyInstance(samClass.getClassLoader(), interfaces, handler));
}
}
throw new RuntimeException("No method found in " + samClass.getCanonicalName());
}
public InvocationDelegator(T receiver, String delegate, Class<?>[] parameterTypes) {
this.receiver = receiver;
try {
this.delegate = receiver.getClass().getDeclaredMethod(delegate, parameterTypes);
this.delegate.setAccessible(true);
} catch (NoSuchMethodException e) {
throw new RuntimeException("Missing " + delegate + "() in " + receiver.getClass().getCanonicalName(), e);
}
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
return delegate.invoke(receiver, args);
}
}
MainActivityに以下のメソッドがあるとして:
private void doSomething() {
Log.d("XXX", "Hello, world!");
}
これで doSomething()
を呼び出すRunnable
を生成できます。
Runnable task = InvocationDelegator.create(this, Runnable.class, "doSomething");
task.run(); // this.doSomething() を呼び出す