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