From ad849fb3a227631e073814c2cd34621c93bf019d Mon Sep 17 00:00:00 2001 From: Juergen Hoeller Date: Mon, 8 Dec 2025 12:41:43 +0100 Subject: [PATCH] Accept assignable match for covariant return type See gh-35936 (cherry picked from commit df27627516a9a6a835dda44563dce4328b1639c5) --- .../core/BridgeMethodResolver.java | 8 ++-- .../core/BridgeMethodResolverTests.java | 38 +++++++++++++++++-- 2 files changed, 38 insertions(+), 8 deletions(-) diff --git a/spring-core/src/main/java/org/springframework/core/BridgeMethodResolver.java b/spring-core/src/main/java/org/springframework/core/BridgeMethodResolver.java index 06e83ec27ca..4ca2912ea17 100644 --- a/spring-core/src/main/java/org/springframework/core/BridgeMethodResolver.java +++ b/spring-core/src/main/java/org/springframework/core/BridgeMethodResolver.java @@ -202,15 +202,15 @@ 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()))) { + if (!ClassUtils.resolvePrimitiveIfNecessary(genericReturnType.toClass()).isAssignableFrom( + ClassUtils.resolvePrimitiveIfNecessary(candidateMethod.getReturnType()))) { return false; } Class[] candidateParameters = candidateMethod.getParameterTypes(); for (int i = 0; i < candidateParameters.length; i++) { ResolvableType genericParameter = ResolvableType.forMethodParameter(genericMethod, i, clazz); - if (!ClassUtils.resolvePrimitiveIfNecessary(candidateParameters[i]).equals( - ClassUtils.resolvePrimitiveIfNecessary(genericParameter.toClass()))) { + if (!ClassUtils.resolvePrimitiveIfNecessary(genericParameter.toClass()).equals( + ClassUtils.resolvePrimitiveIfNecessary(candidateParameters[i]))) { return false; } } diff --git a/spring-core/src/test/java/org/springframework/core/BridgeMethodResolverTests.java b/spring-core/src/test/java/org/springframework/core/BridgeMethodResolverTests.java index 85ea80b7ef3..496c36018e0 100644 --- a/spring-core/src/test/java/org/springframework/core/BridgeMethodResolverTests.java +++ b/spring-core/src/test/java/org/springframework/core/BridgeMethodResolverTests.java @@ -113,6 +113,14 @@ class BridgeMethodResolverTests { } } + @Test + void findBridgedMethodForCovariantReturnType() throws Exception { + Method originalMethod = OuterSubclass.class.getDeclaredMethod("getInner"); + for (Method method: OuterSubclass.class.getDeclaredMethods()) { + assertThat(BridgeMethodResolver.findBridgedMethod(method)).isEqualTo(originalMethod); + } + } + @Test void findBridgedMethodInHierarchyWithBoundedGenerics() throws Exception { Method originalMethod = Bar.class.getDeclaredMethod("someMethod", Object.class, Object.class); @@ -425,6 +433,7 @@ class BridgeMethodResolverTests { public abstract static class SubBar extends InterBar { } + public interface StringProducer extends CharSequence { } @@ -506,6 +515,31 @@ class BridgeMethodResolverTests { } + static class Outer { + + Inner getInner() { + return new Inner(); + } + } + + + static class OuterSubclass extends Outer { + + @Override + InnerSubclass getInner() { + return new InnerSubclass(); + } + } + + + static class Inner { + } + + + static class InnerSubclass extends Inner { + } + + public static class Enclosing { public class Enclosed { @@ -899,22 +933,18 @@ class BridgeMethodResolverTests { public interface MessageBroadcaster extends Receiver { - } public static class RemovedMessageEvent extends MessageEvent { - } public static class NewMessageEvent extends MessageEvent { - } public static class ModifiedMessageEvent extends MessageEvent { - }