diff --git a/spring-core/src/main/java/org/springframework/core/ResolvableType.java b/spring-core/src/main/java/org/springframework/core/ResolvableType.java index 60344942ce..8c678723af 100644 --- a/spring-core/src/main/java/org/springframework/core/ResolvableType.java +++ b/spring-core/src/main/java/org/springframework/core/ResolvableType.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2013 the original author or authors. + * Copyright 2002-2014 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -835,6 +835,23 @@ public final class ResolvableType implements Serializable { return forType(null, new FieldTypeProvider(field), owner.asVariableResolver()); } + /** + * Return a {@link ResolvableType} for the specified {@link Field} with a given + * implementation. + *

Use this variant when the class that declares the field includes generic + * parameter variables that are satisfied by the implementation type. + * @param field the source field + * @param implementationType the implementation type + * @return a {@link ResolvableType} for the specified field + * @see #forField(Field) + */ + public static ResolvableType forField(Field field, ResolvableType implementationType) { + Assert.notNull(field, "Field must not be null"); + implementationType = (implementationType == null ? NONE : implementationType); + ResolvableType owner = implementationType.as(field.getDeclaringClass()); + return forType(null, new FieldTypeProvider(field), owner.asVariableResolver()); + } + /** * Return a {@link ResolvableType} for the specified {@link Field} with the * given nesting level. @@ -963,7 +980,25 @@ public final class ResolvableType implements Serializable { * @see #forMethodParameter(Method, int) */ public static ResolvableType forMethodParameter(MethodParameter methodParameter) { - return forMethodParameter(methodParameter, null); + return forMethodParameter(methodParameter, (Type) null); + } + + /** + * Return a {@link ResolvableType} for the specified {@link MethodParameter} with a + * given implementation type. Use this variant when the class that declares the method + * includes generic parameter variables that are satisfied by the implementation type. + * @param methodParameter the source method parameter (must not be {@code null}) + * @param implementationType the implementation type + * @return a {@link ResolvableType} for the specified method parameter + * @see #forMethodParameter(MethodParameter) + */ + public static ResolvableType forMethodParameter(MethodParameter methodParameter, ResolvableType implementationType) { + Assert.notNull(methodParameter, "MethodParameter must not be null"); + implementationType = (implementationType == null ? forType(methodParameter.getContainingClass()) : implementationType); + ResolvableType owner = implementationType.as(methodParameter.getDeclaringClass()); + return forType(null, new MethodParameterTypeProvider(methodParameter), + owner.asVariableResolver()).getNested(methodParameter.getNestingLevel(), + methodParameter.typeIndexesPerLevel); } /** diff --git a/spring-core/src/test/java/org/springframework/core/ResolvableTypeTests.java b/spring-core/src/test/java/org/springframework/core/ResolvableTypeTests.java index ea9aaed552..31a1a13ba8 100644 --- a/spring-core/src/test/java/org/springframework/core/ResolvableTypeTests.java +++ b/spring-core/src/test/java/org/springframework/core/ResolvableTypeTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2013 the original author or authors. + * Copyright 2002-2014 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -659,6 +659,16 @@ public class ResolvableTypeTests { assertThat(type.getGeneric().resolve(), equalTo((Class) String.class)); } + @Test + public void resolveTypeVariableFromFieldTypeWithImplementsType() throws Exception { + ResolvableType implementationType = ResolvableType.forClassWithGenerics( + Fields.class, Integer.class); + ResolvableType type = ResolvableType.forField( + Fields.class.getField("parameterizedType"), implementationType); + assertThat(type.resolve(), equalTo((Class) List.class)); + assertThat(type.getGeneric().resolve(), equalTo((Class) Integer.class)); + } + @Test public void resolveTypeVariableFromSuperType() throws Exception { ResolvableType type = ResolvableType.forClass(ExtendsList.class); @@ -735,6 +745,16 @@ public class ResolvableTypeTests { assertThat(type.getType().toString(), equalTo("T")); } + @Test + public void resolveTypeVariableFromMethodParameterTypeWithImplementsType() throws Exception { + Method method = Methods.class.getMethod("typedParameter", Object.class); + MethodParameter methodParameter = MethodParameter.forMethodOrConstructor(method, 0); + ResolvableType implementationType = ResolvableType.forClassWithGenerics(Methods.class, Integer.class); + ResolvableType type = ResolvableType.forMethodParameter(methodParameter, implementationType); + assertThat(type.resolve(), equalTo((Class) Integer.class)); + assertThat(type.getType().toString(), equalTo("T")); + } + @Test public void resolveTypeVariableFromMethodReturn() throws Exception { Method method = Methods.class.getMethod("typedReturn");