From 91a0107e4abba457f72ed1b6bd541611039832fe Mon Sep 17 00:00:00 2001 From: Juergen Hoeller Date: Fri, 20 Feb 2015 21:53:44 +0100 Subject: [PATCH] ResolvableType.getType() returns ParameterizedType when built with forClassWithGenerics Issue: SPR-12701 --- .../springframework/core/ResolvableType.java | 55 ++++++++++++++++--- .../core/ResolvableTypeTests.java | 13 ++++- 2 files changed, 58 insertions(+), 10 deletions(-) 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 982546bb252..aed8d7e60a5 100644 --- a/spring-core/src/main/java/org/springframework/core/ResolvableType.java +++ b/spring-core/src/main/java/org/springframework/core/ResolvableType.java @@ -1131,11 +1131,21 @@ public final class ResolvableType implements Serializable { * @return a {@link ResolvableType} for the specific class and generics * @see #forClassWithGenerics(Class, Class...) */ - public static ResolvableType forClassWithGenerics(Class sourceClass, ResolvableType... generics) { + public static ResolvableType forClassWithGenerics(final Class sourceClass, final ResolvableType... generics) { Assert.notNull(sourceClass, "Source class must not be null"); Assert.notNull(generics, "Generics must not be null"); - TypeVariable[] typeVariables = sourceClass.getTypeParameters(); - return forType(sourceClass, new TypeVariablesVariableResolver(typeVariables, generics)); + TypeVariable[] variables = sourceClass.getTypeParameters(); + Assert.isTrue(variables.length == generics.length, "Mismatched number of generics specified"); + + Type[] arguments = new Type[generics.length]; + for (int i = 0; i < generics.length; i++) { + ResolvableType generic = generics[i]; + Type argument = (generic != null ? generic.getType() : null); + arguments[i] = (argument != null ? argument : variables[i]); + } + + ParameterizedType syntheticType = new SyntheticParameterizedType(sourceClass, arguments); + return forType(syntheticType, new TypeVariablesVariableResolver(variables, generics)); } /** @@ -1249,20 +1259,19 @@ public final class ResolvableType implements Serializable { @SuppressWarnings("serial") private static class TypeVariablesVariableResolver implements VariableResolver { - private final TypeVariable[] typeVariables; + private final TypeVariable[] variables; private final ResolvableType[] generics; - public TypeVariablesVariableResolver(TypeVariable[] typeVariables, ResolvableType[] generics) { - Assert.isTrue(typeVariables.length == generics.length, "Mismatched number of generics specified"); - this.typeVariables = typeVariables; + public TypeVariablesVariableResolver(TypeVariable[] variables, ResolvableType[] generics) { + this.variables = variables; this.generics = generics; } @Override public ResolvableType resolveVariable(TypeVariable variable) { - for (int i = 0; i < this.typeVariables.length; i++) { - if (SerializableTypeWrapper.unwrap(this.typeVariables[i]).equals( + for (int i = 0; i < this.variables.length; i++) { + if (SerializableTypeWrapper.unwrap(this.variables[i]).equals( SerializableTypeWrapper.unwrap(variable))) { return this.generics[i]; } @@ -1277,6 +1286,34 @@ public final class ResolvableType implements Serializable { } + private static final class SyntheticParameterizedType implements ParameterizedType, Serializable { + + private final Type rawType; + + private final Type[] typeArguments; + + public SyntheticParameterizedType(Type rawType, Type[] typeArguments) { + this.rawType = rawType; + this.typeArguments = typeArguments; + } + + @Override + public Type[] getActualTypeArguments() { + return this.typeArguments; + } + + @Override + public Type getRawType() { + return this.rawType; + } + + @Override + public Type getOwnerType() { + return null; + } + } + + /** * Internal helper to handle bounds from {@link WildcardType}s. */ 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 31a1a13ba81..7b341451c58 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-2014 the original author or authors. + * Copyright 2002-2015 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. @@ -39,6 +39,7 @@ import java.util.List; import java.util.Map; import java.util.SortedSet; import java.util.TreeSet; +import java.util.concurrent.Callable; import org.hamcrest.Matchers; import org.junit.Rule; @@ -1210,6 +1211,16 @@ public class ResolvableTypeTests { assertEquals("java.util.Collection>", type.toString()); } + @Test + public void testSpr12701() throws Exception { + ResolvableType resolvableType = ResolvableType.forClassWithGenerics(Callable.class, String.class); + Type type = resolvableType.getType(); + assertThat(type, is(instanceOf(ParameterizedType.class))); + assertThat(((ParameterizedType) type).getRawType(), is(equalTo(Callable.class))); + assertThat(((ParameterizedType) type).getActualTypeArguments().length, is(equalTo(1))); + assertThat(((ParameterizedType) type).getActualTypeArguments()[0], is(equalTo(String.class))); + } + private ResolvableType testSerialization(ResolvableType type) throws Exception { ByteArrayOutputStream bos = new ByteArrayOutputStream();