WildcardUsageTest.java
/*
* Copyright (C) 2015-2016 Federico Tomassetti
* Copyright (C) 2017-2024 The JavaParser Team.
*
* This file is part of JavaParser.
*
* JavaParser can be used either under the terms of
* a) the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
* b) the terms of the Apache License
*
* You should have received a copy of both licenses in LICENCE.LGPL and
* LICENCE.APACHE. Please refer to those files for details.
*
* JavaParser is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*/
package com.github.javaparser.symbolsolver.model.typesystem;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertThrows;
import static org.junit.jupiter.api.Assertions.assertTrue;
import com.github.javaparser.resolution.TypeSolver;
import com.github.javaparser.resolution.declarations.ResolvedTypeParameterDeclaration;
import com.github.javaparser.resolution.model.typesystem.ReferenceTypeImpl;
import com.github.javaparser.resolution.types.ResolvedReferenceType;
import com.github.javaparser.resolution.types.ResolvedType;
import com.github.javaparser.resolution.types.ResolvedTypeVariable;
import com.github.javaparser.resolution.types.ResolvedWildcard;
import com.github.javaparser.symbolsolver.reflectionmodel.ReflectionClassDeclaration;
import com.github.javaparser.symbolsolver.resolution.typesolvers.ReflectionTypeSolver;
import java.io.Serializable;
import java.util.*;
import java.util.stream.Collectors;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
class WildcardUsageTest {
class Foo {}
class Bar extends Foo {}
private TypeSolver typeSolver;
private ReferenceTypeImpl foo;
private ReferenceTypeImpl bar;
private ReferenceTypeImpl object;
private ReferenceTypeImpl string;
private ResolvedWildcard unbounded = ResolvedWildcard.UNBOUNDED;
private ResolvedWildcard superFoo;
private ResolvedWildcard superBar;
private ResolvedWildcard extendsFoo;
private ResolvedWildcard extendsBar;
private ResolvedWildcard superA;
private ResolvedWildcard extendsA;
private ResolvedWildcard superString;
private ResolvedWildcard extendsString;
private ResolvedTypeVariable a;
@BeforeEach
void setup() {
typeSolver = new ReflectionTypeSolver();
foo = new ReferenceTypeImpl(new ReflectionClassDeclaration(Foo.class, typeSolver));
bar = new ReferenceTypeImpl(new ReflectionClassDeclaration(Bar.class, typeSolver));
object = new ReferenceTypeImpl(new ReflectionClassDeclaration(Object.class, typeSolver));
string = new ReferenceTypeImpl(new ReflectionClassDeclaration(String.class, typeSolver));
superFoo = ResolvedWildcard.superBound(foo);
superBar = ResolvedWildcard.superBound(bar);
extendsFoo = ResolvedWildcard.extendsBound(foo);
extendsBar = ResolvedWildcard.extendsBound(bar);
a = new ResolvedTypeVariable(ResolvedTypeParameterDeclaration.onType("A", "foo.Bar", Collections.emptyList()));
superA = ResolvedWildcard.superBound(a);
extendsA = ResolvedWildcard.extendsBound(a);
superString = ResolvedWildcard.superBound(string);
extendsString = ResolvedWildcard.extendsBound(string);
}
@Test
void testIsArray() {
assertEquals(false, unbounded.isArray());
assertEquals(false, superFoo.isArray());
assertEquals(false, superBar.isArray());
assertEquals(false, extendsFoo.isArray());
assertEquals(false, extendsBar.isArray());
}
@Test
void testIsPrimitive() {
assertEquals(false, unbounded.isPrimitive());
assertEquals(false, superFoo.isPrimitive());
assertEquals(false, superBar.isPrimitive());
assertEquals(false, extendsFoo.isPrimitive());
assertEquals(false, extendsBar.isPrimitive());
}
@Test
void testIsNull() {
assertEquals(false, unbounded.isNull());
assertEquals(false, superFoo.isNull());
assertEquals(false, superBar.isNull());
assertEquals(false, extendsFoo.isNull());
assertEquals(false, extendsBar.isNull());
}
@Test
void testIsReference() {
assertEquals(true, unbounded.isReference());
assertEquals(true, superFoo.isReference());
assertEquals(true, superBar.isReference());
assertEquals(true, extendsFoo.isReference());
assertEquals(true, extendsBar.isReference());
}
@Test
void testIsReferenceType() {
assertEquals(false, unbounded.isReferenceType());
assertEquals(false, superFoo.isReferenceType());
assertEquals(false, superBar.isReferenceType());
assertEquals(false, extendsFoo.isReferenceType());
assertEquals(false, extendsBar.isReferenceType());
}
@Test
void testIsVoid() {
assertEquals(false, unbounded.isVoid());
assertEquals(false, superFoo.isVoid());
assertEquals(false, superBar.isVoid());
assertEquals(false, extendsFoo.isVoid());
assertEquals(false, extendsBar.isVoid());
}
@Test
void testIsTypeVariable() {
assertEquals(false, unbounded.isTypeVariable());
assertEquals(false, superFoo.isTypeVariable());
assertEquals(false, superBar.isTypeVariable());
assertEquals(false, extendsFoo.isTypeVariable());
assertEquals(false, extendsBar.isTypeVariable());
}
@Test
void testIsWildcard() {
assertEquals(true, unbounded.isWildcard());
assertEquals(true, superFoo.isWildcard());
assertEquals(true, superBar.isWildcard());
assertEquals(true, extendsFoo.isWildcard());
assertEquals(true, extendsBar.isWildcard());
}
@Test
void testAsArrayTypeUsage() {
assertThrows(UnsupportedOperationException.class, () -> unbounded.asArrayType());
}
@Test
void testAsReferenceTypeUsage() {
assertThrows(UnsupportedOperationException.class, () -> unbounded.asReferenceType());
}
@Test
void testAsTypeParameter() {
assertThrows(UnsupportedOperationException.class, () -> unbounded.asTypeParameter());
}
@Test
void testAsPrimitive() {
assertThrows(UnsupportedOperationException.class, () -> unbounded.asPrimitive());
}
@Test
void testAsWildcard() {
assertTrue(unbounded == unbounded.asWildcard());
assertTrue(superFoo == superFoo.asWildcard());
assertTrue(superBar == superBar.asWildcard());
assertTrue(extendsFoo == extendsFoo.asWildcard());
assertTrue(extendsBar == extendsBar.asWildcard());
}
@Test
void testAsDescribe() {
assertEquals("?", unbounded.describe());
assertEquals(
"? super com.github.javaparser.symbolsolver.model.typesystem.WildcardUsageTest.Foo",
superFoo.describe());
assertEquals(
"? super com.github.javaparser.symbolsolver.model.typesystem.WildcardUsageTest.Bar",
superBar.describe());
assertEquals(
"? extends com.github.javaparser.symbolsolver.model.typesystem.WildcardUsageTest.Foo",
extendsFoo.describe());
assertEquals(
"? extends com.github.javaparser.symbolsolver.model.typesystem.WildcardUsageTest.Bar",
extendsBar.describe());
}
@Test
void testReplaceParam() {
ResolvedTypeParameterDeclaration tpA =
ResolvedTypeParameterDeclaration.onType("A", "foo.Bar", Collections.emptyList());
ResolvedTypeParameterDeclaration tpB =
ResolvedTypeParameterDeclaration.onType("B", "foo.Bar", Collections.emptyList());
assertTrue(unbounded == unbounded.replaceTypeVariables(tpA, string));
assertTrue(superFoo == superFoo.replaceTypeVariables(tpA, string));
assertTrue(extendsFoo == extendsFoo.replaceTypeVariables(tpA, string));
assertEquals(superString, superA.replaceTypeVariables(tpA, string));
assertEquals(extendsString, extendsA.replaceTypeVariables(tpA, string));
assertTrue(superA == superA.replaceTypeVariables(tpB, string));
assertTrue(extendsA == extendsA.replaceTypeVariables(tpB, string));
}
@Test
void testIsAssignableBySimple() {
assertEquals(false, unbounded.isAssignableBy(object));
assertEquals(true, object.isAssignableBy(unbounded));
assertEquals(false, string.isAssignableBy(unbounded));
assertEquals(true, superFoo.isAssignableBy(foo));
assertEquals(false, foo.isAssignableBy(superFoo));
assertEquals(false, extendsFoo.isAssignableBy(foo));
assertEquals(true, foo.isAssignableBy(extendsFoo));
}
/*@Test
public void testIsAssignableByGenerics() {
assertEquals(false, listOfStrings.isAssignableBy(listOfWildcardExtendsString));
assertEquals(false, listOfStrings.isAssignableBy(listOfWildcardExtendsString));
assertEquals(true, listOfWildcardExtendsString.isAssignableBy(listOfStrings));
assertEquals(false, listOfWildcardExtendsString.isAssignableBy(listOfWildcardSuperString));
assertEquals(true, listOfWildcardSuperString.isAssignableBy(listOfStrings));
assertEquals(false, listOfWildcardSuperString.isAssignableBy(listOfWildcardExtendsString));
}
@Test
public void testIsAssignableByGenericsInheritance() {
assertEquals(true, collectionOfString.isAssignableBy(collectionOfString));
assertEquals(true, collectionOfString.isAssignableBy(listOfStrings));
assertEquals(true, collectionOfString.isAssignableBy(linkedListOfString));
assertEquals(false, listOfStrings.isAssignableBy(collectionOfString));
assertEquals(true, listOfStrings.isAssignableBy(listOfStrings));
assertEquals(true, listOfStrings.isAssignableBy(linkedListOfString));
assertEquals(false, linkedListOfString.isAssignableBy(collectionOfString));
assertEquals(false, linkedListOfString.isAssignableBy(listOfStrings));
assertEquals(true, linkedListOfString.isAssignableBy(linkedListOfString));
}
@Test
public void testGetAllAncestorsConsideringTypeParameters() {
assertTrue(linkedListOfString.getAllAncestors().contains(object));
assertTrue(linkedListOfString.getAllAncestors().contains(listOfStrings));
assertTrue(linkedListOfString.getAllAncestors().contains(collectionOfString));
assertFalse(linkedListOfString.getAllAncestors().contains(listOfA));
}
@Test
public void testGetAllAncestorsConsideringGenericsCases() {
ReferenceTypeUsage foo = new ReferenceTypeUsage(new ReflectionClassDeclaration(Foo.class, typeSolver), typeSolver);
ReferenceTypeUsage bar = new ReferenceTypeUsage(new ReflectionClassDeclaration(Bar.class, typeSolver), typeSolver);
ReferenceTypeUsage left, right;
//YES MoreBazzing<Foo, Bar> e1 = new MoreBazzing<Foo, Bar>();
assertEquals(true,
new ReferenceTypeUsage(
new ReflectionClassDeclaration(MoreBazzing.class, typeSolver),
ImmutableList.of(foo, bar), typeSolver)
.isAssignableBy(new ReferenceTypeUsage(
new ReflectionClassDeclaration(MoreBazzing.class, typeSolver),
ImmutableList.of(foo, bar), typeSolver))
);
//YES MoreBazzing<? extends Foo, Bar> e2 = new MoreBazzing<Foo, Bar>();
assertEquals(true,
new ReferenceTypeUsage(
new ReflectionClassDeclaration(MoreBazzing.class, typeSolver),
ImmutableList.of(WildcardUsage.extendsBound(foo), bar), typeSolver)
.isAssignableBy(new ReferenceTypeUsage(
new ReflectionClassDeclaration(MoreBazzing.class, typeSolver),
ImmutableList.of(foo, bar), typeSolver))
);
//YES MoreBazzing<Foo, ? extends Bar> e3 = new MoreBazzing<Foo, Bar>();
assertEquals(true,
new ReferenceTypeUsage(
new ReflectionClassDeclaration(MoreBazzing.class, typeSolver),
ImmutableList.of(foo, WildcardUsage.extendsBound(bar)), typeSolver)
.isAssignableBy(new ReferenceTypeUsage(
new ReflectionClassDeclaration(MoreBazzing.class, typeSolver),
ImmutableList.of(foo, bar), typeSolver))
);
//YES MoreBazzing<? extends Foo, ? extends Foo> e4 = new MoreBazzing<Foo, Bar>();
assertEquals(true,
new ReferenceTypeUsage(
new ReflectionClassDeclaration(MoreBazzing.class, typeSolver),
ImmutableList.of(WildcardUsage.extendsBound(foo), WildcardUsage.extendsBound(foo)), typeSolver)
.isAssignableBy(new ReferenceTypeUsage(
new ReflectionClassDeclaration(MoreBazzing.class, typeSolver),
ImmutableList.of(foo, bar), typeSolver))
);
//YES MoreBazzing<? extends Foo, ? extends Foo> e5 = new MoreBazzing<Bar, Bar>();
left = new ReferenceTypeUsage(
new ReflectionClassDeclaration(MoreBazzing.class, typeSolver),
ImmutableList.of(WildcardUsage.extendsBound(foo), WildcardUsage.extendsBound(foo)), typeSolver);
right = new ReferenceTypeUsage(
new ReflectionClassDeclaration(MoreBazzing.class, typeSolver),
ImmutableList.of(bar, bar), typeSolver);
assertEquals(true, left.isAssignableBy(right));
//YES Bazzer<Object, String, String> e6 = new MoreBazzing<String, Object>();
left = new ReferenceTypeUsage(
new ReflectionClassDeclaration(Bazzer.class, typeSolver),
ImmutableList.of(object, string, string), typeSolver);
right = new ReferenceTypeUsage(
new ReflectionClassDeclaration(MoreBazzing.class, typeSolver),
ImmutableList.of(string, object), typeSolver);
assertEquals(true, left.isAssignableBy(right));
//YES Bazzer<String,String,String> e7 = new MoreBazzing<String, String>();
assertEquals(true,
new ReferenceTypeUsage(
new ReflectionClassDeclaration(Bazzer.class, typeSolver),
ImmutableList.of(string, string, string), typeSolver)
.isAssignableBy(new ReferenceTypeUsage(
new ReflectionClassDeclaration(MoreBazzing.class, typeSolver),
ImmutableList.of(string, string), typeSolver))
);
//YES Bazzer<Bar,String,Foo> e8 = new MoreBazzing<Foo, Bar>();
assertEquals(true,
new ReferenceTypeUsage(
new ReflectionClassDeclaration(Bazzer.class, typeSolver),
ImmutableList.of(bar, string, foo), typeSolver)
.isAssignableBy(new ReferenceTypeUsage(
new ReflectionClassDeclaration(MoreBazzing.class, typeSolver),
ImmutableList.of(foo, bar), typeSolver))
);
//YES Bazzer<Foo,String,Bar> e9 = new MoreBazzing<Bar, Foo>();
assertEquals(true,
new ReferenceTypeUsage(
new ReflectionClassDeclaration(Bazzer.class, typeSolver),
ImmutableList.of(foo, string, bar), typeSolver)
.isAssignableBy(new ReferenceTypeUsage(
new ReflectionClassDeclaration(MoreBazzing.class, typeSolver),
ImmutableList.of(bar, foo), typeSolver))
);
//NO Bazzer<Bar,String,Foo> n1 = new MoreBazzing<Bar, Foo>();
assertEquals(false,
new ReferenceTypeUsage(
new ReflectionClassDeclaration(Bazzer.class, typeSolver),
ImmutableList.of(bar,string,foo), typeSolver)
.isAssignableBy(new ReferenceTypeUsage(
new ReflectionClassDeclaration(MoreBazzing.class, typeSolver),
ImmutableList.of(bar, foo), typeSolver))
);
//NO Bazzer<Bar,String,Bar> n2 = new MoreBazzing<Bar, Foo>();
assertEquals(false,
new ReferenceTypeUsage(
new ReflectionClassDeclaration(Bazzer.class, typeSolver),
ImmutableList.of(bar, string, foo), typeSolver)
.isAssignableBy(new ReferenceTypeUsage(
new ReflectionClassDeclaration(MoreBazzing.class, typeSolver),
ImmutableList.of(bar, foo), typeSolver))
);
//NO Bazzer<Foo,Object,Bar> n3 = new MoreBazzing<Bar, Foo>();
assertEquals(false,
new ReferenceTypeUsage(
new ReflectionClassDeclaration(Bazzer.class, typeSolver),
ImmutableList.of(foo, object, bar), typeSolver)
.isAssignableBy(new ReferenceTypeUsage(
new ReflectionClassDeclaration(MoreBazzing.class, typeSolver),
ImmutableList.of(bar, foo), typeSolver))
);
}
@Test
public void charSequenceIsAssignableToObject() {
TypeSolver typeSolver = new JreTypeSolver();
ReferenceTypeUsage charSequence = new ReferenceTypeUsage(new ReflectionInterfaceDeclaration(CharSequence.class, typeSolver), typeSolver);
ReferenceTypeUsage object = new ReferenceTypeUsage(new ReflectionClassDeclaration(Object.class, typeSolver), typeSolver);
assertEquals(false, charSequence.isAssignableBy(object));
assertEquals(true, object.isAssignableBy(charSequence));
}
@Test
public void testGetFieldTypeExisting() {
class Foo<A> {
List<A> elements;
}
TypeSolver typeSolver = new JreTypeSolver();
ReferenceTypeUsage ref = new ReferenceTypeUsage(new ReflectionClassDeclaration(Foo.class, typeSolver), typeSolver);
assertEquals(true, ref.getFieldType("elements").isPresent());
assertEquals(true, ref.getFieldType("elements").get().isReferenceType());
assertEquals(List.class.getCanonicalName(), ref.getFieldType("elements").get().asReferenceType().getQualifiedName());
assertEquals(1, ref.getFieldType("elements").get().asReferenceType().typeParametersValues().size());
assertEquals(true, ref.getFieldType("elements").get().asReferenceType().typeParametersValues().get(0).isTypeParameter());
assertEquals("A", ref.getFieldType("elements").get().asReferenceType().typeParametersValues().get(0).asTypeParameter().getName());
ref = new ReferenceTypeUsage(new ReflectionClassDeclaration(Foo.class, typeSolver),
ImmutableList.of(new ReferenceTypeUsage(new ReflectionClassDeclaration(String.class, typeSolver), typeSolver)),
typeSolver);
assertEquals(true, ref.getFieldType("elements").isPresent());
assertEquals(true, ref.getFieldType("elements").get().isReferenceType());
assertEquals(List.class.getCanonicalName(), ref.getFieldType("elements").get().asReferenceType().getQualifiedName());
assertEquals(1, ref.getFieldType("elements").get().asReferenceType().typeParametersValues().size());
assertEquals(true, ref.getFieldType("elements").get().asReferenceType().typeParametersValues().get(0).isReferenceType());
assertEquals(String.class.getCanonicalName(), ref.getFieldType("elements").get().asReferenceType().typeParametersValues().get(0).asReferenceType().getQualifiedName());
}
@Test
public void testGetFieldTypeUnexisting() {
class Foo<A> {
List<A> elements;
}
TypeSolver typeSolver = new JreTypeSolver();
ReferenceTypeUsage ref = new ReferenceTypeUsage(new ReflectionClassDeclaration(Foo.class, typeSolver), typeSolver);
assertEquals(false, ref.getFieldType("bar").isPresent());
ref = new ReferenceTypeUsage(new ReflectionClassDeclaration(Foo.class, typeSolver),
ImmutableList.of(new ReferenceTypeUsage(new ReflectionClassDeclaration(String.class, typeSolver), typeSolver)),
typeSolver);
assertEquals(false, ref.getFieldType("bar").isPresent());
}*/
/*
* The raw type is the supertype of all possible generic types, whether they
* contain a wildcard or not. For example, Collection is the supertype of
* Collection<Number>, Collection<Integer>, but also of Collection<?> and
* Collection<? extends Number>
*/
@Test
void testIsRawTypeAssignableByGenerics() {
ResolvedType rawCollectionType = type(Collection.class.getCanonicalName());
ResolvedType collectionOfNumbers =
genericType(Collection.class.getCanonicalName(), Number.class.getCanonicalName());
ResolvedType collectionOfSomethingExtendingNumbers =
genericType(Collection.class.getCanonicalName(), extendsBound(Number.class.getCanonicalName()));
ResolvedType collectionOfSomethingExtendingInteger =
genericType(Collection.class.getCanonicalName(), extendsBound(Integer.class.getCanonicalName()));
ResolvedType collectionOfAnything =
genericType(Collection.class.getCanonicalName(), ResolvedWildcard.UNBOUNDED);
assertTrue(rawCollectionType.isAssignableBy(collectionOfNumbers));
assertTrue(rawCollectionType.isAssignableBy(collectionOfSomethingExtendingNumbers));
assertTrue(rawCollectionType.isAssignableBy(collectionOfSomethingExtendingInteger));
assertTrue(rawCollectionType.isAssignableBy(collectionOfAnything));
}
/*
* Inheritance relationships are the same for generic types as for raw types, as
* long as the generic type does not vary in the hierarchy. So
* Collection<Number> is the supertype of List<Integer>, Set<Integer>, and
* ArrayList<Integer>. Similarly, Collection<? extends Number> is the supertype
* of List<? extends Number>, Set<? extends Number>, and ArrayList<? extends
* Number>. On the other hand, no relationship exists between List<Number>, and
* List<? extends Number>, since the generic type is no longer the same.
*/
@Test
void testIsGenericTypeAssignableByGenerics() {
// Collection<Integer>
ResolvedType collectionOfInteger =
genericType(Collection.class.getCanonicalName(), Integer.class.getCanonicalName());
// List<Integer>
ResolvedType listOfInteger = genericType(List.class.getCanonicalName(), Integer.class.getCanonicalName());
// Set<Integer>
ResolvedType SetOfInteger = genericType(Set.class.getCanonicalName(), Integer.class.getCanonicalName());
assertTrue(collectionOfInteger.isAssignableBy(listOfInteger));
assertTrue(collectionOfInteger.isAssignableBy(SetOfInteger));
// Collection<? extends Number>
ResolvedType collectionOfSomethingExtendingNumbers =
genericType(Collection.class.getCanonicalName(), extendsBound(Number.class.getCanonicalName()));
// list<? extends Number>
ResolvedType listOfSomethingExtendingNumbers =
genericType(List.class.getCanonicalName(), extendsBound(Number.class.getCanonicalName()));
// Set<? extends Number>
ResolvedType setOfSomethingExtendingNumbers =
genericType(Set.class.getCanonicalName(), extendsBound(Number.class.getCanonicalName()));
assertTrue(collectionOfSomethingExtendingNumbers.isAssignableBy(listOfSomethingExtendingNumbers));
assertTrue(collectionOfSomethingExtendingNumbers.isAssignableBy(setOfSomethingExtendingNumbers));
// List<Number>
ResolvedType listOfNumber = genericType(List.class.getCanonicalName(), Number.class.getCanonicalName());
assertFalse(listOfNumber.isAssignableBy(listOfSomethingExtendingNumbers));
// Class<String> is not assignable by class<? extends String>
ResolvedType classOfString = genericType(Class.class.getCanonicalName(), String.class.getCanonicalName());
ResolvedType classOfSomethingExtendingString =
genericType(Class.class.getCanonicalName(), extendsBound(String.class.getCanonicalName()));
assertFalse(classOfString.isAssignableBy(classOfSomethingExtendingString));
}
/*
* The generic type built on the ? type is the supertype of all the generic
* types that can be built on this type, whether they contain a wildcard or not.
* For example, the type Collection<?> is the supertype of Collection<Number>
* and Collection<? extends Number>.
*/
@Test
void testIsUnboundGenericTypeAssignableByGenerics() {
// Collection<?>
ResolvedType collectionOfAnything =
genericType(Collection.class.getCanonicalName(), ResolvedWildcard.UNBOUNDED);
// Collection<? extends Number>
ResolvedType collectionOfSomethingExtendingNumbers =
genericType(Collection.class.getCanonicalName(), extendsBound(Number.class.getCanonicalName()));
// Collection<Number>
ResolvedType collectionOfNumbers =
genericType(Collection.class.getCanonicalName(), Number.class.getCanonicalName());
assertTrue(collectionOfAnything.isAssignableBy(collectionOfSomethingExtendingNumbers));
assertTrue(collectionOfAnything.isAssignableBy(collectionOfNumbers));
}
/*
* A generic type built on a type ? extends X (where X is a given concrete type)
* is the supertype of the generic type built on X, of all the generic types
* built on the subtypes of X, and of all the generic types bounded by
* extensions of the subtypes of X. In other words, Collection<? extends Number>
* is the supertype of Collection<Float> and Collection<? extends Float> (which
* is a bit of a stretch, since Float is a final class).
*/
@Test
void testIsExtendBoundedGenericTypeAssignableByGenerics() {
// Collection<? extends Number>
ResolvedType collectionOfSomethingExtendingNumbers =
genericType(Collection.class.getCanonicalName(), extendsBound(Number.class.getCanonicalName()));
// Collection<Float>
ResolvedType collectionOfFloat =
genericType(Collection.class.getCanonicalName(), Float.class.getCanonicalName());
// Collection<? extends Float>
ResolvedType collectionOfSomethingExtendingFloat =
genericType(Collection.class.getCanonicalName(), extendsBound(Float.class.getCanonicalName()));
assertTrue(collectionOfSomethingExtendingNumbers.isAssignableBy(collectionOfFloat));
assertTrue(collectionOfSomethingExtendingNumbers.isAssignableBy(collectionOfSomethingExtendingFloat));
}
@Test
void testIsSuperBoundedGenericTypeAssignableByGenerics() {
// Collection<? super Number>
ResolvedType collectionOfSomethingSuperNumbers =
genericType(Collection.class.getCanonicalName(), superBound(Number.class.getCanonicalName()));
// List<? super Serializable>
ResolvedType collectionOfSomethingSuperSerializable =
genericType(Collection.class.getCanonicalName(), superBound(Serializable.class.getCanonicalName()));
// Collection<Number>
ResolvedType collectionOfNumber =
genericType(Collection.class.getCanonicalName(), Number.class.getCanonicalName());
// Collection<Serializable>
ResolvedType collectionOfSerializable =
genericType(Collection.class.getCanonicalName(), Serializable.class.getCanonicalName());
assertTrue(collectionOfSomethingSuperNumbers.isAssignableBy(collectionOfSomethingSuperSerializable));
assertTrue(collectionOfSomethingSuperNumbers.isAssignableBy(collectionOfNumber));
assertTrue(collectionOfSomethingSuperNumbers.isAssignableBy(collectionOfSerializable));
}
// Utility methods
private void print(List<ResolvedReferenceType> ancestors) {
for (ResolvedReferenceType ancestor : ancestors) {
System.out.println(ancestor.describe());
}
}
private List<ResolvedType> types(String... types) {
return Arrays.stream(types).map(type -> type(type)).collect(Collectors.toList());
}
private ResolvedType type(String type) {
return new ReferenceTypeImpl(typeSolver.solveType(type));
}
private ResolvedType genericType(String type, String... parameterTypes) {
return new ReferenceTypeImpl(typeSolver.solveType(type), types(parameterTypes));
}
private ResolvedType genericType(String type, ResolvedType... parameterTypes) {
return new ReferenceTypeImpl(typeSolver.solveType(type), Arrays.asList(parameterTypes));
}
private ResolvedType extendsBound(String type) {
return ResolvedWildcard.extendsBound(type(type));
}
private ResolvedType superBound(String type) {
return ResolvedWildcard.superBound(type(type));
}
}