• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2011 The Guava Authors
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.common.reflect;
18 
19 import static com.google.common.truth.Truth.assertThat;
20 import static java.util.Arrays.asList;
21 
22 import com.google.common.collect.Lists;
23 import com.google.common.testing.EqualsTester;
24 import com.google.common.testing.NullPointerTester;
25 import com.google.common.testing.NullPointerTester.Visibility;
26 import com.google.common.testing.SerializableTester;
27 
28 import junit.framework.TestCase;
29 
30 import java.lang.reflect.GenericArrayType;
31 import java.lang.reflect.GenericDeclaration;
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.Arrays;
37 import java.util.HashMap;
38 import java.util.List;
39 import java.util.Map;
40 
41 /**
42  * Tests for {@link Types}.
43  *
44  * @author Ben Yu
45  */
46 public class TypesTest extends TestCase {
47 
testNewParameterizedType_ownerTypeImplied()48   public void testNewParameterizedType_ownerTypeImplied() throws Exception {
49     ParameterizedType jvmType = (ParameterizedType)
50         new TypeCapture<Map.Entry<String, Integer>>() {}.capture();
51     ParameterizedType ourType = Types.newParameterizedType(
52         Map.Entry.class, String.class, Integer.class);
53     assertEquals(jvmType, ourType);
54     assertEquals(Map.class, ourType.getOwnerType());
55   }
56 
testNewParameterizedType()57   public void testNewParameterizedType() {
58     ParameterizedType jvmType = (ParameterizedType)
59         new TypeCapture<HashMap<String, int[][]>>() {}.capture();
60     ParameterizedType ourType = Types.newParameterizedType(
61         HashMap.class, String.class, int[][].class);
62 
63     new EqualsTester()
64         .addEqualityGroup(jvmType, ourType)
65         .testEquals();
66     assertEquals(jvmType.toString(), ourType.toString());
67     assertEquals(jvmType.hashCode(), ourType.hashCode());
68     assertEquals(HashMap.class, ourType.getRawType());
69     assertThat(ourType.getActualTypeArguments()).asList()
70         .has().exactlyAs(asList(jvmType.getActualTypeArguments())).inOrder();
71     assertEquals(Arrays.asList(
72             String.class,
73             Types.newArrayType(Types.newArrayType(int.class))),
74         Arrays.asList(ourType.getActualTypeArguments()));
75     assertEquals(null, ourType.getOwnerType());
76   }
77 
testNewParameterizedType_nonStaticLocalClass()78   public void testNewParameterizedType_nonStaticLocalClass() {
79     class LocalClass<T> {}
80     Type jvmType = new LocalClass<String>() {}.getClass().getGenericSuperclass();
81     Type ourType = Types.newParameterizedType(LocalClass.class, String.class);
82     assertEquals(jvmType, ourType);
83   }
84 
testNewParameterizedType_staticLocalClass()85   public void testNewParameterizedType_staticLocalClass() {
86     doTestNewParameterizedType_staticLocalClass();
87   }
88 
doTestNewParameterizedType_staticLocalClass()89   private static void doTestNewParameterizedType_staticLocalClass() {
90     class LocalClass<T> {}
91     Type jvmType = new LocalClass<String>() {}.getClass().getGenericSuperclass();
92     Type ourType = Types.newParameterizedType(LocalClass.class, String.class);
93     assertEquals(jvmType, ourType);
94   }
95 
testNewParameterizedTypeWithOwner()96   public void testNewParameterizedTypeWithOwner() {
97     ParameterizedType jvmType = (ParameterizedType)
98         new TypeCapture<Map.Entry<String, int[][]>>() {}.capture();
99     ParameterizedType ourType = Types.newParameterizedTypeWithOwner(
100         Map.class, Map.Entry.class, String.class, int[][].class);
101 
102     new EqualsTester()
103         .addEqualityGroup(jvmType, ourType)
104         .addEqualityGroup(new TypeCapture<Map.Entry<String, String>>() {}.capture())
105         .addEqualityGroup(new TypeCapture<Map<String, Integer>>() {}.capture())
106         .testEquals();
107     assertEquals(jvmType.toString(), ourType.toString());
108     assertEquals(Map.class, ourType.getOwnerType());
109     assertEquals(Map.Entry.class, ourType.getRawType());
110     assertThat(ourType.getActualTypeArguments()).asList()
111         .has().exactlyAs(asList(jvmType.getActualTypeArguments())).inOrder();
112   }
113 
testNewParameterizedType_serializable()114   public void testNewParameterizedType_serializable() {
115     SerializableTester.reserializeAndAssert(Types.newParameterizedType(
116         Map.Entry.class, String.class, Integer.class));
117   }
118 
testNewParameterizedType_ownerMismatch()119   public void testNewParameterizedType_ownerMismatch() {
120     try {
121       Types.newParameterizedTypeWithOwner(
122           Number.class, List.class, String.class);
123       fail();
124     } catch (IllegalArgumentException expected) {}
125   }
126 
testNewParameterizedType_ownerMissing()127   public void testNewParameterizedType_ownerMissing() {
128     assertEquals(
129         Types.newParameterizedType(Map.Entry.class, String.class, Integer.class),
130         Types.newParameterizedTypeWithOwner(
131             null, Map.Entry.class, String.class, Integer.class));
132   }
133 
testNewParameterizedType_invalidTypeParameters()134   public void testNewParameterizedType_invalidTypeParameters() {
135     try {
136       Types.newParameterizedTypeWithOwner(
137           Map.class, Map.Entry.class, String.class);
138       fail();
139     } catch (IllegalArgumentException expected) {}
140   }
141 
testNewParameterizedType_primitiveTypeParameters()142   public void testNewParameterizedType_primitiveTypeParameters() {
143     try {
144       Types.newParameterizedTypeWithOwner(
145           Map.class, Map.Entry.class, int.class, int.class);
146       fail();
147     } catch (IllegalArgumentException expected) {}
148   }
149 
testNewArrayType()150   public void testNewArrayType() {
151     Type jvmType1 = new TypeCapture<List<String>[]>() {}.capture();
152     GenericArrayType ourType1 = (GenericArrayType) Types.newArrayType(
153         Types.newParameterizedType(List.class, String.class));
154     Type jvmType2 = new TypeCapture<List[]>() {}.capture();
155     Type ourType2 = Types.newArrayType(List.class);
156     new EqualsTester()
157         .addEqualityGroup(jvmType1, ourType1)
158         .addEqualityGroup(jvmType2, ourType2)
159         .testEquals();
160     assertEquals(new TypeCapture<List<String>>() {}.capture(),
161         ourType1.getGenericComponentType());
162     assertEquals(jvmType1.toString(), ourType1.toString());
163     assertEquals(jvmType2.toString(), ourType2.toString());
164   }
165 
testNewArrayTypeOfArray()166   public void testNewArrayTypeOfArray() {
167     Type jvmType = new TypeCapture<int[][]>() {}.capture();
168     Type ourType = Types.newArrayType(int[].class);
169     assertEquals(jvmType.toString(), ourType.toString());
170     new EqualsTester()
171         .addEqualityGroup(jvmType, ourType)
172         .testEquals();
173   }
174 
testNewArrayType_primitive()175   public void testNewArrayType_primitive() {
176     Type jvmType = new TypeCapture<int[]>() {}.capture();
177     Type ourType = Types.newArrayType(int.class);
178     assertEquals(jvmType.toString(), ourType.toString());
179     new EqualsTester()
180         .addEqualityGroup(jvmType, ourType)
181         .testEquals();
182   }
183 
testNewArrayType_upperBoundedWildcard()184   public void testNewArrayType_upperBoundedWildcard() {
185     Type wildcard = Types.subtypeOf(Number.class);
186     assertEquals(Types.subtypeOf(Number[].class), Types.newArrayType(wildcard));
187   }
188 
testNewArrayType_lowerBoundedWildcard()189   public void testNewArrayType_lowerBoundedWildcard() {
190     Type wildcard = Types.supertypeOf(Number.class);
191     assertEquals(Types.supertypeOf(Number[].class), Types.newArrayType(wildcard));
192   }
193 
testNewArrayType_serializable()194   public void testNewArrayType_serializable() {
195     SerializableTester.reserializeAndAssert(
196         Types.newArrayType(int[].class));
197   }
198 
199   private static class WithWildcardType {
200 
201     @SuppressWarnings("unused")
withoutBound(List<?> list)202     void withoutBound(List<?> list) {}
203 
204     @SuppressWarnings("unused")
withObjectBound(List<? extends Object> list)205     void withObjectBound(List<? extends Object> list) {}
206 
207     @SuppressWarnings("unused")
withUpperBound(List<? extends int[][]> list)208     void withUpperBound(List<? extends int[][]> list) {}
209 
210     @SuppressWarnings("unused")
withLowerBound(List<? super String[][]> list)211     void withLowerBound(List<? super String[][]> list) {}
212 
getWildcardType(String methodName)213     static WildcardType getWildcardType(String methodName) throws Exception {
214       ParameterizedType parameterType = (ParameterizedType)
215           WithWildcardType.class
216               .getDeclaredMethod(methodName, List.class)
217               .getGenericParameterTypes()[0];
218       return (WildcardType) parameterType.getActualTypeArguments()[0];
219     }
220   }
221 
testNewWildcardType()222   public void testNewWildcardType() throws Exception {
223     WildcardType noBoundJvmType =
224         WithWildcardType.getWildcardType("withoutBound");
225     WildcardType objectBoundJvmType =
226         WithWildcardType.getWildcardType("withObjectBound");
227     WildcardType upperBoundJvmType =
228         WithWildcardType.getWildcardType("withUpperBound");
229     WildcardType lowerBoundJvmType =
230         WithWildcardType.getWildcardType("withLowerBound");
231     WildcardType objectBound =
232         Types.subtypeOf(Object.class);
233     WildcardType upperBound =
234         Types.subtypeOf(int[][].class);
235     WildcardType lowerBound =
236         Types.supertypeOf(String[][].class);
237 
238     assertEqualWildcardType(noBoundJvmType, objectBound);
239     assertEqualWildcardType(objectBoundJvmType, objectBound);
240     assertEqualWildcardType(upperBoundJvmType, upperBound);
241     assertEqualWildcardType(lowerBoundJvmType, lowerBound);
242 
243     new EqualsTester()
244         .addEqualityGroup(
245             noBoundJvmType, objectBoundJvmType, objectBound)
246         .addEqualityGroup(upperBoundJvmType, upperBound)
247         .addEqualityGroup(lowerBoundJvmType, lowerBound)
248         .testEquals();
249   }
250 
testNewWildcardType_primitiveTypeBound()251   public void testNewWildcardType_primitiveTypeBound() {
252     try {
253       Types.subtypeOf(int.class);
254       fail();
255     } catch (IllegalArgumentException expected) {}
256   }
257 
testNewWildcardType_serializable()258   public void testNewWildcardType_serializable() {
259     SerializableTester.reserializeAndAssert(
260         Types.supertypeOf(String.class));
261     SerializableTester.reserializeAndAssert(
262         Types.subtypeOf(String.class));
263     SerializableTester.reserializeAndAssert(
264         Types.subtypeOf(Object.class));
265   }
266 
assertEqualWildcardType( WildcardType expected, WildcardType actual)267   private static void assertEqualWildcardType(
268       WildcardType expected, WildcardType actual) {
269     assertEquals(expected.toString(), actual.toString());
270     assertEquals(actual.toString(), expected.hashCode(), actual.hashCode());
271     assertThat(actual.getLowerBounds()).asList()
272         .has().exactlyAs(asList(expected.getLowerBounds())).inOrder();
273     assertThat(actual.getUpperBounds()).asList()
274         .has().exactlyAs(asList(expected.getUpperBounds())).inOrder();
275   }
276 
277   private static class WithTypeVariable {
278 
279     @SuppressWarnings("unused")
withoutBound(List<T> list)280     <T> void withoutBound(List<T> list) {}
281 
282     @SuppressWarnings("unused")
withObjectBound(List<T> list)283     <T extends Object> void withObjectBound(List<T> list) {}
284 
285     @SuppressWarnings("unused")
withUpperBound(List<T> list)286     <T extends Number & CharSequence> void withUpperBound(List<T> list) {}
287 
getTypeVariable(String methodName)288     static TypeVariable<?> getTypeVariable(String methodName) throws Exception {
289       ParameterizedType parameterType = (ParameterizedType)
290           WithTypeVariable.class
291               .getDeclaredMethod(methodName, List.class)
292               .getGenericParameterTypes()[0];
293       return (TypeVariable<?>) parameterType.getActualTypeArguments()[0];
294     }
295   }
296 
testNewTypeVariable()297   public void testNewTypeVariable() throws Exception {
298     TypeVariable<?> noBoundJvmType =
299         WithTypeVariable.getTypeVariable("withoutBound");
300     TypeVariable<?> objectBoundJvmType =
301         WithTypeVariable.getTypeVariable("withObjectBound");
302     TypeVariable<?> upperBoundJvmType =
303         WithTypeVariable.getTypeVariable("withUpperBound");
304     TypeVariable<?> noBound = withBounds(noBoundJvmType);
305     TypeVariable<?> objectBound = withBounds(objectBoundJvmType, Object.class);
306     TypeVariable<?> upperBound = withBounds(
307         upperBoundJvmType, Number.class, CharSequence.class);
308 
309     assertEqualTypeVariable(noBoundJvmType, noBound);
310     assertEqualTypeVariable(noBoundJvmType,
311         withBounds(noBoundJvmType, Object.class));
312     assertEqualTypeVariable(objectBoundJvmType, objectBound);
313     assertEqualTypeVariable(upperBoundJvmType, upperBound);
314 
315     new TypeVariableEqualsTester()
316         .addEqualityGroup(noBoundJvmType, noBound)
317         .addEqualityGroup(objectBoundJvmType, objectBound)
318         .addEqualityGroup(upperBoundJvmType, upperBound)
319         .testEquals();
320   }
321 
testNewTypeVariable_primitiveTypeBound()322   public void testNewTypeVariable_primitiveTypeBound() {
323     try {
324       Types.newArtificialTypeVariable(List.class, "E", int.class);
325       fail();
326     } catch (IllegalArgumentException expected) {}
327   }
328 
testNewTypeVariable_serializable()329   public void testNewTypeVariable_serializable() throws Exception {
330     try {
331       SerializableTester.reserialize(Types.newArtificialTypeVariable(List.class, "E"));
332       fail();
333     } catch (RuntimeException expected) {}
334   }
335 
withBounds( TypeVariable<D> typeVariable, Type... bounds)336   private static <D extends GenericDeclaration> TypeVariable<D> withBounds(
337       TypeVariable<D> typeVariable, Type... bounds) {
338     return Types.newArtificialTypeVariable(
339         typeVariable.getGenericDeclaration(), typeVariable.getName(), bounds);
340   }
341 
342   private static class TypeVariableEqualsTester {
343     private final EqualsTester tester = new EqualsTester();
344 
addEqualityGroup(Type jvmType, Type... types)345     TypeVariableEqualsTester addEqualityGroup(Type jvmType, Type... types) {
346       if (Types.NativeTypeVariableEquals.NATIVE_TYPE_VARIABLE_ONLY) {
347         tester.addEqualityGroup(jvmType);
348         tester.addEqualityGroup((Object[]) types);
349       } else {
350         tester.addEqualityGroup(Lists.asList(jvmType, types).toArray());
351       }
352       return this;
353     }
354 
testEquals()355     void testEquals() {
356       tester.testEquals();
357     }
358   }
359 
assertEqualTypeVariable( TypeVariable<?> expected, TypeVariable<?> actual)360   private static void assertEqualTypeVariable(
361       TypeVariable<?> expected, TypeVariable<?> actual) {
362     assertEquals(expected.toString(), actual.toString());
363     assertEquals(expected.getName(), actual.getName());
364     assertEquals(
365         expected.getGenericDeclaration(), actual.getGenericDeclaration());
366     if (!Types.NativeTypeVariableEquals.NATIVE_TYPE_VARIABLE_ONLY) {
367       assertEquals(actual.toString(), expected.hashCode(), actual.hashCode());
368     }
369     assertThat(actual.getBounds()).asList()
370         .has().exactlyAs(asList(expected.getBounds())).inOrder();
371   }
372 
373   /**
374    * Working with arrays requires defensive code. Verify that we clone the
375    * type array for both input and output.
376    */
testNewParameterizedTypeImmutability()377   public void testNewParameterizedTypeImmutability() {
378     Type[] typesIn = { String.class, Integer.class };
379     ParameterizedType parameterizedType
380         = Types.newParameterizedType(Map.class, typesIn);
381     typesIn[0] = null;
382     typesIn[1] = null;
383 
384     Type[] typesOut = parameterizedType.getActualTypeArguments();
385     typesOut[0] = null;
386     typesOut[1] = null;
387 
388     assertEquals(String.class, parameterizedType.getActualTypeArguments()[0]);
389     assertEquals(Integer.class, parameterizedType.getActualTypeArguments()[1]);
390   }
391 
testNewParameterizedTypeWithWrongNumberOfTypeArguments()392   public void testNewParameterizedTypeWithWrongNumberOfTypeArguments() {
393     try {
394       Types.newParameterizedType(
395           Map.class, String.class, Integer.class, Long.class);
396       fail();
397     } catch (IllegalArgumentException expected) {}
398   }
399 
testToString()400   public void testToString() {
401     assertEquals(int[].class.getName(), Types.toString(int[].class));
402     assertEquals(int[][].class.getName(), Types.toString(int[][].class));
403     assertEquals(String[].class.getName(), Types.toString(String[].class));
404     Type elementType = List.class.getTypeParameters()[0];
405     assertEquals(elementType.toString(), Types.toString(elementType));
406   }
407 
testNullPointers()408   public void testNullPointers() {
409     new NullPointerTester().testStaticMethods(Types.class, Visibility.PACKAGE);
410   }
411 }
412