• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2006 Google Inc.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package com.google.inject;
18 
19 import static com.google.common.base.Preconditions.checkArgument;
20 import static com.google.common.base.Preconditions.checkNotNull;
21 import static com.google.inject.internal.MoreTypes.canonicalize;
22 
23 import com.google.common.collect.ImmutableList;
24 import com.google.inject.internal.MoreTypes;
25 import com.google.inject.util.Types;
26 import java.lang.reflect.Constructor;
27 import java.lang.reflect.Field;
28 import java.lang.reflect.GenericArrayType;
29 import java.lang.reflect.Member;
30 import java.lang.reflect.Method;
31 import java.lang.reflect.ParameterizedType;
32 import java.lang.reflect.Type;
33 import java.lang.reflect.TypeVariable;
34 import java.lang.reflect.WildcardType;
35 import java.util.List;
36 
37 /**
38  * Represents a generic type {@code T}. Java doesn't yet provide a way to represent generic types,
39  * so this class does. Forces clients to create a subclass of this class which enables retrieval of
40  * the type information even at runtime.
41  *
42  * <p>For example, to create a type literal for {@code List<String>}, you can create an empty
43  * anonymous inner class:
44  *
45  * <p>{@code TypeLiteral<List<String>> list = new TypeLiteral<List<String>>() {};}
46  *
47  * <p>Along with modeling generic types, this class can resolve type parameters. For example, to
48  * figure out what type {@code keySet()} returns on a {@code Map<Integer, String>}, use this code:
49  *
50  * <pre>{@code
51  * TypeLiteral<Map<Integer, String>> mapType
52  *     = new TypeLiteral<Map<Integer, String>>() {};
53  * TypeLiteral<?> keySetType
54  *     = mapType.getReturnType(Map.class.getMethod("keySet"));
55  * System.out.println(keySetType); // prints "Set<Integer>"
56  * }</pre>
57  *
58  * @author crazybob@google.com (Bob Lee)
59  * @author jessewilson@google.com (Jesse Wilson)
60  */
61 public class TypeLiteral<T> {
62 
63   final Class<? super T> rawType;
64   final Type type;
65   final int hashCode;
66 
67   /**
68    * Constructs a new type literal. Derives represented class from type parameter.
69    *
70    * <p>Clients create an empty anonymous subclass. Doing so embeds the type parameter in the
71    * anonymous class's type hierarchy so we can reconstitute it at runtime despite erasure.
72    */
73   @SuppressWarnings("unchecked")
TypeLiteral()74   protected TypeLiteral() {
75     this.type = getSuperclassTypeParameter(getClass());
76     this.rawType = (Class<? super T>) MoreTypes.getRawType(type);
77     this.hashCode = type.hashCode();
78   }
79 
80   /** Unsafe. Constructs a type literal manually. */
81   @SuppressWarnings("unchecked")
TypeLiteral(Type type)82   TypeLiteral(Type type) {
83     this.type = canonicalize(checkNotNull(type, "type"));
84     this.rawType = (Class<? super T>) MoreTypes.getRawType(this.type);
85     this.hashCode = this.type.hashCode();
86   }
87 
88   /**
89    * Returns the type from super class's type parameter in {@link MoreTypes#canonicalize(Type)
90    * canonical form}.
91    */
getSuperclassTypeParameter(Class<?> subclass)92   static Type getSuperclassTypeParameter(Class<?> subclass) {
93     Type superclass = subclass.getGenericSuperclass();
94     if (superclass instanceof Class) {
95       throw new RuntimeException("Missing type parameter.");
96     }
97     ParameterizedType parameterized = (ParameterizedType) superclass;
98     return canonicalize(parameterized.getActualTypeArguments()[0]);
99   }
100 
101   /** Gets type literal from super class's type parameter. */
fromSuperclassTypeParameter(Class<?> subclass)102   static TypeLiteral<?> fromSuperclassTypeParameter(Class<?> subclass) {
103     return new TypeLiteral<Object>(getSuperclassTypeParameter(subclass));
104   }
105 
106   /**
107    * Returns the raw (non-generic) type for this type.
108    *
109    * @since 2.0
110    */
getRawType()111   public final Class<? super T> getRawType() {
112     return rawType;
113   }
114 
115   /** Gets underlying {@code Type} instance. */
getType()116   public final Type getType() {
117     return type;
118   }
119 
120   /** Gets the type of this type's provider. */
121   @SuppressWarnings("unchecked")
providerType()122   final TypeLiteral<Provider<T>> providerType() {
123     // This cast is safe and wouldn't generate a warning if Type had a type
124     // parameter.
125     return (TypeLiteral<Provider<T>>) get(Types.providerOf(getType()));
126   }
127 
128   @Override
hashCode()129   public final int hashCode() {
130     return this.hashCode;
131   }
132 
133   @Override
equals(Object o)134   public final boolean equals(Object o) {
135     return o instanceof TypeLiteral<?> && MoreTypes.equals(type, ((TypeLiteral) o).type);
136   }
137 
138   @Override
toString()139   public final String toString() {
140     return MoreTypes.typeToString(type);
141   }
142 
143   /** Gets type literal for the given {@code Type} instance. */
get(Type type)144   public static TypeLiteral<?> get(Type type) {
145     return new TypeLiteral<Object>(type);
146   }
147 
148   /** Gets type literal for the given {@code Class} instance. */
get(Class<T> type)149   public static <T> TypeLiteral<T> get(Class<T> type) {
150     return new TypeLiteral<T>(type);
151   }
152 
153   /** Returns an immutable list of the resolved types. */
resolveAll(Type[] types)154   private List<TypeLiteral<?>> resolveAll(Type[] types) {
155     TypeLiteral<?>[] result = new TypeLiteral<?>[types.length];
156     for (int t = 0; t < types.length; t++) {
157       result[t] = resolve(types[t]);
158     }
159     return ImmutableList.copyOf(result);
160   }
161 
162   /** Resolves known type parameters in {@code toResolve} and returns the result. */
resolve(Type toResolve)163   TypeLiteral<?> resolve(Type toResolve) {
164     return TypeLiteral.get(resolveType(toResolve));
165   }
166 
resolveType(Type toResolve)167   Type resolveType(Type toResolve) {
168     // this implementation is made a little more complicated in an attempt to avoid object-creation
169     while (true) {
170       if (toResolve instanceof TypeVariable) {
171         TypeVariable original = (TypeVariable) toResolve;
172         toResolve = MoreTypes.resolveTypeVariable(type, rawType, original);
173         if (toResolve == original) {
174           return toResolve;
175         }
176 
177       } else if (toResolve instanceof GenericArrayType) {
178         GenericArrayType original = (GenericArrayType) toResolve;
179         Type componentType = original.getGenericComponentType();
180         Type newComponentType = resolveType(componentType);
181         return componentType == newComponentType ? original : Types.arrayOf(newComponentType);
182 
183       } else if (toResolve instanceof ParameterizedType) {
184         ParameterizedType original = (ParameterizedType) toResolve;
185         Type ownerType = original.getOwnerType();
186         Type newOwnerType = resolveType(ownerType);
187         boolean changed = newOwnerType != ownerType;
188 
189         Type[] args = original.getActualTypeArguments();
190         for (int t = 0, length = args.length; t < length; t++) {
191           Type resolvedTypeArgument = resolveType(args[t]);
192           if (resolvedTypeArgument != args[t]) {
193             if (!changed) {
194               args = args.clone();
195               changed = true;
196             }
197             args[t] = resolvedTypeArgument;
198           }
199         }
200 
201         return changed
202             ? Types.newParameterizedTypeWithOwner(newOwnerType, original.getRawType(), args)
203             : original;
204 
205       } else if (toResolve instanceof WildcardType) {
206         WildcardType original = (WildcardType) toResolve;
207         Type[] originalLowerBound = original.getLowerBounds();
208         Type[] originalUpperBound = original.getUpperBounds();
209 
210         if (originalLowerBound.length == 1) {
211           Type lowerBound = resolveType(originalLowerBound[0]);
212           if (lowerBound != originalLowerBound[0]) {
213             return Types.supertypeOf(lowerBound);
214           }
215         } else if (originalUpperBound.length == 1) {
216           Type upperBound = resolveType(originalUpperBound[0]);
217           if (upperBound != originalUpperBound[0]) {
218             return Types.subtypeOf(upperBound);
219           }
220         }
221         return original;
222 
223       } else {
224         return toResolve;
225       }
226     }
227   }
228 
229   /**
230    * Returns the generic form of {@code supertype}. For example, if this is {@code
231    * ArrayList<String>}, this returns {@code Iterable<String>} given the input {@code
232    * Iterable.class}.
233    *
234    * @param supertype a superclass of, or interface implemented by, this.
235    * @since 2.0
236    */
getSupertype(Class<?> supertype)237   public TypeLiteral<?> getSupertype(Class<?> supertype) {
238     checkArgument(
239         supertype.isAssignableFrom(rawType), "%s is not a supertype of %s", supertype, this.type);
240     return resolve(MoreTypes.getGenericSupertype(type, rawType, supertype));
241   }
242 
243   /**
244    * Returns the resolved generic type of {@code field}.
245    *
246    * @param field a field defined by this or any superclass.
247    * @since 2.0
248    */
getFieldType(Field field)249   public TypeLiteral<?> getFieldType(Field field) {
250     checkArgument(
251         field.getDeclaringClass().isAssignableFrom(rawType),
252         "%s is not defined by a supertype of %s",
253         field,
254         type);
255     return resolve(field.getGenericType());
256   }
257 
258   /**
259    * Returns the resolved generic parameter types of {@code methodOrConstructor}.
260    *
261    * @param methodOrConstructor a method or constructor defined by this or any supertype.
262    * @since 2.0
263    */
getParameterTypes(Member methodOrConstructor)264   public List<TypeLiteral<?>> getParameterTypes(Member methodOrConstructor) {
265     Type[] genericParameterTypes;
266 
267     if (methodOrConstructor instanceof Method) {
268       Method method = (Method) methodOrConstructor;
269       checkArgument(
270           method.getDeclaringClass().isAssignableFrom(rawType),
271           "%s is not defined by a supertype of %s",
272           method,
273           type);
274       genericParameterTypes = method.getGenericParameterTypes();
275 
276     } else if (methodOrConstructor instanceof Constructor) {
277       Constructor<?> constructor = (Constructor<?>) methodOrConstructor;
278       checkArgument(
279           constructor.getDeclaringClass().isAssignableFrom(rawType),
280           "%s does not construct a supertype of %s",
281           constructor,
282           type);
283       genericParameterTypes = constructor.getGenericParameterTypes();
284 
285     } else {
286       throw new IllegalArgumentException("Not a method or a constructor: " + methodOrConstructor);
287     }
288 
289     return resolveAll(genericParameterTypes);
290   }
291 
292   /**
293    * Returns the resolved generic exception types thrown by {@code constructor}.
294    *
295    * @param methodOrConstructor a method or constructor defined by this or any supertype.
296    * @since 2.0
297    */
getExceptionTypes(Member methodOrConstructor)298   public List<TypeLiteral<?>> getExceptionTypes(Member methodOrConstructor) {
299     Type[] genericExceptionTypes;
300 
301     if (methodOrConstructor instanceof Method) {
302       Method method = (Method) methodOrConstructor;
303       checkArgument(
304           method.getDeclaringClass().isAssignableFrom(rawType),
305           "%s is not defined by a supertype of %s",
306           method,
307           type);
308       genericExceptionTypes = method.getGenericExceptionTypes();
309 
310     } else if (methodOrConstructor instanceof Constructor) {
311       Constructor<?> constructor = (Constructor<?>) methodOrConstructor;
312       checkArgument(
313           constructor.getDeclaringClass().isAssignableFrom(rawType),
314           "%s does not construct a supertype of %s",
315           constructor,
316           type);
317       genericExceptionTypes = constructor.getGenericExceptionTypes();
318 
319     } else {
320       throw new IllegalArgumentException("Not a method or a constructor: " + methodOrConstructor);
321     }
322 
323     return resolveAll(genericExceptionTypes);
324   }
325 
326   /**
327    * Returns the resolved generic return type of {@code method}.
328    *
329    * @param method a method defined by this or any supertype.
330    * @since 2.0
331    */
getReturnType(Method method)332   public TypeLiteral<?> getReturnType(Method method) {
333     checkArgument(
334         method.getDeclaringClass().isAssignableFrom(rawType),
335         "%s is not defined by a supertype of %s",
336         method,
337         type);
338     return resolve(method.getGenericReturnType());
339   }
340 }
341