Skip to content

Commit c09e104

Browse files
committed
Workaround for generic parameter types on inner class constructors
Issue: SPR-16734
1 parent 6a727e1 commit c09e104

File tree

2 files changed

+37
-13
lines changed

2 files changed

+37
-13
lines changed

spring-core/src/main/java/org/springframework/core/MethodParameter.java

Lines changed: 15 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -337,7 +337,6 @@ public MethodParameter nestedIfOptional() {
337337
return (isOptional() ? nested() : this);
338338
}
339339

340-
341340
/**
342341
* Set a containing class to resolve the parameter type against.
343342
*/
@@ -390,9 +389,19 @@ public Type getGenericParameterType() {
390389
paramType = (method != null ? method.getGenericReturnType() : void.class);
391390
}
392391
else {
393-
paramType = (this.method != null ?
394-
this.method.getGenericParameterTypes()[this.parameterIndex] :
395-
this.constructor.getGenericParameterTypes()[this.parameterIndex]);
392+
Type[] genericParameterTypes = (this.method != null ?
393+
this.method.getGenericParameterTypes() : this.constructor.getGenericParameterTypes());
394+
int index = this.parameterIndex;
395+
if (this.constructor != null && this.constructor.getDeclaringClass().isMemberClass() &&
396+
!Modifier.isStatic(this.constructor.getDeclaringClass().getModifiers()) &&
397+
genericParameterTypes.length == this.constructor.getParameterTypes().length - 1) {
398+
// Bug in javac: type array excludes enclosing instance parameter
399+
// for inner classes with at least one generic constructor parameter,
400+
// so access it with the actual parameter index lowered by 1
401+
index = this.parameterIndex - 1;
402+
}
403+
paramType = (index >= 0 && index < genericParameterTypes.length ?
404+
genericParameterTypes[index] : getParameterType());
396405
}
397406
this.genericParameterType = paramType;
398407
}
@@ -497,12 +506,8 @@ public Annotation[] getParameterAnnotations() {
497506
// for inner classes, so access it with the actual parameter index lowered by 1
498507
index = this.parameterIndex - 1;
499508
}
500-
if (index >= 0 && index < annotationArray.length) {
501-
paramAnns = adaptAnnotationArray(annotationArray[index]);
502-
}
503-
else {
504-
paramAnns = EMPTY_ANNOTATION_ARRAY;
505-
}
509+
paramAnns = (index >= 0 && index < annotationArray.length ?
510+
adaptAnnotationArray(annotationArray[index]) : EMPTY_ANNOTATION_ARRAY);
506511
this.parameterAnnotations = paramAnns;
507512
}
508513
return paramAnns;

spring-core/src/test/java/org/springframework/core/MethodParameterTests.java

Lines changed: 22 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
import java.lang.annotation.Target;
2323
import java.lang.reflect.Constructor;
2424
import java.lang.reflect.Method;
25+
import java.util.concurrent.Callable;
2526

2627
import org.junit.Before;
2728
import org.junit.Test;
@@ -96,7 +97,7 @@ public void annotatedConstructorParameterInStaticNestedClass() throws Exception
9697

9798
@Test // SPR-16652
9899
public void annotatedConstructorParameterInInnerClass() throws Exception {
99-
Constructor<?> constructor = InnerClass.class.getConstructor(getClass(), String.class, Integer.class);
100+
Constructor<?> constructor = InnerClass.class.getConstructor(getClass(), String.class, Callable.class);
100101

101102
MethodParameter methodParameter = MethodParameter.forMethodOrConstructor(constructor, 0);
102103
assertEquals(getClass(), methodParameter.getParameterType());
@@ -107,10 +108,28 @@ public void annotatedConstructorParameterInInnerClass() throws Exception {
107108
assertNotNull("Failed to find @Param annotation", methodParameter.getParameterAnnotation(Param.class));
108109

109110
methodParameter = MethodParameter.forMethodOrConstructor(constructor, 2);
110-
assertEquals(Integer.class, methodParameter.getParameterType());
111+
assertEquals(Callable.class, methodParameter.getParameterType());
111112
assertNull(methodParameter.getParameterAnnotation(Param.class));
112113
}
113114

115+
@Test // SPR-16734
116+
public void genericConstructorParameterInInnerClass() throws Exception {
117+
Constructor<?> constructor = InnerClass.class.getConstructor(getClass(), String.class, Callable.class);
118+
119+
MethodParameter methodParameter = MethodParameter.forMethodOrConstructor(constructor, 0);
120+
assertEquals(getClass(), methodParameter.getParameterType());
121+
assertEquals(getClass(), methodParameter.getGenericParameterType());
122+
123+
methodParameter = MethodParameter.forMethodOrConstructor(constructor, 1);
124+
assertEquals(String.class, methodParameter.getParameterType());
125+
assertEquals(String.class, methodParameter.getGenericParameterType());
126+
127+
methodParameter = MethodParameter.forMethodOrConstructor(constructor, 2);
128+
assertEquals(Callable.class, methodParameter.getParameterType());
129+
assertEquals(ResolvableType.forClassWithGenerics(Callable.class, Integer.class).getType(),
130+
methodParameter.getGenericParameterType());
131+
}
132+
114133

115134
public int method(String p1, long p2) {
116135
return 42;
@@ -126,7 +145,7 @@ private static class NestedClass {
126145
@SuppressWarnings("unused")
127146
private class InnerClass {
128147

129-
public InnerClass(@Param String s, Integer i) {
148+
public InnerClass(@Param String s, Callable<Integer> i) {
130149
}
131150
}
132151

0 commit comments

Comments
 (0)