001/* 002 * Copyright 2002-2018 the original author or authors. 003 * 004 * Licensed under the Apache License, Version 2.0 (the "License"); 005 * you may not use this file except in compliance with the License. 006 * You may obtain a copy of the License at 007 * 008 * https://www.apache.org/licenses/LICENSE-2.0 009 * 010 * Unless required by applicable law or agreed to in writing, software 011 * distributed under the License is distributed on an "AS IS" BASIS, 012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 013 * See the License for the specific language governing permissions and 014 * limitations under the License. 015 */ 016 017package org.springframework.core; 018 019import java.lang.reflect.ParameterizedType; 020import java.lang.reflect.Type; 021 022import org.springframework.util.Assert; 023 024/** 025 * The purpose of this class is to enable capturing and passing a generic 026 * {@link Type}. In order to capture the generic type and retain it at runtime, 027 * you need to create a subclass (ideally as anonymous inline class) as follows: 028 * 029 * <pre class="code"> 030 * ParameterizedTypeReference<List<String>> typeRef = new ParameterizedTypeReference<List<String>>() {}; 031 * </pre> 032 * 033 * <p>The resulting {@code typeRef} instance can then be used to obtain a {@link Type} 034 * instance that carries the captured parameterized type information at runtime. 035 * For more information on "super type tokens" see the link to Neal Gafter's blog post. 036 * 037 * @author Arjen Poutsma 038 * @author Rossen Stoyanchev 039 * @since 3.2 040 * @see <a href="https://gafter.blogspot.nl/2006/12/super-type-tokens.html">Neal Gafter on Super Type Tokens</a> 041 */ 042public abstract class ParameterizedTypeReference<T> { 043 044 private final Type type; 045 046 047 protected ParameterizedTypeReference() { 048 Class<?> parameterizedTypeReferenceSubclass = findParameterizedTypeReferenceSubclass(getClass()); 049 Type type = parameterizedTypeReferenceSubclass.getGenericSuperclass(); 050 Assert.isInstanceOf(ParameterizedType.class, type, "Type must be a parameterized type"); 051 ParameterizedType parameterizedType = (ParameterizedType) type; 052 Type[] actualTypeArguments = parameterizedType.getActualTypeArguments(); 053 Assert.isTrue(actualTypeArguments.length == 1, "Number of type arguments must be 1"); 054 this.type = actualTypeArguments[0]; 055 } 056 057 private ParameterizedTypeReference(Type type) { 058 this.type = type; 059 } 060 061 062 public Type getType() { 063 return this.type; 064 } 065 066 @Override 067 public boolean equals(Object obj) { 068 return (this == obj || (obj instanceof ParameterizedTypeReference && 069 this.type.equals(((ParameterizedTypeReference<?>) obj).type))); 070 } 071 072 @Override 073 public int hashCode() { 074 return this.type.hashCode(); 075 } 076 077 @Override 078 public String toString() { 079 return "ParameterizedTypeReference<" + this.type + ">"; 080 } 081 082 083 /** 084 * Build a {@code ParameterizedTypeReference} wrapping the given type. 085 * @param type a generic type (possibly obtained via reflection, 086 * e.g. from {@link java.lang.reflect.Method#getGenericReturnType()}) 087 * @return a corresponding reference which may be passed into 088 * {@code ParameterizedTypeReference}-accepting methods 089 * @since 4.3.12 090 */ 091 public static <T> ParameterizedTypeReference<T> forType(Type type) { 092 return new ParameterizedTypeReference<T>(type) { 093 }; 094 } 095 096 private static Class<?> findParameterizedTypeReferenceSubclass(Class<?> child) { 097 Class<?> parent = child.getSuperclass(); 098 if (Object.class == parent) { 099 throw new IllegalStateException("Expected ParameterizedTypeReference superclass"); 100 } 101 else if (ParameterizedTypeReference.class == parent) { 102 return child; 103 } 104 else { 105 return findParameterizedTypeReferenceSubclass(parent); 106 } 107 } 108 109}