From 53091c76bf2f1a3c772c3b9dc24f57526dffb2df Mon Sep 17 00:00:00 2001 From: Juergen Hoeller Date: Mon, 9 Oct 2017 13:59:01 +0200 Subject: [PATCH] Convenient forType methods for ParameterizedTypeReference Issue: SPR-16054 --- .../core/ParameterizedTypeReference.java | 17 +++++++++ .../springframework/core/ResolvableType.java | 13 +++++++ .../core/ParameterizedTypeReferenceTests.java | 37 +++++++++++++------ .../core/ResolvableTypeTests.java | 16 ++++++++ 4 files changed, 72 insertions(+), 11 deletions(-) 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 dcb9c29591..7f6d807fe2 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 a3b25d889e..6077e9738d 100644 --- a/spring-core/src/main/java/org/springframework/core/ResolvableType.java +++ b/spring-core/src/main/java/org/springframework/core/ResolvableType.java @@ -1340,6 +1340,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}. 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 56060b2468..686d581295 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 0d447bd010..ef84035d08 100644 --- a/spring-core/src/test/java/org/springframework/core/ResolvableTypeTests.java +++ b/spring-core/src/test/java/org/springframework/core/ResolvableTypeTests.java @@ -870,6 +870,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("?"));