1 /* 2 * Copyright (c) 2007 Mockito contributors 3 * This program is made available under the terms of the MIT License. 4 */ 5 package org.mockito.internal.util.reflection; 6 7 8 import org.mockito.exceptions.base.MockitoException; 9 import org.mockito.internal.util.Checks; 10 11 import java.lang.reflect.*; 12 import java.util.*; 13 14 15 /** 16 * This class can retrieve generic meta-data that the compiler stores on classes 17 * and accessible members. 18 * 19 * <p> 20 * The main idea of this code is to create a Map that will help to resolve return types. 21 * In order to actually work with nested generics, this map will have to be passed along new instances 22 * as a type context. 23 * </p> 24 * 25 * <p> 26 * Hence : 27 * <ul> 28 * <li>A new instance representing the metadata is created using the {@link #inferFrom(Type)} method from a real 29 * <code>Class</code> or from a <code>ParameterizedType</code>, other types are not yet supported.</li> 30 * 31 * <li>Then from this metadata, we can extract meta-data for a generic return type of a method, using 32 * {@link #resolveGenericReturnType(Method)}.</li> 33 * </ul> 34 * </p> 35 * 36 * <p> 37 * For now this code support the following kind of generic declarations : 38 * <pre class="code"><code class="java"> 39 * interface GenericsNest<K extends Comparable<K> & Cloneable> extends Map<K, Set<Number>> { 40 * Set<Number> remove(Object key); // override with fixed ParameterizedType 41 * List<? super Integer> returning_wildcard_with_class_lower_bound(); 42 * List<? super K> returning_wildcard_with_typeVar_lower_bound(); 43 * List<? extends K> returning_wildcard_with_typeVar_upper_bound(); 44 * K returningK(); 45 * <O extends K> List<O> paramType_with_type_params(); 46 * <S, T extends S> T two_type_params(); 47 * <O extends K> O typeVar_with_type_params(); 48 * Number returningNonGeneric(); 49 * } 50 * </code></pre> 51 * 52 * @see #inferFrom(Type) 53 * @see #resolveGenericReturnType(Method) 54 * @see org.mockito.internal.stubbing.defaultanswers.ReturnsDeepStubs 55 */ 56 public abstract class GenericMetadataSupport { 57 58 // public static MockitoLogger logger = new ConsoleMockitoLogger(); 59 60 /** 61 * Represents actual type variables resolved for current class. 62 */ 63 protected Map<TypeVariable<?>, Type> contextualActualTypeParameters = new HashMap<TypeVariable<?>, Type>(); 64 65 /** 66 * Registers the type variables for the given type and all of its superclasses and superinterfaces. 67 */ registerAllTypeVariables(Type classType)68 protected void registerAllTypeVariables(Type classType) { 69 Queue<Type> typesToRegister = new LinkedList<Type>(); 70 Set<Type> registeredTypes = new HashSet<Type>(); 71 typesToRegister.add(classType); 72 73 while (!typesToRegister.isEmpty()) { 74 Type typeToRegister = typesToRegister.poll(); 75 if (typeToRegister == null || registeredTypes.contains(typeToRegister)) { 76 continue; 77 } 78 79 registerTypeVariablesOn(typeToRegister); 80 registeredTypes.add(typeToRegister); 81 82 Class<?> rawType = extractRawTypeOf(typeToRegister); 83 typesToRegister.add(rawType.getGenericSuperclass()); 84 typesToRegister.addAll(Arrays.asList(rawType.getGenericInterfaces())); 85 } 86 } 87 extractRawTypeOf(Type type)88 protected Class<?> extractRawTypeOf(Type type) { 89 if (type instanceof Class) { 90 return (Class<?>) type; 91 } 92 if (type instanceof ParameterizedType) { 93 return (Class<?>) ((ParameterizedType) type).getRawType(); 94 } 95 if (type instanceof BoundedType) { 96 return extractRawTypeOf(((BoundedType) type).firstBound()); 97 } 98 if (type instanceof TypeVariable) { 99 /* 100 * If type is a TypeVariable, then it is needed to gather data elsewhere. Usually TypeVariables are declared 101 * on the class definition, such as such as List<E>. 102 */ 103 return extractRawTypeOf(contextualActualTypeParameters.get(type)); 104 } 105 throw new MockitoException("Raw extraction not supported for : '" + type + "'"); 106 } 107 registerTypeVariablesOn(Type classType)108 protected void registerTypeVariablesOn(Type classType) { 109 if (!(classType instanceof ParameterizedType)) { 110 return; 111 } 112 ParameterizedType parameterizedType = (ParameterizedType) classType; 113 TypeVariable<?>[] typeParameters = ((Class<?>) parameterizedType.getRawType()).getTypeParameters(); 114 Type[] actualTypeArguments = parameterizedType.getActualTypeArguments(); 115 for (int i = 0; i < actualTypeArguments.length; i++) { 116 TypeVariable<?> typeParameter = typeParameters[i]; 117 Type actualTypeArgument = actualTypeArguments[i]; 118 119 if (actualTypeArgument instanceof WildcardType) { 120 contextualActualTypeParameters.put(typeParameter, boundsOf((WildcardType) actualTypeArgument)); 121 } else if (typeParameter != actualTypeArgument) { 122 contextualActualTypeParameters.put(typeParameter, actualTypeArgument); 123 } 124 // logger.log("For '" + parameterizedType + "' found type variable : { '" + typeParameter + "(" + System.identityHashCode(typeParameter) + ")" + "' : '" + actualTypeArgument + "(" + System.identityHashCode(typeParameter) + ")" + "' }"); 125 } 126 } 127 registerTypeParametersOn(TypeVariable<?>[] typeParameters)128 protected void registerTypeParametersOn(TypeVariable<?>[] typeParameters) { 129 for (TypeVariable<?> type : typeParameters) { 130 registerTypeVariableIfNotPresent(type); 131 } 132 } 133 registerTypeVariableIfNotPresent(TypeVariable<?> typeVariable)134 private void registerTypeVariableIfNotPresent(TypeVariable<?> typeVariable) { 135 if (!contextualActualTypeParameters.containsKey(typeVariable)) { 136 contextualActualTypeParameters.put(typeVariable, boundsOf(typeVariable)); 137 // logger.log("For '" + typeVariable.getGenericDeclaration() + "' found type variable : { '" + typeVariable + "(" + System.identityHashCode(typeVariable) + ")" + "' : '" + boundsOf(typeVariable) + "' }"); 138 } 139 } 140 141 /** 142 * @param typeParameter The TypeVariable parameter 143 * @return A {@link BoundedType} for easy bound information, if first bound is a TypeVariable 144 * then retrieve BoundedType of this TypeVariable 145 */ boundsOf(TypeVariable<?> typeParameter)146 private BoundedType boundsOf(TypeVariable<?> typeParameter) { 147 if (typeParameter.getBounds()[0] instanceof TypeVariable) { 148 return boundsOf((TypeVariable<?>) typeParameter.getBounds()[0]); 149 } 150 return new TypeVarBoundedType(typeParameter); 151 } 152 153 /** 154 * @param wildCard The WildCard type 155 * @return A {@link BoundedType} for easy bound information, if first bound is a TypeVariable 156 * then retrieve BoundedType of this TypeVariable 157 */ boundsOf(WildcardType wildCard)158 private BoundedType boundsOf(WildcardType wildCard) { 159 /* 160 * According to JLS(http://docs.oracle.com/javase/specs/jls/se5.0/html/typesValues.html#4.5.1): 161 * - Lower and upper can't coexist: (for instance, this is not allowed: <? extends List<String> & super MyInterface>) 162 * - Multiple bounds are not supported (for instance, this is not allowed: <? extends List<String> & MyInterface>) 163 */ 164 165 WildCardBoundedType wildCardBoundedType = new WildCardBoundedType(wildCard); 166 if (wildCardBoundedType.firstBound() instanceof TypeVariable) { 167 return boundsOf((TypeVariable<?>) wildCardBoundedType.firstBound()); 168 } 169 170 return wildCardBoundedType; 171 } 172 173 /** 174 * @return Raw type of the current instance. 175 */ rawType()176 public abstract Class<?> rawType(); 177 178 /** 179 * @return Returns extra interfaces <strong>if relevant</strong>, otherwise empty List. 180 */ extraInterfaces()181 public List<Type> extraInterfaces() { 182 return Collections.emptyList(); 183 } 184 185 /** 186 * @return Returns an array with the raw types of {@link #extraInterfaces()} <strong>if relevant</strong>. 187 */ rawExtraInterfaces()188 public Class<?>[] rawExtraInterfaces() { 189 return new Class[0]; 190 } 191 192 /** 193 * @return Returns true if metadata knows about extra-interfaces {@link #extraInterfaces()} <strong>if relevant</strong>. 194 */ hasRawExtraInterfaces()195 public boolean hasRawExtraInterfaces() { 196 return rawExtraInterfaces().length > 0; 197 } 198 199 /** 200 * @return Actual type arguments matching the type variables of the raw type represented by this {@link GenericMetadataSupport} instance. 201 */ actualTypeArguments()202 public Map<TypeVariable<?>, Type> actualTypeArguments() { 203 TypeVariable<?>[] typeParameters = rawType().getTypeParameters(); 204 LinkedHashMap<TypeVariable<?>, Type> actualTypeArguments = new LinkedHashMap<TypeVariable<?>, Type>(); 205 206 for (TypeVariable<?> typeParameter : typeParameters) { 207 208 Type actualType = getActualTypeArgumentFor(typeParameter); 209 210 actualTypeArguments.put(typeParameter, actualType); 211 // logger.log("For '" + rawType().getCanonicalName() + "' returning explicit TypeVariable : { '" + typeParameter + "(" + System.identityHashCode(typeParameter) + ")" + "' : '" + actualType +"' }"); 212 } 213 214 return actualTypeArguments; 215 } 216 getActualTypeArgumentFor(TypeVariable<?> typeParameter)217 protected Type getActualTypeArgumentFor(TypeVariable<?> typeParameter) { 218 Type type = this.contextualActualTypeParameters.get(typeParameter); 219 if (type instanceof TypeVariable) { 220 TypeVariable<?> typeVariable = (TypeVariable<?>) type; 221 return getActualTypeArgumentFor(typeVariable); 222 } 223 224 return type; 225 } 226 227 /** 228 * Resolve current method generic return type to a {@link GenericMetadataSupport}. 229 * 230 * @param method Method to resolve the return type. 231 * @return {@link GenericMetadataSupport} representing this generic return type. 232 */ resolveGenericReturnType(Method method)233 public GenericMetadataSupport resolveGenericReturnType(Method method) { 234 Type genericReturnType = method.getGenericReturnType(); 235 // logger.log("Method '" + method.toGenericString() + "' has return type : " + genericReturnType.getClass().getInterfaces()[0].getSimpleName() + " : " + genericReturnType); 236 237 int arity = 0; 238 while(genericReturnType instanceof GenericArrayType) { 239 arity++; 240 genericReturnType = ((GenericArrayType) genericReturnType).getGenericComponentType(); 241 } 242 243 GenericMetadataSupport genericMetadataSupport = resolveGenericType(genericReturnType, method); 244 if (arity == 0) { 245 return genericMetadataSupport; 246 } else { 247 return new GenericArrayReturnType(genericMetadataSupport, arity); 248 } 249 } 250 resolveGenericType(Type type, Method method)251 private GenericMetadataSupport resolveGenericType(Type type, Method method) { 252 253 if (type instanceof Class) { 254 return new NotGenericReturnTypeSupport(this, type); 255 } 256 if (type instanceof ParameterizedType) { 257 return new ParameterizedReturnType(this, method.getTypeParameters(), (ParameterizedType) type); 258 } 259 if (type instanceof TypeVariable) { 260 return new TypeVariableReturnType(this, method.getTypeParameters(), (TypeVariable<?>) type); 261 } 262 263 throw new MockitoException("Ouch, it shouldn't happen, type '" + type.getClass().getCanonicalName() + "' on method : '" + method.toGenericString() + "' is not supported : " + type); 264 } 265 266 /** 267 * Create an new instance of {@link GenericMetadataSupport} inferred from a {@link Type}. 268 * 269 * <p> 270 * At the moment <code>type</code> can only be a {@link Class} or a {@link ParameterizedType}, otherwise 271 * it'll throw a {@link MockitoException}. 272 * </p> 273 * 274 * @param type The class from which the {@link GenericMetadataSupport} should be built. 275 * @return The new {@link GenericMetadataSupport}. 276 * @throws MockitoException Raised if type is not a {@link Class} or a {@link ParameterizedType}. 277 */ inferFrom(Type type)278 public static GenericMetadataSupport inferFrom(Type type) { 279 Checks.checkNotNull(type, "type"); 280 if (type instanceof Class) { 281 return new FromClassGenericMetadataSupport((Class<?>) type); 282 } 283 if (type instanceof ParameterizedType) { 284 return new FromParameterizedTypeGenericMetadataSupport((ParameterizedType) type); 285 } 286 287 throw new MockitoException("Type meta-data for this Type (" + type.getClass().getCanonicalName() + ") is not supported : " + type); 288 } 289 290 291 //////////////////////////////////////////////////////////////////////////////////////////////////////////////////// 292 //// Below are specializations of GenericMetadataSupport that could handle retrieval of possible Types 293 //////////////////////////////////////////////////////////////////////////////////////////////////////////////////// 294 295 /** 296 * Generic metadata implementation for {@link Class}. 297 * 298 * Offer support to retrieve generic metadata on a {@link Class} by reading type parameters and type variables on 299 * the class and its ancestors and interfaces. 300 */ 301 private static class FromClassGenericMetadataSupport extends GenericMetadataSupport { 302 private final Class<?> clazz; 303 FromClassGenericMetadataSupport(Class<?> clazz)304 public FromClassGenericMetadataSupport(Class<?> clazz) { 305 this.clazz = clazz; 306 307 registerTypeParametersOn(clazz.getTypeParameters()); 308 registerAllTypeVariables(clazz); 309 } 310 311 @Override rawType()312 public Class<?> rawType() { 313 return clazz; 314 } 315 } 316 317 /** 318 * Generic metadata implementation for "standalone" {@link ParameterizedType}. 319 * 320 * Offer support to retrieve generic metadata on a {@link ParameterizedType} by reading type variables of 321 * the related raw type and declared type variable of this parameterized type. 322 * 323 * This class is not designed to work on ParameterizedType returned by {@link Method#getGenericReturnType()}, as 324 * the ParameterizedType instance return in these cases could have Type Variables that refer to type declaration(s). 325 * That's what meant the "standalone" word at the beginning of the Javadoc. 326 * Instead use {@link ParameterizedReturnType}. 327 */ 328 private static class FromParameterizedTypeGenericMetadataSupport extends GenericMetadataSupport { 329 private final ParameterizedType parameterizedType; 330 FromParameterizedTypeGenericMetadataSupport(ParameterizedType parameterizedType)331 public FromParameterizedTypeGenericMetadataSupport(ParameterizedType parameterizedType) { 332 this.parameterizedType = parameterizedType; 333 readActualTypeParameters(); 334 } 335 readActualTypeParameters()336 private void readActualTypeParameters() { 337 registerAllTypeVariables(parameterizedType); 338 } 339 340 @Override rawType()341 public Class<?> rawType() { 342 return (Class<?>) parameterizedType.getRawType(); 343 } 344 } 345 346 /** 347 * Generic metadata specific to {@link ParameterizedType} returned via {@link Method#getGenericReturnType()}. 348 */ 349 private static class ParameterizedReturnType extends GenericMetadataSupport { 350 private final ParameterizedType parameterizedType; 351 private final TypeVariable<?>[] typeParameters; 352 ParameterizedReturnType(GenericMetadataSupport source, TypeVariable<?>[] typeParameters, ParameterizedType parameterizedType)353 public ParameterizedReturnType(GenericMetadataSupport source, TypeVariable<?>[] typeParameters, ParameterizedType parameterizedType) { 354 this.parameterizedType = parameterizedType; 355 this.typeParameters = typeParameters; 356 this.contextualActualTypeParameters = source.contextualActualTypeParameters; 357 358 readTypeParameters(); 359 readTypeVariables(); 360 } 361 readTypeParameters()362 private void readTypeParameters() { 363 registerTypeParametersOn(typeParameters); 364 } 365 readTypeVariables()366 private void readTypeVariables() { 367 registerTypeVariablesOn(parameterizedType); 368 } 369 370 @Override rawType()371 public Class<?> rawType() { 372 return (Class<?>) parameterizedType.getRawType(); 373 } 374 375 } 376 377 /** 378 * Generic metadata for {@link TypeVariable} returned via {@link Method#getGenericReturnType()}. 379 */ 380 private static class TypeVariableReturnType extends GenericMetadataSupport { 381 private final TypeVariable<?> typeVariable; 382 private final TypeVariable<?>[] typeParameters; 383 private Class<?> rawType; 384 TypeVariableReturnType(GenericMetadataSupport source, TypeVariable<?>[] typeParameters, TypeVariable<?> typeVariable)385 public TypeVariableReturnType(GenericMetadataSupport source, TypeVariable<?>[] typeParameters, TypeVariable<?> typeVariable) { 386 this.typeParameters = typeParameters; 387 this.typeVariable = typeVariable; 388 this.contextualActualTypeParameters = source.contextualActualTypeParameters; 389 390 readTypeParameters(); 391 readTypeVariables(); 392 } 393 readTypeParameters()394 private void readTypeParameters() { 395 registerTypeParametersOn(typeParameters); 396 } 397 readTypeVariables()398 private void readTypeVariables() { 399 for (Type type : typeVariable.getBounds()) { 400 registerTypeVariablesOn(type); 401 } 402 registerTypeParametersOn(new TypeVariable[] { typeVariable }); 403 registerTypeVariablesOn(getActualTypeArgumentFor(typeVariable)); 404 } 405 406 @Override rawType()407 public Class<?> rawType() { 408 if (rawType == null) { 409 rawType = extractRawTypeOf(typeVariable); 410 } 411 return rawType; 412 } 413 414 @Override extraInterfaces()415 public List<Type> extraInterfaces() { 416 Type type = extractActualBoundedTypeOf(typeVariable); 417 if (type instanceof BoundedType) { 418 return Arrays.asList(((BoundedType) type).interfaceBounds()); 419 } 420 if (type instanceof ParameterizedType) { 421 return Collections.singletonList(type); 422 } 423 if (type instanceof Class) { 424 return Collections.emptyList(); 425 } 426 throw new MockitoException("Cannot extract extra-interfaces from '" + typeVariable + "' : '" + type + "'"); 427 } 428 429 /** 430 * @return Returns an array with the extracted raw types of {@link #extraInterfaces()}. 431 * @see #extractRawTypeOf(java.lang.reflect.Type) 432 */ rawExtraInterfaces()433 public Class<?>[] rawExtraInterfaces() { 434 List<Type> extraInterfaces = extraInterfaces(); 435 List<Class<?>> rawExtraInterfaces = new ArrayList<Class<?>>(); 436 for (Type extraInterface : extraInterfaces) { 437 Class<?> rawInterface = extractRawTypeOf(extraInterface); 438 // avoid interface collision with actual raw type (with typevariables, resolution ca be quite aggressive) 439 if(!rawType().equals(rawInterface)) { 440 rawExtraInterfaces.add(rawInterface); 441 } 442 } 443 return rawExtraInterfaces.toArray(new Class[rawExtraInterfaces.size()]); 444 } 445 extractActualBoundedTypeOf(Type type)446 private Type extractActualBoundedTypeOf(Type type) { 447 if (type instanceof TypeVariable) { 448 /* 449 If type is a TypeVariable, then it is needed to gather data elsewhere. Usually TypeVariables are declared 450 on the class definition, such as such as List<E>. 451 */ 452 return extractActualBoundedTypeOf(contextualActualTypeParameters.get(type)); 453 } 454 if (type instanceof BoundedType) { 455 Type actualFirstBound = extractActualBoundedTypeOf(((BoundedType) type).firstBound()); 456 if (!(actualFirstBound instanceof BoundedType)) { 457 return type; // avoid going one step further, ie avoid : O(TypeVar) -> K(TypeVar) -> Some ParamType 458 } 459 return actualFirstBound; 460 } 461 return type; // irrelevant, we don't manage other types as they are not bounded. 462 } 463 } 464 465 private static class GenericArrayReturnType extends GenericMetadataSupport { 466 467 private final GenericMetadataSupport genericArrayType; 468 469 private final int arity; 470 GenericArrayReturnType(GenericMetadataSupport genericArrayType, int arity)471 public GenericArrayReturnType(GenericMetadataSupport genericArrayType, int arity) { 472 this.genericArrayType = genericArrayType; 473 this.arity = arity; 474 } 475 476 @Override rawType()477 public Class<?> rawType() { 478 Class<?> rawComponentType = genericArrayType.rawType(); 479 StringBuilder stringBuilder = new StringBuilder(); 480 for (int i = 0; i < arity; i++) { 481 stringBuilder.append("["); 482 } 483 try { 484 return Class.forName(stringBuilder.append("L").append(rawComponentType.getName()).append(";").toString(), false, rawComponentType.getClassLoader()); 485 } catch (ClassNotFoundException e) { 486 throw new IllegalStateException("This was not supposed to happend", e); 487 } 488 } 489 } 490 491 /** 492 * Non-Generic metadata for {@link Class} returned via {@link Method#getGenericReturnType()}. 493 */ 494 private static class NotGenericReturnTypeSupport extends GenericMetadataSupport { 495 private final Class<?> returnType; 496 NotGenericReturnTypeSupport(GenericMetadataSupport source, Type genericReturnType)497 public NotGenericReturnTypeSupport(GenericMetadataSupport source, Type genericReturnType) { 498 returnType = (Class<?>) genericReturnType; 499 this.contextualActualTypeParameters = source.contextualActualTypeParameters; 500 501 registerAllTypeVariables(returnType); 502 } 503 504 @Override rawType()505 public Class<?> rawType() { 506 return returnType; 507 } 508 } 509 510 511 512 /** 513 * Type representing bounds of a type 514 * 515 * @see TypeVarBoundedType 516 * @see <a href="http://docs.oracle.com/javase/specs/jls/se5.0/html/typesValues.html#4.4">http://docs.oracle.com/javase/specs/jls/se5.0/html/typesValues.html#4.4</a> 517 * @see WildCardBoundedType 518 * @see <a href="http://docs.oracle.com/javase/specs/jls/se5.0/html/typesValues.html#4.5.1">http://docs.oracle.com/javase/specs/jls/se5.0/html/typesValues.html#4.5.1</a> 519 */ 520 public interface BoundedType extends Type { firstBound()521 Type firstBound(); 522 interfaceBounds()523 Type[] interfaceBounds(); 524 } 525 526 /** 527 * Type representing bounds of a type variable, allows to keep all bounds information. 528 * 529 * <p>It uses the first bound in the array, as this array is never null and always contains at least 530 * one element (Object is always here if no bounds are declared).</p> 531 * 532 * <p>If upper bounds are declared with SomeClass and additional interfaces, then firstBound will be SomeClass and 533 * interfacesBound will be an array of the additional interfaces. 534 * 535 * i.e. <code>SomeClass</code>. 536 * <pre class="code"><code class="java"> 537 * interface UpperBoundedTypeWithClass<E extends Comparable<E> & Cloneable> { 538 * E get(); 539 * } 540 * // will return Comparable type 541 * </code></pre> 542 * </p> 543 * 544 * @see <a href="http://docs.oracle.com/javase/specs/jls/se5.0/html/typesValues.html#4.4">http://docs.oracle.com/javase/specs/jls/se5.0/html/typesValues.html#4.4</a> 545 */ 546 public static class TypeVarBoundedType implements BoundedType { 547 private final TypeVariable<?> typeVariable; 548 TypeVarBoundedType(TypeVariable<?> typeVariable)549 public TypeVarBoundedType(TypeVariable<?> typeVariable) { 550 this.typeVariable = typeVariable; 551 } 552 553 /** 554 * @return either a class or an interface (parameterized or not), if no bounds declared Object is returned. 555 */ firstBound()556 public Type firstBound() { 557 return typeVariable.getBounds()[0]; // 558 } 559 560 /** 561 * On a Type Variable (typeVar extends C_0 & I_1 & I_2 & etc), will return an array 562 * containing I_1 and I_2. 563 * 564 * @return other bounds for this type, these bounds can only be only interfaces as the JLS says, 565 * empty array if no other bound declared. 566 */ interfaceBounds()567 public Type[] interfaceBounds() { 568 Type[] interfaceBounds = new Type[typeVariable.getBounds().length - 1]; 569 System.arraycopy(typeVariable.getBounds(), 1, interfaceBounds, 0, typeVariable.getBounds().length - 1); 570 return interfaceBounds; 571 } 572 573 @Override equals(Object o)574 public boolean equals(Object o) { 575 if (this == o) return true; 576 if (o == null || getClass() != o.getClass()) return false; 577 578 return typeVariable.equals(((TypeVarBoundedType) o).typeVariable); 579 580 } 581 582 @Override hashCode()583 public int hashCode() { 584 return typeVariable.hashCode(); 585 } 586 587 @Override toString()588 public String toString() { 589 return "{firstBound=" + firstBound() + ", interfaceBounds=" + Arrays.deepToString(interfaceBounds()) + '}'; 590 } 591 typeVariable()592 public TypeVariable<?> typeVariable() { 593 return typeVariable; 594 } 595 } 596 597 /** 598 * Type representing bounds of a wildcard, allows to keep all bounds information. 599 * 600 * <p>The JLS says that lower bound and upper bound are mutually exclusive, and that multiple bounds 601 * are not allowed. 602 * 603 * @see <a href="http://docs.oracle.com/javase/specs/jls/se5.0/html/typesValues.html#4.4">http://docs.oracle.com/javase/specs/jls/se5.0/html/typesValues.html#4.4</a> 604 */ 605 public static class WildCardBoundedType implements BoundedType { 606 private final WildcardType wildcard; 607 608 WildCardBoundedType(WildcardType wildcard)609 public WildCardBoundedType(WildcardType wildcard) { 610 this.wildcard = wildcard; 611 } 612 613 /** 614 * @return The first bound, either a type or a reference to a TypeVariable 615 */ firstBound()616 public Type firstBound() { 617 Type[] lowerBounds = wildcard.getLowerBounds(); 618 Type[] upperBounds = wildcard.getUpperBounds(); 619 620 return lowerBounds.length != 0 ? lowerBounds[0] : upperBounds[0]; 621 } 622 623 /** 624 * @return An empty array as, wildcard don't support multiple bounds. 625 */ interfaceBounds()626 public Type[] interfaceBounds() { 627 return new Type[0]; 628 } 629 630 @Override equals(Object o)631 public boolean equals(Object o) { 632 if (this == o) return true; 633 if (o == null || getClass() != o.getClass()) return false; 634 635 return wildcard.equals(((TypeVarBoundedType) o).typeVariable); 636 637 } 638 639 @Override hashCode()640 public int hashCode() { 641 return wildcard.hashCode(); 642 } 643 644 @Override toString()645 public String toString() { 646 return "{firstBound=" + firstBound() + ", interfaceBounds=[]}"; 647 } 648 wildCard()649 public WildcardType wildCard() { 650 return wildcard; 651 } 652 } 653 654 } 655 656 657