Skip to content

Commit

Permalink
Merge branch '6.2.x'
Browse files Browse the repository at this point in the history
# Conflicts:
#	spring-core/src/main/java/org/springframework/core/ResolvableType.java
  • Loading branch information
jhoeller committed Jan 30, 2025
2 parents 4620d86 + ed994dc commit 7405e20
Show file tree
Hide file tree
Showing 4 changed files with 75 additions and 26 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -177,7 +177,7 @@ else if (genericType instanceof ParameterizedType parameterizedType) {
generics[i] = resolvedTypeArgument;
}
else {
generics[i] = ResolvableType.forType(typeArgument).resolveType();
generics[i] = ResolvableType.forType(typeArgument);
}
}
else if (typeArgument instanceof ParameterizedType) {
Expand Down Expand Up @@ -223,7 +223,7 @@ private static ResolvableType resolveVariable(TypeVariable<?> typeVariable, Reso
return resolvedType;
}
}
return ResolvableType.NONE;
return ResolvableType.forVariableBounds(typeVariable);
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -320,9 +320,6 @@ private boolean isAssignableFrom(ResolvableType other, boolean strict,
other.getComponentType(), true, matchedBefore, upUntilUnresolvable));
}

// We're checking nested generic variables now...
boolean exactMatch = (strict && matchedBefore != null);

// Deal with wildcard bounds
WildcardBounds ourBounds = WildcardBounds.get(this);
WildcardBounds otherBounds = WildcardBounds.get(other);
Expand All @@ -336,8 +333,9 @@ private boolean isAssignableFrom(ResolvableType other, boolean strict,
else if (upUntilUnresolvable) {
return otherBounds.isAssignableFrom(this, matchedBefore);
}
else if (!exactMatch) {
return otherBounds.isAssignableTo(this, matchedBefore);
else if (!strict) {
return (matchedBefore != null ? otherBounds.equalsType(this) :
otherBounds.isAssignableTo(this, matchedBefore));
}
else {
return false;
Expand All @@ -350,6 +348,7 @@ else if (!exactMatch) {
}

// Main assignability check about to follow
boolean exactMatch = (strict && matchedBefore != null);
boolean checkGenerics = true;
Class<?> ourResolved = null;
if (this.type instanceof TypeVariable<?> variable) {
Expand Down Expand Up @@ -942,13 +941,6 @@ ResolvableType resolveType() {
return NONE;
}

private @Nullable Type resolveBounds(Type[] bounds) {
if (bounds.length == 0 || bounds[0] == Object.class) {
return null;
}
return bounds[0];
}

private @Nullable ResolvableType resolveVariable(TypeVariable<?> variable) {
if (this.type instanceof TypeVariable) {
return resolveType().resolveVariable(variable);
Expand Down Expand Up @@ -1449,6 +1441,23 @@ public static ResolvableType forArrayComponent(ResolvableType componentType) {
return new ResolvableType(arrayType, componentType, null, null);
}

/**
* Return a {@code ResolvableType} for the bounds of the specified {@link TypeVariable}.
* @param typeVariable the type variable
* @return a {@code ResolvableType} for the specified bounds
* @since 6.2.3
*/
static ResolvableType forVariableBounds(TypeVariable<?> typeVariable) {
return forType(resolveBounds(typeVariable.getBounds()));
}

private static @Nullable Type resolveBounds(Type[] bounds) {
if (bounds.length == 0 || bounds[0] == Object.class) {
return null;
}
return bounds[0];
}

/**
* Return a {@code ResolvableType} for the specified {@link Type}.
* <p>Note: The resulting {@code ResolvableType} instance may not be {@link Serializable}.
Expand Down Expand Up @@ -1477,7 +1486,6 @@ public static ResolvableType forType(@Nullable Type type, @Nullable ResolvableTy
return forType(type, variableResolver);
}


/**
* Return a {@code ResolvableType} for the specified {@link ParameterizedTypeReference}.
* <p>Note: The resulting {@code ResolvableType} instance may not be {@link Serializable}.
Expand Down Expand Up @@ -1763,6 +1771,21 @@ public boolean isAssignableTo(ResolvableType type, @Nullable Map<Type, Type> mat
}
}

/**
* Return {@code true} if these bounds are equal to the specified type.
* @param type the type to test against
* @return {@code true} if these bounds are equal to the type
* @since 6.2.3
*/
public boolean equalsType(ResolvableType type) {
for (ResolvableType bound : this.bounds) {
if (!type.equalsType(bound)) {
return false;
}
}
return true;
}

/**
* Return the underlying bounds.
*/
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -212,19 +212,27 @@ void resolveNestedTypeVariable() throws Exception {
}

@Test
void resolvedTypeWithBase() {
Type type = method(WithBaseTypes.class, "get").getGenericReturnType();
Type resolvedType = resolveType(type, WithBaseTypes.class);
void resolveTypeWithElementBounds() {
Type type = method(WithElementBounds.class, "get").getGenericReturnType();
Type resolvedType = resolveType(type, WithElementBounds.class);
ParameterizedTypeReference<List<A>> reference = new ParameterizedTypeReference<>() {};
assertThat(resolvedType).isEqualTo(reference.getType());
}

@Test
void resolveTypeWithUnresolvableElement() {
Type type = method(WithUnresolvableElement.class, "get").getGenericReturnType();
Type resolvedType = resolveType(type, WithUnresolvableElement.class);
assertThat(resolvedType.toString()).isEqualTo("java.util.List<E>");
}

private static Method method(Class<?> target, String methodName, Class<?>... parameterTypes) {
Method method = findMethod(target, methodName, parameterTypes);
assertThat(method).describedAs(target.getName() + "#" + methodName).isNotNull();
return method;
}


public interface MyInterfaceType<T> {
}

Expand Down Expand Up @@ -348,33 +356,35 @@ public static class MySimpleTypeWithMethods extends MyTypeWithMethods<Integer> {
static class GenericClass<T> {
}

class A{}
class A {}

class B<T>{}
class B<T> {}

class C extends A {}

class D extends B<Long> {}

class E extends C {}

class TestIfc<T>{}
class TestIfc<T> {}

class TestImpl<I extends A, T extends B<I>> extends TestIfc<T>{
class TestImpl<I extends A, T extends B<I>> extends TestIfc<T> {
}

abstract static class BiGenericClass<T extends B<?>, V extends A> {}

abstract static class SpecializedBiGenericClass<U extends C> extends BiGenericClass<D, U>{}
abstract static class SpecializedBiGenericClass<U extends C> extends BiGenericClass<D, U> {}

static class TypeFixedBiGenericClass extends SpecializedBiGenericClass<E> {}

static class TopLevelClass<T> {

class Nested<X> {
}
}

static class TypedTopLevelClass extends TopLevelClass<Integer> {

class TypedNested extends Nested<Long> {
}
}
Expand All @@ -394,24 +404,31 @@ interface IdFixingRepository<T> extends Repository<T, Long> {
}

static class WithMethodParameter {

public void nestedGenerics(List<Map<String, Integer>> input) {
}
}

public interface ListOfListSupplier<T> {
interface ListOfListSupplier<T> {

List<List<T>> get();
}

public interface StringListOfListSupplier extends ListOfListSupplier<String> {
interface StringListOfListSupplier extends ListOfListSupplier<String> {
}

static class WithBaseTypes {
static class WithElementBounds {

<T extends A> List<T> get() {
return List.of();
}
}

static class WithUnresolvableElement<T> {

List<T> get() {
return List.of();
}
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -1230,6 +1230,8 @@ void strictGenericsMatching() {
ResolvableType consumerUnresolved = ResolvableType.forClass(Consumer.class);
ResolvableType consumerObject = ResolvableType.forClassWithGenerics(Consumer.class, Object.class);
ResolvableType consumerNestedUnresolved = ResolvableType.forClassWithGenerics(Consumer.class, ResolvableType.forClass(Consumer.class));
ResolvableType consumerNumber = ResolvableType.forClassWithGenerics(Consumer.class, Number.class);
ResolvableType consumerExtendsNumber = ResolvableType.forClass(SubConsumer.class);

assertThat(consumerUnresolved.isAssignableFrom(consumerObject)).isTrue();
assertThat(consumerUnresolved.isAssignableFromResolvedPart(consumerObject)).isTrue();
Expand All @@ -1239,6 +1241,10 @@ void strictGenericsMatching() {
assertThat(consumerUnresolved.isAssignableFromResolvedPart(consumerNestedUnresolved)).isTrue();
assertThat(consumerObject.isAssignableFrom(consumerNestedUnresolved)).isFalse();
assertThat(consumerObject.isAssignableFromResolvedPart(consumerNestedUnresolved)).isFalse();
assertThat(consumerObject.isAssignableFrom(consumerNumber)).isFalse();
assertThat(consumerObject.isAssignableFromResolvedPart(consumerNumber)).isFalse();
assertThat(consumerObject.isAssignableFrom(consumerExtendsNumber)).isFalse();
assertThat(consumerObject.isAssignableFromResolvedPart(consumerExtendsNumber)).isTrue();
}

@Test
Expand Down Expand Up @@ -1788,6 +1794,9 @@ public class MyCollectionSuperclassType extends MySuperclassType<Collection<Stri
public interface Consumer<T> {
}

private static class SubConsumer<N extends Number> implements Consumer<N> {
}

public class Wildcard<T extends CharSequence> {
}

Expand Down

0 comments on commit 7405e20

Please sign in to comment.