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 017 018package org.springframework.core; 019 020import java.lang.reflect.ParameterizedType; 021import java.lang.reflect.Type; 022 023import org.springframework.lang.Nullable; 024import org.springframework.util.Assert; 025 026/** 027 * The purpose of this class is to enable capturing and passing a generic 028 * {@link Type}. In order to capture the generic type and retain it at runtime, 029 * you need to create a subclass (ideally as anonymous inline class) as follows: 030 * 031 * <pre class="code"> 032 * ParameterizedTypeReference<List<String>> typeRef = new ParameterizedTypeReference<List<String>>() {}; 033 * </pre> 034 * 035 * <p>The resulting {@code typeRef} instance can then be used to obtain a {@link Type} 036 * instance that carries the captured parameterized type information at runtime. 037 * For more information on "super type tokens" see the link to Neal Gafter's blog post. 038 * 039 * @author Arjen Poutsma 040 * @author Rossen Stoyanchev 041 * @since 3.2 042 * @param <T> the referenced type 043 * @see <a href="https://gafter.blogspot.nl/2006/12/super-type-tokens.html">Neal Gafter on Super Type Tokens</a> 044 */ 045public abstract class ParameterizedTypeReference<T> { 046 047 private final Type type; 048 049 050 protected ParameterizedTypeReference() { 051 Class<?> parameterizedTypeReferenceSubclass = findParameterizedTypeReferenceSubclass(getClass()); 052 Type type = parameterizedTypeReferenceSubclass.getGenericSuperclass(); 053 Assert.isInstanceOf(ParameterizedType.class, type, "Type must be a parameterized type"); 054 ParameterizedType parameterizedType = (ParameterizedType) type; 055 Type[] actualTypeArguments = parameterizedType.getActualTypeArguments(); 056 Assert.isTrue(actualTypeArguments.length == 1, "Number of type arguments must be 1"); 057 this.type = actualTypeArguments[0]; 058 } 059 060 private ParameterizedTypeReference(Type type) { 061 this.type = type; 062 } 063 064 065 public Type getType() { 066 return this.type; 067 } 068 069 @Override 070 public boolean equals(@Nullable Object other) { 071 return (this == other || (other instanceof ParameterizedTypeReference && 072 this.type.equals(((ParameterizedTypeReference<?>) other).type))); 073 } 074 075 @Override 076 public int hashCode() { 077 return this.type.hashCode(); 078 } 079 080 @Override 081 public String toString() { 082 return "ParameterizedTypeReference<" + this.type + ">"; 083 } 084 085 086 /** 087 * Build a {@code ParameterizedTypeReference} wrapping the given type. 088 * @param type a generic type (possibly obtained via reflection, 089 * e.g. from {@link java.lang.reflect.Method#getGenericReturnType()}) 090 * @return a corresponding reference which may be passed into 091 * {@code ParameterizedTypeReference}-accepting methods 092 * @since 4.3.12 093 */ 094 public static <T> ParameterizedTypeReference<T> forType(Type type) { 095 return new ParameterizedTypeReference<T>(type) { 096 }; 097 } 098 099 private static Class<?> findParameterizedTypeReferenceSubclass(Class<?> child) { 100 Class<?> parent = child.getSuperclass(); 101 if (Object.class == parent) { 102 throw new IllegalStateException("Expected ParameterizedTypeReference superclass"); 103 } 104 else if (ParameterizedTypeReference.class == parent) { 105 return child; 106 } 107 else { 108 return findParameterizedTypeReferenceSubclass(parent); 109 } 110 } 111 112}