diff --git a/.version b/.version index feaae22..b50dd27 100644 --- a/.version +++ b/.version @@ -1 +1 @@ -1.13.0 +1.13.1 diff --git a/CHANGELOG.md b/CHANGELOG.md index de504f5..3e0135e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,8 @@ # Changelog +#### Version 1.13.1 (October 1, 2022) +* Fixed a bug that caused a `NoSuchMethodException` to be thrown when using the `Reflection` utility to `call` a non-overriden interface-defined default method. + #### Version 1.13.0 (September 5, 2022) * Added the `ByteBuffers#share` method that returns a new ByteBuffer containing a shared subseqence of a source `ByteBuffer` while incrementing the `position` of the source the same number of bytes that are shared. * Upgraded `logback` dependency to version `1.2.11` diff --git a/src/main/java/com/cinchapi/common/reflect/Reflection.java b/src/main/java/com/cinchapi/common/reflect/Reflection.java index 0f32670..5f896ef 100644 --- a/src/main/java/com/cinchapi/common/reflect/Reflection.java +++ b/src/main/java/com/cinchapi/common/reflect/Reflection.java @@ -23,10 +23,12 @@ import java.lang.reflect.Modifier; import java.lang.reflect.ParameterizedType; import java.lang.reflect.Type; +import java.util.ArrayDeque; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.Collections; +import java.util.Deque; import java.util.List; import java.util.Map; import java.util.Set; @@ -968,8 +970,10 @@ private static Method getMethod(@Nullable Object[] args, Class... paramTypes) { List potential = Lists.newArrayListWithCapacity(1); List deferred = Lists.newArrayListWithCapacity(1); + Deque> queue = new ArrayDeque<>(); + queue.add(clazz); try { - while (clazz != null) { + while ((clazz = queue.poll()) != null) { for (Method method : Arrays.stream(clazz.getDeclaredMethods()) .filter(method -> method.getName().equals(name)) .collect(Collectors.toList())) { @@ -989,7 +993,15 @@ else if(callable != TernaryTruth.FALSE } } if(potential.isEmpty()) { - clazz = clazz.getSuperclass(); + Class superClass = clazz.getSuperclass(); + if(superClass != null) { + queue.add(superClass); + } + for (Class iface : clazz.getInterfaces()) { + // Account for default interface methods that are not + // explicitly overridden in the the #clazz. + queue.add(iface); + } } else { break; diff --git a/src/test/java/com/cinchapi/common/reflect/ReflectionTest.java b/src/test/java/com/cinchapi/common/reflect/ReflectionTest.java index 36d5123..c054dfe 100644 --- a/src/test/java/com/cinchapi/common/reflect/ReflectionTest.java +++ b/src/test/java/com/cinchapi/common/reflect/ReflectionTest.java @@ -406,6 +406,15 @@ public void testCallOverrideWithGenericParameter() { Reflection.call(obj, "put", "Company", "Cinchapi"); Assert.assertTrue(true); // lack of Exception means we pass } + + @Test + public void testCallDefaultInterfaceMethod() { + ClassB obj = new ClassB(); + String expected = "Jeff Nelson"; + String actual = Reflection.call(obj, "foo", expected); + Assert.assertEquals("foo_"+expected, actual); + Assert.assertEquals("baz", Reflection.call(obj, "baz", expected)); + } private static class A { @@ -585,5 +594,41 @@ public void put(String key, T value) { } } + + class ClassA implements InterfaceA, InterfaceB { + + + @Override + public String baz(String value) { + return value; + } + + } + + class ClassB extends ClassA { + + @Override + public String baz(String value) { + return "baz"; + } + } + + interface InterfaceA { + + public String baz(String value); + + public default String foo(String value) { + return "foo_"+value; + } + + } + + interface InterfaceB { + public default String bar(String value) { + return "bar_"+value; + } + + public String baz(String value); + } }