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