Proxyでインターフェースデフォルトメソッドを呼び出す
調べた限り、普通にはできない様子。
今回の方法は、リフレクションでどこぞのコンストラクタに無理やりアクセスするので保証はされないソースになってしまっています。
ちゃんとした(オフィシャルな)方法があるなら知りたいです。
/**
* デフォルトメソッド実行
*/
private static Object invokeDefaultMethod(Object proxy, Method method, Object[] args) throws Throwable {
//コンストラクタにリフレクションでアクセス
Constructor<MethodHandles.Lookup> constructor = MethodHandles.Lookup.class
.getDeclaredConstructor(Class.class, int.class);
constructor.setAccessible(true);
Class<?> declaringClass = method.getDeclaringClass();
int allModes = MethodHandles.Lookup.PUBLIC | MethodHandles.Lookup.PRIVATE | MethodHandles.Lookup.PROTECTED | MethodHandles.Lookup.PACKAGE;
return constructor.newInstance(declaringClass, allModes)
.unreflectSpecial(method, declaringClass)
.bindTo(proxy)
.invokeWithArguments(args);
}
実行
String[] array = { "a", "b", "c" };
@SuppressWarnings("unchecked")
List<String> proxyList = (List<String>) Proxy.newProxyInstance(
Thread.currentThread().getContextClassLoader(),
new Class<?>[] { List.class },
(proxy, method, args) -> {
if (method.isDefault()) {
return invokeDefaultMethod(proxy, method, args);
} else if (method.getName().equals("iterator") && method.getParameterCount() == 0) {
//forEach()内部ではiterator()が必要
return Arrays.stream(array).iterator();
}
throw new UnsupportedOperationException(method.toString());
});
//デフォルトメソッドのforEachを実行
proxyList.forEach(System.out::print);//abc
System.out.println();
ダメなパターン
普通にこう書きたいところ。
private static Object invokeDefaultMethod(Object proxy, Method method, Object[] args) throws Throwable {
Class<?> declaringClass = method.getDeclaringClass();
return MethodHandles.lookup().in(declaringClass)
.unreflectSpecial(method, declaringClass)
.bindTo(proxy)
.invokeWithArguments(args);
}
しかしこれだとエラー
残念。
Exception in thread "main" java.lang.reflect.UndeclaredThrowableException
at com.sun.proxy.$Proxy0.forEach(Unknown Source)
at hogehoge.XXXXXX.main(XXXXXX.java:XX)
Caused by: java.lang.IllegalAccessException: no private access for invokespecial: interface java.lang.Iterable, from java.lang.Iterable/public
at java.lang.invoke.MemberName.makeAccessException(MemberName.java:852)
at java.lang.invoke.MethodHandles$Lookup.checkSpecialCaller(MethodHandles.java:1568)
at java.lang.invoke.MethodHandles$Lookup.unreflectSpecial(MethodHandles.java:1227)
at hogehoge.XXXXXX.invokeDefaultMethod(XXXXXX.java:XX)
at hogehoge.XXXXXX.lambda$1(XXXXXX.java:XX)
... 2 more