View Javadoc
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  
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  
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  
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  
85    public void testNewParameterizedType_staticLocalClass() {
86      doTestNewParameterizedType_staticLocalClass();
87    }
88  
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  
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 
114   public void testNewParameterizedType_serializable() {
115     SerializableTester.reserializeAndAssert(Types.newParameterizedType(
116         Map.Entry.class, String.class, Integer.class));
117   }
118 
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 
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 
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 
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 
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 
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 
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 
184   public void testNewArrayType_upperBoundedWildcard() {
185     Type wildcard = Types.subtypeOf(Number.class);
186     assertEquals(Types.subtypeOf(Number[].class), Types.newArrayType(wildcard));
187   }
188 
189   public void testNewArrayType_lowerBoundedWildcard() {
190     Type wildcard = Types.supertypeOf(Number.class);
191     assertEquals(Types.supertypeOf(Number[].class), Types.newArrayType(wildcard));
192   }
193 
194   public void testNewArrayType_serializable() {
195     SerializableTester.reserializeAndAssert(
196         Types.newArrayType(int[].class));
197   }
198 
199   private static class WithWildcardType {
200 
201     @SuppressWarnings("unused")
202     void withoutBound(List<?> list) {}
203 
204     @SuppressWarnings("unused")
205     void withObjectBound(List<? extends Object> list) {}
206 
207     @SuppressWarnings("unused")
208     void withUpperBound(List<? extends int[][]> list) {}
209 
210     @SuppressWarnings("unused")
211     void withLowerBound(List<? super String[][]> list) {}
212 
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 
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 
251   public void testNewWildcardType_primitiveTypeBound() {
252     try {
253       Types.subtypeOf(int.class);
254       fail();
255     } catch (IllegalArgumentException expected) {}
256   }
257 
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 
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")
280     <T> void withoutBound(List<T> list) {}
281 
282     @SuppressWarnings("unused")
283     <T extends Object> void withObjectBound(List<T> list) {}
284 
285     @SuppressWarnings("unused")
286     <T extends Number & CharSequence> void withUpperBound(List<T> list) {}
287 
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 
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 
322   public void testNewTypeVariable_primitiveTypeBound() {
323     try {
324       Types.newArtificialTypeVariable(List.class, "E", int.class);
325       fail();
326     } catch (IllegalArgumentException expected) {}
327   }
328 
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 
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 
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 
355     void testEquals() {
356       tester.testEquals();
357     }
358   }
359 
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    */
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 
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 
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 
408   public void testNullPointers() {
409     new NullPointerTester().testStaticMethods(Types.class, Visibility.PACKAGE);
410   }
411 }