How do I create a proxy and invoke default interface methods as if they were implemented by the proxy super-class? For example:
interface Foo {
default int returnSomething() {
return 1;
}
}
interface Bar extends Foo {
default int returnSomethingMore() {
return returnSomething();
}
}
class Super implements Foo {
@Override
public int returnSomething() {
return 2;
}
}
I need a proxy of Bar
, which will use the Super::returnSomething
implementation when calling Bar::returnSomethingMore
.
I tried this:
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(Super.class);
enhancer.setInterfaces(new Class[] { Bar.class });
enhancer.setCallback((obj, method, args, proxy) -> {
if (method.getName().equals("returnSomethingMore")) {
return proxy.invokeSuper(obj, args);
// -> NoSuchMethodError
return proxy.invoke(obj, args);
// -> StackOverflowError
Class<?> declaringClass = method.getDeclaringClass();
Lookup lookup = MethodHandles.lookup();
return MethodHandles
.privateLookupIn(declaringClass, lookup)
.unreflectSpecial(method, declaringClass)
.bindTo(obj)
.invokeWithArguments(args);
// -> returns 1, not 2
}
});
How do I create a proxy object whose returnSomethingMore
method returns 2
?
I've dropped cglib
(which thanks to SO's tag I've known it's no longer in active development) and adopted ByteBuddy
which gave me the proxy that behaves as I need:
@Test
public void defaultInterfaceMethodTest() {
Class<? extends Super> loaded = new ByteBuddy()
.subclass(Super.class)
.implement(Bar.class).make()
.load(this.getClass().getClassLoader()).getLoaded();
Bar newInstance = (Bar) loaded.getConstructor().newInstance();
int shouldBeTwo = newInstance.returnSomethingMore();
Assert.assertEquals(2, shouldBeTwo);
}