TypeLiteralTypeResolutionTest.java revision d9c913acca55023ef5d76a32c3d4a51ee6b420cb
1/** 2 * Copyright (C) 2008 Google Inc. 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.inject; 18 19import static com.google.inject.Asserts.assertEqualsBothWays; 20import static com.google.inject.Asserts.assertNotSerializable; 21import com.google.common.collect.ImmutableList; 22import com.google.inject.util.Types; 23import static com.google.inject.util.Types.arrayOf; 24import static com.google.inject.util.Types.listOf; 25import static com.google.inject.util.Types.newParameterizedType; 26import static com.google.inject.util.Types.newParameterizedTypeWithOwner; 27import static com.google.inject.util.Types.setOf; 28import java.io.IOException; 29import java.lang.reflect.Constructor; 30import java.lang.reflect.Field; 31import java.lang.reflect.Method; 32import java.lang.reflect.Type; 33import java.util.AbstractCollection; 34import java.util.AbstractList; 35import java.util.ArrayList; 36import java.util.Arrays; 37import java.util.Collection; 38import java.util.HashMap; 39import java.util.List; 40import java.util.Map; 41import java.util.Set; 42import junit.framework.TestCase; 43 44/** 45 * This test checks that TypeLiteral can perform type resolution on its members. 46 * 47 * @author jessewilson@google.com (Jesse Wilson) 48 */ 49public class TypeLiteralTypeResolutionTest extends TestCase { 50 Type arrayListOfString = newParameterizedType(ArrayList.class, String.class); 51 Type hasGenericFieldsOfShort = newParameterizedTypeWithOwner( 52 getClass(), HasGenericFields.class, Short.class); 53 Type hasGenericConstructorOfShort = newParameterizedTypeWithOwner( 54 getClass(), GenericConstructor.class, Short.class); 55 Type throwerOfNpe = newParameterizedTypeWithOwner( 56 getClass(), Thrower.class, NullPointerException.class); 57 Type hasArrayOfShort = newParameterizedTypeWithOwner(getClass(), HasArray.class, Short.class); 58 Type hasRelatedOfString = newParameterizedTypeWithOwner( 59 getClass(), HasRelated.class, String.class, String.class); 60 Type mapK = Map.class.getTypeParameters()[0]; 61 Type hashMapK = HashMap.class.getTypeParameters()[0]; 62 Type setEntryKV; 63 Type entryStringInteger = setOf(newParameterizedTypeWithOwner( 64 Map.class, Map.Entry.class, String.class, Integer.class)); 65 Field list; 66 Field instance; 67 Constructor<GenericConstructor> newHasGenericConstructor; 68 Constructor<Thrower> newThrower; 69 Constructor newString; 70 Method stringIndexOf; 71 Method comparableCompareTo; 72 Method getArray; 73 Method getSetOfArray; 74 Method echo; 75 Method throwS; 76 77 @Override protected void setUp() throws Exception { 78 super.setUp(); 79 80 list = HasGenericFields.class.getField("list"); 81 instance = HasGenericFields.class.getField("instance"); 82 newHasGenericConstructor = GenericConstructor.class.getConstructor(Object.class, Object.class); 83 newThrower = Thrower.class.getConstructor(); 84 stringIndexOf = String.class.getMethod("indexOf", String.class); 85 newString = String.class.getConstructor(String.class); 86 comparableCompareTo = Comparable.class.getMethod("compareTo", Object.class); 87 getArray = HasArray.class.getMethod("getArray"); 88 getSetOfArray = HasArray.class.getMethod("getSetOfArray"); 89 echo = HasRelated.class.getMethod("echo", Object.class); 90 throwS = Thrower.class.getMethod("throwS"); 91 setEntryKV = HashMap.class.getMethod("entrySet").getGenericReturnType(); 92 } 93 94 public void testDirectInheritance() throws NoSuchMethodException { 95 TypeLiteral<?> resolver = TypeLiteral.get(arrayListOfString); 96 assertEquals(listOf(String.class), 97 resolver.getReturnType(List.class.getMethod("subList", int.class, int.class)).getType()); 98 assertEquals(ImmutableList.<TypeLiteral<?>>of(TypeLiteral.get(String.class)), 99 resolver.getParameterTypes(Collection.class.getMethod("add", Object.class))); 100 } 101 102 public void testGenericSupertype() { 103 TypeLiteral<?> resolver = TypeLiteral.get(arrayListOfString); 104 assertEquals(newParameterizedType(Collection.class, String.class), 105 resolver.getSupertype(Collection.class).getType()); 106 assertEquals(newParameterizedType(Iterable.class, String.class), 107 resolver.getSupertype(Iterable.class).getType()); 108 assertEquals(newParameterizedType(AbstractList.class, String.class), 109 resolver.getSupertype(AbstractList.class).getType()); 110 assertEquals(Object.class, resolver.getSupertype(Object.class).getType()); 111 } 112 113 public void testRecursiveTypeVariable() { 114 TypeLiteral<?> resolver = TypeLiteral.get(MyInteger.class); 115 assertEquals(MyInteger.class, resolver.getParameterTypes(comparableCompareTo).get(0).getType()); 116 } 117 118 interface MyComparable<E extends MyComparable<E>> extends Comparable<E> {} 119 120 static class MyInteger implements MyComparable<MyInteger> { 121 int value; 122 public int compareTo(MyInteger o) { 123 return value - o.value; 124 } 125 } 126 127 public void testFields() { 128 TypeLiteral<?> resolver = TypeLiteral.get(hasGenericFieldsOfShort); 129 assertEquals(listOf(Short.class), resolver.getFieldType(list).getType()); 130 assertEquals(Short.class, resolver.getFieldType(instance).getType()); 131 } 132 133 static class HasGenericFields<T> { 134 public List<T> list; 135 public T instance; 136 } 137 138 public void testGenericConstructor() throws NoSuchMethodException { 139 TypeLiteral<?> resolver = TypeLiteral.get(hasGenericConstructorOfShort); 140 assertEquals(Short.class, 141 resolver.getParameterTypes(newHasGenericConstructor).get(0).getType()); 142 } 143 144 static class GenericConstructor<S> { 145 @SuppressWarnings("UnusedDeclaration") 146 public <T> GenericConstructor(S s, T t) {} 147 } 148 149 public void testThrowsExceptions() { 150 TypeLiteral<?> type = TypeLiteral.get(throwerOfNpe); 151 assertEquals(NullPointerException.class, type.getExceptionTypes(newThrower).get(0).getType()); 152 assertEquals(NullPointerException.class, type.getExceptionTypes(throwS).get(0).getType()); 153 } 154 155 static class Thrower<S extends Exception> { 156 public Thrower() throws S {} 157 public void throwS() throws S {} 158 } 159 160 public void testArrays() { 161 TypeLiteral<?> resolver = TypeLiteral.get(hasArrayOfShort); 162 assertEquals(arrayOf(Short.class), resolver.getReturnType(getArray).getType()); 163 assertEquals(setOf(arrayOf(Short.class)), resolver.getReturnType(getSetOfArray).getType()); 164 } 165 166 static interface HasArray<T extends Number> { 167 T[] getArray(); 168 Set<T[]> getSetOfArray(); 169 } 170 171 public void testRelatedTypeVariables() { 172 TypeLiteral<?> resolver = TypeLiteral.get(hasRelatedOfString); 173 assertEquals(String.class, resolver.getParameterTypes(echo).get(0).getType()); 174 assertEquals(String.class, resolver.getReturnType(echo).getType()); 175 } 176 177 interface HasRelated<T, R extends T> { 178 T echo(R r); 179 } 180 181 /** Ensure the cache doesn't cache too much */ 182 public void testCachingAndReindexing() throws NoSuchMethodException { 183 TypeLiteral<?> resolver = TypeLiteral.get( 184 newParameterizedTypeWithOwner(getClass(), HasLists.class, String.class, Short.class)); 185 assertEquals(listOf(String.class), 186 resolver.getReturnType(HasLists.class.getMethod("listS")).getType()); 187 assertEquals(listOf(Short.class), 188 resolver.getReturnType(HasLists.class.getMethod("listT")).getType()); 189 } 190 191 interface HasLists<S, T> { 192 List<S> listS(); 193 List<T> listT(); 194 List<Map.Entry<S, T>> listEntries(); 195 } 196 197 public void testUnsupportedQueries() throws NoSuchMethodException { 198 TypeLiteral<?> resolver = TypeLiteral.get(arrayListOfString); 199 200 try { 201 resolver.getExceptionTypes(stringIndexOf); 202 fail(); 203 } catch (IllegalArgumentException e) { 204 assertEquals("public int java.lang.String.indexOf(java.lang.String) is not defined by a " 205 + "supertype of java.util.ArrayList<java.lang.String>", e.getMessage()); 206 } 207 try { 208 resolver.getParameterTypes(stringIndexOf); 209 fail(); 210 } catch (Exception e) { 211 assertEquals("public int java.lang.String.indexOf(java.lang.String) is not defined by a " 212 + "supertype of java.util.ArrayList<java.lang.String>", e.getMessage()); 213 } 214 try { 215 resolver.getReturnType(stringIndexOf); 216 fail(); 217 } catch (Exception e) { 218 assertEquals("public int java.lang.String.indexOf(java.lang.String) is not defined by a " 219 + "supertype of java.util.ArrayList<java.lang.String>", e.getMessage()); 220 } 221 try { 222 resolver.getSupertype(String.class); 223 fail(); 224 } catch (Exception e) { 225 assertEquals("class java.lang.String is not a supertype of " 226 + "java.util.ArrayList<java.lang.String>", e.getMessage()); 227 } 228 try { 229 resolver.getExceptionTypes(newString); 230 fail(); 231 } catch (Exception e) { 232 assertEquals("public java.lang.String(java.lang.String) does not construct " 233 + "a supertype of java.util.ArrayList<java.lang.String>", e.getMessage()); 234 } 235 try { 236 resolver.getParameterTypes(newString); 237 fail(); 238 } catch (Exception e) { 239 assertEquals("public java.lang.String(java.lang.String) does not construct " 240 + "a supertype of java.util.ArrayList<java.lang.String>", e.getMessage()); 241 } 242 } 243 244 public void testResolve() { 245 TypeLiteral<?> typeResolver = TypeLiteral.get(StringIntegerMap.class); 246 assertEquals(String.class, typeResolver.resolveType(mapK)); 247 248 typeResolver = new TypeLiteral<Map<String, Integer>>() {}; 249 assertEquals(String.class, typeResolver.resolveType(mapK)); 250 assertEquals(Types.mapOf(String.class, Integer.class), 251 typeResolver.getSupertype(Map.class).getType()); 252 253 typeResolver = new TypeLiteral<BetterMap<String, Integer>>() {}; 254 assertEquals(String.class, typeResolver.resolveType(mapK)); 255 256 typeResolver = new TypeLiteral<BestMap<String, Integer>>() {}; 257 assertEquals(String.class, typeResolver.resolveType(mapK)); 258 259 typeResolver = TypeLiteral.get(StringIntegerHashMap.class); 260 assertEquals(String.class, typeResolver.resolveType(mapK)); 261 assertEquals(String.class, typeResolver.resolveType(hashMapK)); 262 assertEquals(entryStringInteger, typeResolver.resolveType(setEntryKV)); 263 assertEquals(Object.class, typeResolver.getSupertype(Object.class).getType()); 264 } 265 266 public void testOnObject() { 267 TypeLiteral<?> typeResolver = TypeLiteral.get(Object.class); 268 assertEquals(Object.class, typeResolver.getSupertype(Object.class).getType()); 269 assertEquals(Object.class, typeResolver.getRawType()); 270 271 // interfaces also resolve Object 272 typeResolver = TypeLiteral.get(Types.setOf(Integer.class)); 273 assertEquals(Object.class, typeResolver.getSupertype(Object.class).getType()); 274 } 275 276 interface StringIntegerMap extends Map<String, Integer> {} 277 interface BetterMap<K1, V1> extends Map<K1, V1> {} 278 interface BestMap<K2, V2> extends BetterMap<K2, V2> {} 279 static class StringIntegerHashMap extends HashMap<String, Integer> {} 280 281 public void testGetSupertype() { 282 TypeLiteral<AbstractList<String>> listOfString = new TypeLiteral<AbstractList<String>>() {}; 283 assertEquals(Types.newParameterizedType(AbstractCollection.class, String.class), 284 listOfString.getSupertype(AbstractCollection.class).getType()); 285 286 TypeLiteral arrayListOfE = TypeLiteral.get(newParameterizedType( 287 ArrayList.class, ArrayList.class.getTypeParameters())); 288 assertEquals( 289 newParameterizedType(AbstractCollection.class, ArrayList.class.getTypeParameters()), 290 arrayListOfE.getSupertype(AbstractCollection.class).getType()); 291 } 292 293 public void testGetSupertypeForArraysAsList() { 294 Class<? extends List> arraysAsListClass = Arrays.asList().getClass(); 295 Type anotherE = arraysAsListClass.getTypeParameters()[0]; 296 TypeLiteral type = TypeLiteral.get(newParameterizedType(AbstractList.class, anotherE)); 297 assertEquals(newParameterizedType(AbstractCollection.class, anotherE), 298 type.getSupertype(AbstractCollection.class).getType()); 299 } 300 301 public void testWildcards() throws NoSuchFieldException { 302 TypeLiteral<Parameterized<String>> ofString = new TypeLiteral<Parameterized<String>>() {}; 303 304 assertEquals(new TypeLiteral<List<String>>() {}.getType(), 305 ofString.getFieldType(Parameterized.class.getField("t")).getType()); 306 assertEquals(new TypeLiteral<List<? extends String>>() {}.getType(), 307 ofString.getFieldType(Parameterized.class.getField("extendsT")).getType()); 308 assertEquals(new TypeLiteral<List<? super String>>() {}.getType(), 309 ofString.getFieldType(Parameterized.class.getField("superT")).getType()); 310 } 311 312 static class Parameterized<T> { 313 public List<T> t; 314 public List<? extends T> extendsT; 315 public List<? super T> superT; 316 } 317 318 // TODO(jessewilson): tests for tricky bounded types like <T extends Collection, Serializable> 319 320 public void testEqualsAndHashCode() throws IOException { 321 TypeLiteral<?> a1 = TypeLiteral.get(arrayListOfString); 322 TypeLiteral<?> a2 = TypeLiteral.get(arrayListOfString); 323 TypeLiteral<?> b = TypeLiteral.get(listOf(String.class)); 324 assertEqualsBothWays(a1, a2); 325 assertNotSerializable(a1); 326 assertFalse(a1.equals(b)); 327 } 328} 329