diff --git a/spring-core/src/main/java/org/springframework/core/ParameterizedTypeReference.java b/spring-core/src/main/java/org/springframework/core/ParameterizedTypeReference.java index dcb9c29591b..7f6d807fe25 100644 --- a/spring-core/src/main/java/org/springframework/core/ParameterizedTypeReference.java +++ b/spring-core/src/main/java/org/springframework/core/ParameterizedTypeReference.java @@ -53,6 +53,10 @@ public abstract class ParameterizedTypeReference { this.type = parameterizedType.getActualTypeArguments()[0]; } + private ParameterizedTypeReference(Type type) { + this.type = type; + } + public Type getType() { return this.type; @@ -75,6 +79,19 @@ public abstract class ParameterizedTypeReference { } + /** + * Build a {@code ParameterizedTypeReference} wrapping the given type. + * @param type a generic type (possibly obtained via reflection, + * e.g. from {@link java.lang.reflect.Method#getGenericReturnType()}) + * @return a corresponding reference which may be passed into + * {@code ParameterizedTypeReference}-accepting methods + * @since 4.3.12 + */ + public static ParameterizedTypeReference forType(Type type) { + return new ParameterizedTypeReference(type) { + }; + } + private static Class findParameterizedTypeReferenceSubclass(Class child) { Class parent = child.getSuperclass(); if (Object.class == parent) { 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 7853d88d766..35e6da4df88 100644 --- a/spring-core/src/main/java/org/springframework/core/ResolvableType.java +++ b/spring-core/src/main/java/org/springframework/core/ResolvableType.java @@ -1309,6 +1309,19 @@ public class ResolvableType implements Serializable { return forType(type, variableResolver); } + + /** + * Return a {@link ResolvableType} for the specified {@link ParameterizedTypeReference}. + * Note: The resulting {@link ResolvableType} may not be {@link Serializable}. + * @param typeReference the reference to obtain the source type from + * @return a {@link ResolvableType} for the specified {@link ParameterizedTypeReference} + * @since 4.3.12 + * @see #forType(Type) + */ + public static ResolvableType forType(ParameterizedTypeReference typeReference) { + return forType(typeReference.getType(), null, null); + } + /** * Return a {@link ResolvableType} for the specified {@link Type} backed by a given * {@link VariableResolver}. @@ -1545,7 +1558,7 @@ public class ResolvableType implements Serializable { } WildcardType wildcardType = (WildcardType) resolveToWildcard.type; Kind boundsType = (wildcardType.getLowerBounds().length > 0 ? Kind.LOWER : Kind.UPPER); - Type[] bounds = boundsType == Kind.UPPER ? wildcardType.getUpperBounds() : wildcardType.getLowerBounds(); + Type[] bounds = (boundsType == Kind.UPPER ? wildcardType.getUpperBounds() : wildcardType.getLowerBounds()); ResolvableType[] resolvableBounds = new ResolvableType[bounds.length]; for (int i = 0; i < bounds.length; i++) { resolvableBounds[i] = ResolvableType.forType(bounds[i], type.variableResolver); diff --git a/spring-core/src/test/java/org/springframework/core/ParameterizedTypeReferenceTests.java b/spring-core/src/test/java/org/springframework/core/ParameterizedTypeReferenceTests.java index 56060b2468e..686d581295b 100644 --- a/spring-core/src/test/java/org/springframework/core/ParameterizedTypeReferenceTests.java +++ b/spring-core/src/test/java/org/springframework/core/ParameterizedTypeReferenceTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2013 the original author or authors. + * Copyright 2002-2017 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. @@ -33,25 +33,40 @@ import static org.junit.Assert.*; public class ParameterizedTypeReferenceTests { @Test - public void map() throws NoSuchMethodException { + public void stringTypeReference() { + ParameterizedTypeReference typeReference = new ParameterizedTypeReference() {}; + assertEquals(String.class, typeReference.getType()); + } + + @Test + public void mapTypeReference() throws Exception { Type mapType = getClass().getMethod("mapMethod").getGenericReturnType(); - ParameterizedTypeReference> mapTypeReference = new ParameterizedTypeReference>() {}; - assertEquals(mapType, mapTypeReference.getType()); + ParameterizedTypeReference> typeReference = new ParameterizedTypeReference>() {}; + assertEquals(mapType, typeReference.getType()); } @Test - public void list() throws NoSuchMethodException { - Type mapType = getClass().getMethod("listMethod").getGenericReturnType(); - ParameterizedTypeReference> mapTypeReference = new ParameterizedTypeReference>() {}; - assertEquals(mapType, mapTypeReference.getType()); + public void listTypeReference() throws Exception { + Type listType = getClass().getMethod("listMethod").getGenericReturnType(); + ParameterizedTypeReference> typeReference = new ParameterizedTypeReference>() {}; + assertEquals(listType, typeReference.getType()); } @Test - public void string() { - ParameterizedTypeReference typeReference = new ParameterizedTypeReference() {}; - assertEquals(String.class, typeReference.getType()); + public void reflectiveTypeReferenceWithSpecificDeclaration() throws Exception{ + Type listType = getClass().getMethod("listMethod").getGenericReturnType(); + ParameterizedTypeReference> typeReference = ParameterizedTypeReference.forType(listType); + assertEquals(listType, typeReference.getType()); } + @Test + public void reflectiveTypeReferenceWithGenericDeclaration() throws Exception{ + Type listType = getClass().getMethod("listMethod").getGenericReturnType(); + ParameterizedTypeReference typeReference = ParameterizedTypeReference.forType(listType); + assertEquals(listType, typeReference.getType()); + } + + public static Map mapMethod() { return null; } 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 57e77d220b6..ab903fc6d79 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-2016 the original author or authors. + * Copyright 2002-2017 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. @@ -869,6 +869,22 @@ public class ResolvableTypeTests { assertThat(this.typeVariableCaptor.getValue().getName(), equalTo("T")); } + @Test + public void resolveTypeVariableFromReflectiveParameterizedTypeReference() throws Exception { + Type sourceType = Methods.class.getMethod("typedReturn").getGenericReturnType(); + ResolvableType type = ResolvableType.forType(ParameterizedTypeReference.forType(sourceType)); + assertThat(type.resolve(), nullValue()); + assertThat(type.getType().toString(), equalTo("T")); + } + + @Test + public void resolveTypeVariableFromDeclaredParameterizedTypeReference() throws Exception { + Type sourceType = Methods.class.getMethod("charSequenceReturn").getGenericReturnType(); + ResolvableType reflectiveType = ResolvableType.forType(sourceType); + ResolvableType declaredType = ResolvableType.forType(new ParameterizedTypeReference>() {}); + assertEquals(reflectiveType, declaredType); + } + @Test public void toStrings() throws Exception { assertThat(ResolvableType.NONE.toString(), equalTo("?"));