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