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&lt;List&lt;String&gt;&gt; typeRef = new ParameterizedTypeReference&lt;List&lt;String&gt;&gt;() {};
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}