package net.bytebuddy.implementation; import net.bytebuddy.ByteBuddy; import net.bytebuddy.description.modifier.Visibility; import net.bytebuddy.dynamic.DynamicType; import net.bytebuddy.dynamic.loading.ClassLoadingStrategy; import net.bytebuddy.implementation.bind.annotation.DefaultMethod; import net.bytebuddy.implementation.bind.annotation.This; import net.bytebuddy.test.utility.JavaVersionRule; import org.junit.Rule; import org.junit.Test; import org.junit.rules.MethodRule; import java.lang.reflect.Method; import java.lang.reflect.Modifier; import static org.hamcrest.CoreMatchers.is; import static org.hamcrest.MatcherAssert.assertThat; public class MethodDelegationDefaultMethodTest { private static final String FOO = "foo", QUX = "qux"; private static final String SINGLE_DEFAULT_METHOD = "net.bytebuddy.test.precompiled.SingleDefaultMethodInterface"; private static final String CONFLICTING_INTERFACE = "net.bytebuddy.test.precompiled.SingleDefaultMethodConflictingInterface"; private static final String PREFERRING_INTERCEPTOR = "net.bytebuddy.test.precompiled.SingleDefaultMethodPreferringInterceptor"; private static final String CONFLICTING_PREFERRING_INTERCEPTOR = "net.bytebuddy.test.precompiled.SingleDefaultMethodConflictingPreferringInterceptor"; @Rule public MethodRule javaVersionRule = new JavaVersionRule(); @Test @JavaVersionRule.Enforce(8) public void testCallableDefaultCall() throws Exception { DynamicType.Loaded<?> loaded = new ByteBuddy() .subclass(Object.class) .implement(Class.forName(SINGLE_DEFAULT_METHOD)) .intercept(MethodDelegation.to(SampleClass.class)) .make() .load(Class.forName(SINGLE_DEFAULT_METHOD).getClassLoader(), ClassLoadingStrategy.Default.WRAPPER); Object instance = loaded.getLoaded().getDeclaredConstructor().newInstance(); Method method = loaded.getLoaded().getMethod(FOO); assertThat(method.invoke(instance), is((Object) FOO)); } @Test(expected = IllegalArgumentException.class) @JavaVersionRule.Enforce(8) public void testImplicitAmbiguousDefaultCallIsBoundToFirst() throws Exception { new ByteBuddy() .subclass(Object.class) .implement(Class.forName(SINGLE_DEFAULT_METHOD), Class.forName(CONFLICTING_INTERFACE)) .defineMethod(FOO, Object.class, Visibility.PUBLIC) .intercept(MethodDelegation.to(SampleClass.class)) .make(); } @Test @JavaVersionRule.Enforce(8) public void testExplicitDefaultCall() throws Exception { DynamicType.Loaded<?> loaded = new ByteBuddy() .subclass(Object.class) .implement(Class.forName(SINGLE_DEFAULT_METHOD), Class.forName(CONFLICTING_INTERFACE)) .intercept(MethodDelegation.to(Class.forName(PREFERRING_INTERCEPTOR))) .make() .load(getClass().getClassLoader(), ClassLoadingStrategy.Default.WRAPPER); Object instance = loaded.getLoaded().getDeclaredConstructor().newInstance(); Method method = loaded.getLoaded().getMethod(FOO); assertThat(method.invoke(instance), is((Object) FOO)); } @Test @JavaVersionRule.Enforce(8) public void testExplicitDefaultCallToOtherInterface() throws Exception { DynamicType.Loaded<?> loaded = new ByteBuddy() .subclass(Object.class) .implement(Class.forName(SINGLE_DEFAULT_METHOD), Class.forName(CONFLICTING_INTERFACE)) .intercept(MethodDelegation.to(Class.forName(CONFLICTING_PREFERRING_INTERCEPTOR))) .make() .load(getClass().getClassLoader(), ClassLoadingStrategy.Default.WRAPPER); Object instance = loaded.getLoaded().getDeclaredConstructor().newInstance(); Method method = loaded.getLoaded().getMethod(FOO); assertThat(method.invoke(instance), is((Object) QUX)); } @Test(expected = IllegalStateException.class) @JavaVersionRule.Enforce(8) public void testIllegalDefaultCallThrowsException() throws Exception { new ByteBuddy() .subclass(Object.class) .implement(Class.forName(SINGLE_DEFAULT_METHOD)) .intercept(MethodDelegation.to(IllegalAnnotation.class)) .make(); } public static class SampleClass { public static String bar(@DefaultMethod Method method, @This Object target) throws Exception { if (!Modifier.isPublic(method.getModifiers())) { throw new AssertionError(); } return (String) method.invoke(target); } } public static class IllegalAnnotation { public static String bar(@DefaultMethod String value) throws Exception { return value; } } }