1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 package com.google.common.reflect;
18
19 import static com.google.common.truth.Truth.assertThat;
20
21 import com.google.common.base.Predicate;
22 import com.google.common.base.Supplier;
23
24 import junit.framework.TestCase;
25
26 import java.lang.reflect.GenericArrayType;
27 import java.lang.reflect.ParameterizedType;
28 import java.lang.reflect.Type;
29 import java.lang.reflect.TypeVariable;
30 import java.lang.reflect.WildcardType;
31 import java.util.ArrayList;
32 import java.util.List;
33 import java.util.Map;
34
35
36
37
38
39
40 public class TypeTokenResolutionTest extends TestCase {
41
42 private static class Foo<A, B> {
43
44 Class<? super A> getClassA() {
45 return new TypeToken<A>(getClass()) {}.getRawType();
46 }
47
48 Class<? super B> getClassB() {
49 return new TypeToken<B>(getClass()) {}.getRawType();
50 }
51
52 Class<? super A[]> getArrayClassA() {
53 return new TypeToken<A[]>(getClass()) {}.getRawType();
54 }
55
56 Type getArrayTypeA() {
57 return new TypeToken<A[]>(getClass()) {}.getType();
58 }
59
60 Class<? super B[]> getArrayClassB() {
61 return new TypeToken<B[]>(getClass()) {}.getRawType();
62 }
63 }
64
65 public void testSimpleTypeToken() {
66 Foo<String, Integer> foo = new Foo<String, Integer>() {};
67 assertEquals(String.class, foo.getClassA());
68 assertEquals(Integer.class, foo.getClassB());
69 assertEquals(String[].class, foo.getArrayClassA());
70 assertEquals(Integer[].class, foo.getArrayClassB());
71 }
72
73 public void testCompositeTypeToken() {
74 Foo<String[], List<int[]>> foo = new Foo<String[], List<int[]>>() {};
75 assertEquals(String[].class, foo.getClassA());
76 assertEquals(List.class, foo.getClassB());
77 assertEquals(String[][].class, foo.getArrayClassA());
78 assertEquals(List[].class, foo.getArrayClassB());
79 }
80
81 private static class StringFoo<T> extends Foo<String, T> {}
82
83 public void testPartialSpecialization() {
84 StringFoo<Integer> foo = new StringFoo<Integer>() {};
85 assertEquals(String.class, foo.getClassA());
86 assertEquals(Integer.class, foo.getClassB());
87 assertEquals(String[].class, foo.getArrayClassA());
88 assertEquals(Integer[].class, foo.getArrayClassB());
89 assertEquals(new TypeToken<String[]>() {}.getType(), foo.getArrayTypeA());
90 }
91
92 public void testTypeArgNotFound() {
93 StringFoo<Integer> foo = new StringFoo<Integer>();
94 assertEquals(String.class, foo.getClassA());
95 assertEquals(String[].class, foo.getArrayClassA());
96 assertEquals(Object.class, foo.getClassB());
97 assertEquals(Object[].class, foo.getArrayClassB());
98 }
99
100 private static abstract class Bar<T> {}
101
102 private abstract static class Parameterized<O, T, P> {
103 ParameterizedType parameterizedType() {
104 return new ParameterizedType() {
105 @Override public Type[] getActualTypeArguments() {
106 return new Type[]{new TypeCapture<P>() {}.capture()};
107 }
108 @Override public Type getOwnerType() {
109 return new TypeCapture<O>() {}.capture();
110 }
111 @Override public Type getRawType() {
112 return new TypeCapture<T>() {}.capture();
113 }
114 };
115 }
116 }
117
118 public void testResolveType_parameterizedType() {
119 @SuppressWarnings("rawtypes")
120 Parameterized<?, ?, ?> parameterized =
121 new Parameterized<TypeTokenResolutionTest, Bar, String>() {};
122 TypeResolver typeResolver = TypeResolver.accordingTo(parameterized.getClass());
123 ParameterizedType resolved = (ParameterizedType) typeResolver.resolveType(
124 parameterized.parameterizedType());
125 assertEquals(TypeTokenResolutionTest.class, resolved.getOwnerType());
126 assertEquals(Bar.class, resolved.getRawType());
127 assertThat(resolved.getActualTypeArguments()).asList().has().item(String.class);
128 }
129
130 private interface StringListPredicate extends Predicate<List<String>> {}
131
132 private interface IntegerSupplier extends Supplier<Integer> {}
133
134
135
136 private interface IntegerStringFunction extends IntegerSupplier,
137 Predicate<List<String>>, StringListPredicate {}
138
139 public void testGenericInterface() {
140
141 Type fType = Supplier.class.getTypeParameters()[0];
142 assertEquals(Integer.class,
143 TypeToken.of(IntegerStringFunction.class).resolveType(fType)
144 .getRawType());
145
146
147 Type predicateParameterType = Predicate.class.getTypeParameters()[0];
148 assertEquals(new TypeToken<List<String>>() {}.getType(),
149 TypeToken.of(IntegerStringFunction.class).resolveType(predicateParameterType)
150 .getType());
151 }
152
153 private static abstract class StringIntegerFoo extends Foo<String, Integer> {}
154
155 public void testConstructor_typeArgsResolvedFromAncestorClass() {
156 assertEquals(String.class, new StringIntegerFoo() {}.getClassA());
157 assertEquals(Integer.class, new StringIntegerFoo() {}.getClassB());
158 }
159
160 private static class Owner<T> {
161 private static abstract class Nested<X> {
162 Class<? super X> getTypeArgument() {
163 return new TypeToken<X>(getClass()) {}.getRawType();
164 }
165 }
166
167 private abstract class Inner<Y> extends Nested<Y> {
168 Class<? super T> getOwnerType() {
169 return new TypeToken<T>(getClass()) {}.getRawType();
170 }
171 }
172 }
173
174 public void testResolveNestedClass() {
175 assertEquals(String.class, new Owner.Nested<String>() {}.getTypeArgument());
176 }
177
178 public void testResolveInnerClass() {
179 assertEquals(String.class,
180 new Owner<Integer>().new Inner<String>() {}.getTypeArgument());
181 }
182
183 public void testResolveOwnerClass() {
184 assertEquals(Integer.class,
185 new Owner<Integer>().new Inner<String>() {}.getOwnerType());
186 }
187
188 private static class Mapping<F, T> {
189
190 final Type f = new TypeToken<F>(getClass()) {}.getType();
191 final Type t = new TypeToken<T>(getClass()) {}.getType();
192
193 Type getFromType() {
194 return new TypeToken<F>(getClass()) {}.getType();
195 }
196
197 Type getToType() {
198 return new TypeToken<T>(getClass()) {}.getType();
199 }
200
201 Mapping<T, F> flip() {
202 return new Mapping<T, F>() {};
203 }
204
205 Mapping<F, T> selfMapping() {
206 return new Mapping<F, T>() {};
207 }
208 }
209
210 public void testCyclicMapping() {
211 Mapping<Integer, String> mapping = new Mapping<Integer, String>();
212 assertEquals(mapping.f, mapping.getFromType());
213 assertEquals(mapping.t, mapping.getToType());
214 assertEquals(mapping.f, mapping.flip().getFromType());
215 assertEquals(mapping.t, mapping.flip().getToType());
216 assertEquals(mapping.f, mapping.selfMapping().getFromType());
217 assertEquals(mapping.t, mapping.selfMapping().getToType());
218 }
219
220 private static class ParameterizedOuter<T> {
221
222 @SuppressWarnings("unused")
223 public Inner field;
224
225 class Inner {}
226 }
227
228 public void testInnerClassWithParameterizedOwner() throws Exception {
229 Type fieldType = ParameterizedOuter.class.getField("field")
230 .getGenericType();
231 assertEquals(fieldType,
232 TypeToken.of(ParameterizedOuter.class).resolveType(fieldType).getType());
233 }
234
235 private interface StringIterable extends Iterable<String> {}
236
237 public void testResolveType() {
238 assertEquals(String.class, TypeToken.of(this.getClass()).resolveType(String.class).getType());
239 assertEquals(String.class,
240 TypeToken.of(StringIterable.class)
241 .resolveType(Iterable.class.getTypeParameters()[0]).getType());
242 assertEquals(String.class,
243 TypeToken.of(StringIterable.class)
244 .resolveType(Iterable.class.getTypeParameters()[0]).getType());
245 try {
246 TypeToken.of(this.getClass()).resolveType(null);
247 fail();
248 } catch (NullPointerException expected) {}
249 }
250
251 public void testConextIsParameterizedType() throws Exception {
252 class Context {
253 @SuppressWarnings("unused")
254 Map<String, Integer> returningMap() {
255 throw new AssertionError();
256 }
257 }
258 Type context = Context.class.getDeclaredMethod("returningMap")
259 .getGenericReturnType();
260 Type keyType = Map.class.getTypeParameters()[0];
261 Type valueType = Map.class.getTypeParameters()[1];
262
263
264 assertEquals(String.class, TypeToken.of(context).resolveType(keyType).getType());
265 assertEquals(Integer.class,
266 TypeToken.of(context).resolveType(valueType).getType());
267
268
269 assertEquals(keyType, TypeToken.of(keyType).resolveType(keyType).getType());
270 assertEquals(valueType, TypeToken.of(valueType).resolveType(valueType).getType());
271 }
272
273 private static final class GenericArray<T> {
274 final Type t = new TypeToken<T>(getClass()) {}.getType();
275 final Type array = new TypeToken<T[]>(getClass()) {}.getType();
276 }
277
278 public void testGenericArrayType() {
279 GenericArray<?> genericArray = new GenericArray<Integer>();
280 assertEquals(GenericArray.class.getTypeParameters()[0], genericArray.t);
281 assertEquals(Types.newArrayType(genericArray.t),
282 genericArray.array);
283 }
284
285 public void testClassWrapper() {
286 TypeToken<String> typeExpression = TypeToken.of(String.class);
287 assertEquals(String.class, typeExpression.getType());
288 assertEquals(String.class, typeExpression.getRawType());
289 }
290
291 private static class Red<A> {
292 private class Orange {
293 Class<?> getClassA() {
294 return new TypeToken<A>(getClass()) {}.getRawType();
295 }
296
297 Red<A> getSelfB() {
298 return Red.this;
299 }
300 }
301
302 Red<A> getSelfA() {
303 return this;
304 }
305
306 private class Yellow<B> extends Red<B>.Orange {
307 Yellow(Red<B> red) {
308 red.super();
309 }
310
311 Class<?> getClassB() {
312 return new TypeToken<B>(getClass()) {}.getRawType();
313 }
314
315 Red<A> getA() {
316 return getSelfA();
317 }
318
319 Red<B> getB() {
320 return getSelfB();
321 }
322 }
323
324 Class<?> getClassDirect() {
325 return new TypeToken<A>(getClass()) {}.getRawType();
326 }
327 }
328
329 public void test1() {
330 Red<String> redString = new Red<String>() {};
331 Red<Integer> redInteger = new Red<Integer>() {};
332 assertEquals(String.class, redString.getClassDirect());
333 assertEquals(Integer.class, redInteger.getClassDirect());
334
335 Red<String>.Yellow<Integer> yellowInteger =
336 redString.new Yellow<Integer>(redInteger) {};
337 assertEquals(Integer.class, yellowInteger.getClassA());
338 assertEquals(Integer.class, yellowInteger.getClassB());
339 assertEquals(String.class, yellowInteger.getA().getClassDirect());
340 assertEquals(Integer.class, yellowInteger.getB().getClassDirect());
341 }
342
343 public void test2() {
344 Red<String> redString = new Red<String>();
345 Red<Integer> redInteger = new Red<Integer>();
346 Red<String>.Yellow<Integer> yellowInteger =
347 redString.new Yellow<Integer>(redInteger) {};
348 assertEquals(Integer.class, yellowInteger.getClassA());
349 assertEquals(Integer.class, yellowInteger.getClassB());
350 }
351
352 private static <T> Type staticMethodWithLocalClass() {
353 class MyLocalClass {
354 Type getType() {
355 return new TypeToken<T>(getClass()) {}.getType();
356 }
357 }
358 return new MyLocalClass().getType();
359 }
360
361 public void testLocalClassInsideStaticMethod() {
362 assertNotNull(staticMethodWithLocalClass());
363 }
364
365 public void testLocalClassInsideNonStaticMethod() {
366 class MyLocalClass<T> {
367 Type getType() {
368 return new TypeToken<T>(getClass()) {}.getType();
369 }
370 }
371 assertNotNull(new MyLocalClass<String>().getType());
372 }
373
374 private static <T> Type staticMethodWithAnonymousClass() {
375 return new Object() {
376 Type getType() {
377 return new TypeToken<T>(getClass()) {}.getType();
378 }
379 }.getType();
380 }
381
382 public void testAnonymousClassInsideStaticMethod() {
383 assertNotNull(staticMethodWithAnonymousClass());
384 }
385
386 public void testAnonymousClassInsideNonStaticMethod() {
387 assertNotNull(new Object() {
388 Type getType() {
389 return new TypeToken<Object>() {}.getType();
390 }
391 }.getType());
392 }
393
394 public void testStaticContext() {
395 assertEquals(Map.class, mapType().getRawType());
396 }
397
398 private abstract static class Holder<T> {
399 Type getContentType() {
400 return new TypeToken<T>(getClass()) {}.getType();
401 }
402 }
403
404 public void testResolvePrimitiveArrayType() {
405 assertEquals(new TypeToken<int[]>() {}.getType(),
406 new Holder<int[]>() {}.getContentType());
407 assertEquals(new TypeToken<int[][]> () {}.getType(),
408 new Holder<int[][]>() {}.getContentType());
409 }
410
411 public void testResolveToGenericArrayType() {
412 GenericArrayType arrayType = (GenericArrayType)
413 new Holder<List<int[][]>[]>() {}.getContentType();
414 ParameterizedType listType = (ParameterizedType)
415 arrayType.getGenericComponentType();
416 assertEquals(List.class, listType.getRawType());
417 assertEquals(Types.newArrayType(int[].class),
418 listType.getActualTypeArguments()[0]);
419 }
420
421 private abstract class WithGenericBound<A> {
422
423 @SuppressWarnings("unused")
424 public <B extends A> void withTypeVariable(List<B> list) {}
425
426 @SuppressWarnings("unused")
427 public <E extends Enum<E>> void withRecursiveBound(List<E> list) {}
428
429 @SuppressWarnings("unused")
430 public <K extends List<V>, V extends List<K>> void withMutualRecursiveBound(
431 List<Map<K, V>> list) {}
432
433 @SuppressWarnings("unused")
434 void withWildcardLowerBound(List<? super A> list) {}
435
436 @SuppressWarnings("unused")
437 void withWildcardUpperBound(List<? extends A> list) {}
438
439 Type getTargetType(String methodName) throws Exception {
440 ParameterizedType parameterType = (ParameterizedType)
441 WithGenericBound.class.getDeclaredMethod(methodName, List.class)
442 .getGenericParameterTypes()[0];
443 parameterType = (ParameterizedType)
444 TypeToken.of(this.getClass()).resolveType(parameterType).getType();
445 return parameterType.getActualTypeArguments()[0];
446 }
447 }
448
449 public void testWithGenericBoundInTypeVariable() throws Exception {
450 TypeVariable<?> typeVariable = (TypeVariable<?>)
451 new WithGenericBound<String>() {}.getTargetType("withTypeVariable");
452 assertEquals(String.class, typeVariable.getBounds()[0]);
453 }
454
455 public void testWithRecursiveBoundInTypeVariable() throws Exception {
456 TypeVariable<?> typeVariable = (TypeVariable<?>)
457 new WithGenericBound<String>() {}.getTargetType("withRecursiveBound");
458 assertEquals(Types.newParameterizedType(Enum.class, typeVariable),
459 typeVariable.getBounds()[0]);
460 }
461
462 public void testWithMutualRecursiveBoundInTypeVariable() throws Exception {
463 ParameterizedType paramType = (ParameterizedType)
464 new WithGenericBound<String>() {}
465 .getTargetType("withMutualRecursiveBound");
466 TypeVariable<?> k = (TypeVariable<?>) paramType.getActualTypeArguments()[0];
467 TypeVariable<?> v = (TypeVariable<?>) paramType.getActualTypeArguments()[1];
468 assertEquals(Types.newParameterizedType(List.class, v), k.getBounds()[0]);
469 assertEquals(Types.newParameterizedType(List.class, k), v.getBounds()[0]);
470 }
471
472 public void testWithGenericLowerBoundInWildcard() throws Exception {
473 WildcardType wildcardType = (WildcardType)
474 new WithGenericBound<String>() {}
475 .getTargetType("withWildcardLowerBound");
476 assertEquals(String.class, wildcardType.getLowerBounds()[0]);
477 }
478
479 public void testWithGenericUpperBoundInWildcard() throws Exception {
480 WildcardType wildcardType = (WildcardType)
481 new WithGenericBound<String>() {}
482 .getTargetType("withWildcardUpperBound");
483 assertEquals(String.class, wildcardType.getUpperBounds()[0]);
484 }
485
486 public void testInterfaceTypeParameterResolution() throws Exception {
487 assertEquals(String.class,
488 TypeToken.of(new TypeToken<ArrayList<String>>() {}.getType())
489 .resolveType(List.class.getTypeParameters()[0]).getType());
490 }
491
492 private static TypeToken<Map<Object, Object>> mapType() {
493 return new TypeToken<Map<Object, Object>>() {};
494 }
495
496
497 private interface WithFalseRecursiveType<K, V> {
498 WithFalseRecursiveType<List<V>, String> keyShouldNotResolveToStringList();
499 WithFalseRecursiveType<List<K>, List<V>> shouldNotCauseInfiniteLoop();
500 SubTypeOfWithFalseRecursiveType<List<V>, List<K>> evenSubTypeWorks();
501 }
502
503 private interface SubTypeOfWithFalseRecursiveType<K1, V1>
504 extends WithFalseRecursiveType<List<K1>, List<V1>> {
505 SubTypeOfWithFalseRecursiveType<V1, K1> revertKeyAndValueTypes();
506 }
507
508 public void testFalseRecursiveType_mappingOnTheSameDeclarationNotUsed() {
509 Type returnType = genericReturnType(
510 WithFalseRecursiveType.class, "keyShouldNotResolveToStringList");
511 TypeToken<?> keyType = TypeToken.of(returnType)
512 .resolveType(WithFalseRecursiveType.class.getTypeParameters()[0]);
513 assertEquals("java.util.List<V>", keyType.getType().toString());
514 }
515
516 public void testFalseRecursiveType_notRealRecursiveMapping() {
517 Type returnType = genericReturnType(
518 WithFalseRecursiveType.class, "shouldNotCauseInfiniteLoop");
519 TypeToken<?> keyType = TypeToken.of(returnType)
520 .resolveType(WithFalseRecursiveType.class.getTypeParameters()[0]);
521 assertEquals("java.util.List<K>", keyType.getType().toString());
522 }
523
524 public void testFalseRecursiveType_referenceOfSubtypeDoesNotConfuseMe() {
525 Type returnType = genericReturnType(
526 WithFalseRecursiveType.class, "evenSubTypeWorks");
527 TypeToken<?> keyType = TypeToken.of(returnType)
528 .resolveType(WithFalseRecursiveType.class.getTypeParameters()[0]);
529 assertEquals("java.util.List<java.util.List<V>>", keyType.getType().toString());
530 }
531
532 public void testFalseRecursiveType_intermediaryTypeMappingDoesNotConfuseMe() {
533 Type returnType = genericReturnType(
534 SubTypeOfWithFalseRecursiveType.class, "revertKeyAndValueTypes");
535 TypeToken<?> keyType = TypeToken.of(returnType)
536 .resolveType(WithFalseRecursiveType.class.getTypeParameters()[0]);
537 assertEquals("java.util.List<K1>", keyType.getType().toString());
538 }
539
540 private static Type genericReturnType(Class<?> cls, String methodName) {
541 try {
542 return cls.getMethod(methodName).getGenericReturnType();
543 } catch (Exception e) {
544 throw new RuntimeException(e);
545 }
546 }
547
548 public void testTwoStageResolution() {
549 class ForTwoStageResolution<A extends Number> {
550 <B extends A> void verifyTwoStageResolution() {
551 @SuppressWarnings({"unchecked", "rawtypes"})
552 Type type = new TypeToken<B>(getClass()) {}
553
554
555 .where(new TypeParameter<B>() {}, (Class) Integer.class)
556 .getType();
557 assertEquals(Integer.class, type);
558 }
559 }
560 new ForTwoStageResolution<Integer>().verifyTwoStageResolution();
561 new ForTwoStageResolution<Integer>() {}.verifyTwoStageResolution();
562 }
563 }