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