TypesTest.java revision 0888a09821a98ac0680fad765217302858e70fa4
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
17package com.google.common.reflect;
18
19import static java.util.Arrays.asList;
20import static org.truth0.Truth.ASSERT;
21
22import com.google.common.collect.Lists;
23import com.google.common.testing.EqualsTester;
24import com.google.common.testing.NullPointerTester;
25import com.google.common.testing.NullPointerTester.Visibility;
26import com.google.common.testing.SerializableTester;
27
28import junit.framework.TestCase;
29
30import java.lang.reflect.GenericArrayType;
31import java.lang.reflect.GenericDeclaration;
32import java.lang.reflect.ParameterizedType;
33import java.lang.reflect.Type;
34import java.lang.reflect.TypeVariable;
35import java.lang.reflect.WildcardType;
36import java.util.Arrays;
37import java.util.HashMap;
38import java.util.List;
39import java.util.Map;
40
41/**
42 * Tests for {@link Types}.
43 *
44 * @author Ben Yu
45 */
46public 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    ASSERT.that(ourType.getActualTypeArguments())
70        .iteratesOverSequence(jvmType.getActualTypeArguments());
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    ASSERT.that(ourType.getActualTypeArguments())
111        .iteratesOverSequence(jvmType.getActualTypeArguments());
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    ASSERT.that(actual.getLowerBounds())
272        .has().exactlyAs(asList(expected.getLowerBounds())).inOrder();
273    ASSERT.that(actual.getUpperBounds())
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    ASSERT.that(actual.getBounds()).has().exactlyAs(asList(expected.getBounds())).inOrder();
370  }
371
372  /**
373   * Working with arrays requires defensive code. Verify that we clone the
374   * type array for both input and output.
375   */
376  public void testNewParameterizedTypeImmutability() {
377    Type[] typesIn = { String.class, Integer.class };
378    ParameterizedType parameterizedType
379        = Types.newParameterizedType(Map.class, typesIn);
380    typesIn[0] = null;
381    typesIn[1] = null;
382
383    Type[] typesOut = parameterizedType.getActualTypeArguments();
384    typesOut[0] = null;
385    typesOut[1] = null;
386
387    assertEquals(String.class, parameterizedType.getActualTypeArguments()[0]);
388    assertEquals(Integer.class, parameterizedType.getActualTypeArguments()[1]);
389  }
390
391  public void testNewParameterizedTypeWithWrongNumberOfTypeArguments() {
392    try {
393      Types.newParameterizedType(
394          Map.class, String.class, Integer.class, Long.class);
395      fail();
396    } catch (IllegalArgumentException expected) {}
397  }
398
399  public void testToString() {
400    assertEquals(int[].class.getName(), Types.toString(int[].class));
401    assertEquals(int[][].class.getName(), Types.toString(int[][].class));
402    assertEquals(String[].class.getName(), Types.toString(String[].class));
403    Type elementType = List.class.getTypeParameters()[0];
404    assertEquals(elementType.toString(), Types.toString(elementType));
405  }
406
407  public void testNullPointers() {
408    new NullPointerTester().testStaticMethods(Types.class, Visibility.PACKAGE);
409  }
410}
411