mirror of
https://github.com/spring-projects/spring-framework
synced 2026-06-08 17:33:33 +00:00
Select method with resolved return type match (among multiple candidates)
Removes unnecessary array type check for parameters of candidate methods. Closes gh-35936
This commit is contained in:
@@ -29,7 +29,6 @@ import org.jspecify.annotations.Nullable;
|
||||
import org.springframework.util.ClassUtils;
|
||||
import org.springframework.util.ConcurrentReferenceHashMap;
|
||||
import org.springframework.util.ReflectionUtils;
|
||||
import org.springframework.util.ReflectionUtils.MethodFilter;
|
||||
|
||||
/**
|
||||
* Helper for resolving synthetic {@link Method#isBridge bridge Methods} to the
|
||||
@@ -115,8 +114,8 @@ public final class BridgeMethodResolver {
|
||||
if (bridgedMethod == null) {
|
||||
// Gather all methods with matching name and parameter size.
|
||||
List<Method> candidateMethods = new ArrayList<>();
|
||||
MethodFilter filter = (candidateMethod -> isBridgedCandidateFor(candidateMethod, bridgeMethod));
|
||||
ReflectionUtils.doWithMethods(userClass, candidateMethods::add, filter);
|
||||
ReflectionUtils.doWithMethods(userClass, candidateMethods::add,
|
||||
candidateMethod -> isBridgedCandidateFor(candidateMethod, bridgeMethod));
|
||||
if (!candidateMethods.isEmpty()) {
|
||||
bridgedMethod = (candidateMethods.size() == 1 ? candidateMethods.get(0) :
|
||||
searchCandidates(candidateMethods, bridgeMethod, targetClass));
|
||||
@@ -152,9 +151,6 @@ public final class BridgeMethodResolver {
|
||||
private static @Nullable Method searchCandidates(
|
||||
List<Method> candidateMethods, Method bridgeMethod, Class<?> targetClass) {
|
||||
|
||||
if (candidateMethods.isEmpty()) {
|
||||
return null;
|
||||
}
|
||||
Method previousMethod = null;
|
||||
boolean sameSig = true;
|
||||
for (Method candidateMethod : candidateMethods) {
|
||||
@@ -204,18 +200,16 @@ public final class BridgeMethodResolver {
|
||||
}
|
||||
|
||||
private static boolean checkResolvedTypeMatch(Method genericMethod, Method candidateMethod, Class<?> clazz) {
|
||||
// First, compare return type.
|
||||
ResolvableType genericReturnType = ResolvableType.forMethodReturnType(genericMethod, clazz);
|
||||
if (!ClassUtils.resolvePrimitiveIfNecessary(candidateMethod.getReturnType()).equals(
|
||||
ClassUtils.resolvePrimitiveIfNecessary(genericReturnType.toClass()))) {
|
||||
return false;
|
||||
}
|
||||
Class<?>[] candidateParameters = candidateMethod.getParameterTypes();
|
||||
for (int i = 0; i < candidateParameters.length; i++) {
|
||||
ResolvableType genericParameter = ResolvableType.forMethodParameter(genericMethod, i, clazz);
|
||||
Class<?> candidateParameter = candidateParameters[i];
|
||||
if (candidateParameter.isArray()) {
|
||||
// An array type: compare the component type.
|
||||
if (!candidateParameter.componentType().equals(genericParameter.getComponentType().toClass())) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
// A non-array type: compare the type itself.
|
||||
if (!ClassUtils.resolvePrimitiveIfNecessary(candidateParameter).equals(
|
||||
if (!ClassUtils.resolvePrimitiveIfNecessary(candidateParameters[i]).equals(
|
||||
ClassUtils.resolvePrimitiveIfNecessary(genericParameter.toClass()))) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -44,17 +44,6 @@ import static org.assertj.core.api.Assertions.assertThat;
|
||||
@SuppressWarnings("rawtypes")
|
||||
class BridgeMethodResolverTests {
|
||||
|
||||
private static Method findMethodWithReturnType(String name, Class<?> returnType, Class<SettingsDaoImpl> targetType) {
|
||||
Method[] methods = targetType.getMethods();
|
||||
for (Method m : methods) {
|
||||
if (m.getName().equals(name) && m.getReturnType().equals(returnType)) {
|
||||
return m;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
void findBridgedMethod() throws Exception {
|
||||
Method unbridged = MyFoo.class.getDeclaredMethod("someMethod", String.class, Object.class);
|
||||
@@ -106,6 +95,24 @@ class BridgeMethodResolverTests {
|
||||
assertThat(mostSpecificMethod).isSameAs(originalMethod);
|
||||
}
|
||||
|
||||
@Test
|
||||
void findBridgedMethodWithDefaultMethodInInterfaceHierarchy() throws Exception {
|
||||
Method getValueDefault = DefaultMethods.class.getMethod("getValue");
|
||||
Method getValuesDefault = DefaultMethods.class.getMethod("getValues");
|
||||
Method getValueArgDefault = DefaultMethods.class.getMethod("getValue", Integer.class);
|
||||
Method getValuesArgDefault = DefaultMethods.class.getMethod("getValues", Integer[].class);
|
||||
for (Method method : ConcreteMethods.class.getMethods()) {
|
||||
if (method.getName().equals("getValue")) {
|
||||
assertThat(BridgeMethodResolver.findBridgedMethod(method)).isEqualTo(
|
||||
method.getParameterCount() > 0 ? getValueArgDefault : getValueDefault);
|
||||
}
|
||||
else if (method.getName().equals("getValues")) {
|
||||
assertThat(BridgeMethodResolver.findBridgedMethod(method)).isEqualTo(
|
||||
method.getParameterCount() > 0 ? getValuesArgDefault : getValuesDefault);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void findBridgedMethodInHierarchyWithBoundedGenerics() throws Exception {
|
||||
Method originalMethod = Bar.class.getDeclaredMethod("someMethod", Object.class, Object.class);
|
||||
@@ -161,6 +168,16 @@ class BridgeMethodResolverTests {
|
||||
assertThat(BridgeMethodResolver.findBridgedMethod(loadWithSettingsReturn)).isEqualTo(method);
|
||||
}
|
||||
|
||||
private static Method findMethodWithReturnType(String name, Class<?> returnType, Class<?> targetType) {
|
||||
Method[] methods = targetType.getMethods();
|
||||
for (Method m : methods) {
|
||||
if (m.getName().equals(name) && m.getReturnType().equals(returnType)) {
|
||||
return m;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Test
|
||||
void findBridgedMethodFromParent() throws Exception {
|
||||
Method loadFromParentBridge = SettingsDaoImpl.class.getMethod("loadFromParent");
|
||||
@@ -451,6 +468,44 @@ class BridgeMethodResolverTests {
|
||||
}
|
||||
|
||||
|
||||
interface InterfaceMethods<T> {
|
||||
|
||||
T getValue();
|
||||
|
||||
T[] getValues();
|
||||
|
||||
T getValue(T arg);
|
||||
|
||||
T[] getValues(T[] args);
|
||||
}
|
||||
|
||||
|
||||
interface DefaultMethods extends InterfaceMethods<Integer> {
|
||||
|
||||
default Integer getValue() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
default Integer[] getValues() {
|
||||
return new Integer[0];
|
||||
}
|
||||
|
||||
@Override
|
||||
default Integer getValue(Integer arg) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
default Integer[] getValues(Integer[] args) {
|
||||
return new Integer[0];
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static class ConcreteMethods implements DefaultMethods {
|
||||
}
|
||||
|
||||
|
||||
public static class Enclosing<T> {
|
||||
|
||||
public class Enclosed<S> {
|
||||
|
||||
Reference in New Issue
Block a user