Skip to content

Commit

Permalink
Fix MethodParameter equals/hashcode checks
Browse files Browse the repository at this point in the history
Update `MethodParameter` so that the `equals()` and `hashCode()` methods
consider both the `containingClass` and the `nestingLevel`.

Closes spring-projectsgh-23352
  • Loading branch information
philwebb committed Jul 30, 2019
1 parent 9648b1c commit c4a06cc
Show file tree
Hide file tree
Showing 2 changed files with 60 additions and 7 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@
import org.springframework.lang.Nullable;
import org.springframework.util.Assert;
import org.springframework.util.ClassUtils;
import org.springframework.util.ObjectUtils;

/**
* Helper class that encapsulates the specification of a method parameter, i.e. a {@link Method}
Expand Down Expand Up @@ -652,22 +653,31 @@ protected Annotation[] adaptAnnotationArray(Annotation[] annotations) {
return annotations;
}


@Override
public boolean equals(@Nullable Object other) {
if (this == other) {
public boolean equals(@Nullable Object obj) {
if (this == obj) {
return true;
}
if (!(other instanceof MethodParameter)) {
if (!(obj instanceof MethodParameter)) {
return false;
}
MethodParameter otherParam = (MethodParameter) other;
return (this.parameterIndex == otherParam.parameterIndex && getExecutable().equals(otherParam.getExecutable()));
MethodParameter other = (MethodParameter) obj;
boolean result = true;
result = result && this.executable.equals(other.executable);
result = result && this.parameterIndex == other.parameterIndex;
result = result && ObjectUtils.nullSafeEquals(this.containingClass, other.containingClass);
result = result && this.nestingLevel == other.nestingLevel;
return result;
}

@Override
public int hashCode() {
return (getExecutable().hashCode() * 31 + this.parameterIndex);
int result = 1;
result = 31 * result + this.executable.hashCode();
result = 31 * result + this.parameterIndex;
result = 31 * result + ObjectUtils.nullSafeHashCode(this.containingClass);
result = 31 * result + this.nestingLevel;
return result;
}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
import java.lang.annotation.Target;
import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.concurrent.Callable;

import org.junit.Before;
Expand Down Expand Up @@ -149,6 +150,40 @@ public void genericConstructorParameterInInnerClass() throws Exception {
assertThat(methodParameter.getGenericParameterType()).isEqualTo(ResolvableType.forClassWithGenerics(Callable.class, Integer.class).getType());
}

@Test
public void multipleResolveParameterTypeCalls() throws Exception {
Method method = ArrayList.class.getMethod("get", int.class);
MethodParameter methodParameter = MethodParameter.forExecutable(method, -1);
assertThat(methodParameter.getParameterType()).isEqualTo(Object.class);
GenericTypeResolver.resolveParameterType(methodParameter, StringList.class);
assertThat(methodParameter.getParameterType()).isEqualTo(String.class);
GenericTypeResolver.resolveParameterType(methodParameter, IntegerList.class);
assertThat(methodParameter.getParameterType()).isEqualTo(Integer.class);
}

@Test
public void equalsAndHashCodeConsidersNesting() throws Exception {
Method method = ArrayList.class.getMethod("get", int.class);
MethodParameter m1 = MethodParameter.forExecutable(method, -1);
MethodParameter m2 = MethodParameter.forExecutable(method, -1);
MethodParameter m3 = MethodParameter.forExecutable(method, -1).nested();
assertThat(m1).isEqualTo(m2).isNotEqualTo(m3);
assertThat(m1.hashCode()).isEqualTo(m2.hashCode());
}

@Test
public void equalsAndHashCodeConsidersContainingClass() throws Exception {
Method method = ArrayList.class.getMethod("get", int.class);
MethodParameter m1 = MethodParameter.forExecutable(method, -1);
GenericTypeResolver.resolveParameterType(m1, StringList.class);
MethodParameter m2 = MethodParameter.forExecutable(method, -1);
GenericTypeResolver.resolveParameterType(m2, StringList.class);
MethodParameter m3 = MethodParameter.forExecutable(method, -1);
GenericTypeResolver.resolveParameterType(m3, IntegerList.class);
MethodParameter m4 = MethodParameter.forExecutable(method, -1);
assertThat(m1).isEqualTo(m2).isNotEqualTo(m3).isNotEqualTo(m4);
assertThat(m1.hashCode()).isEqualTo(m2.hashCode());
}

public int method(String p1, long p2) {
return 42;
Expand All @@ -173,4 +208,12 @@ public InnerClass(@Param String s, Callable<Integer> i) {
private @interface Param {
}

private static class StringList extends ArrayList<String> {

}

private static class IntegerList extends ArrayList<Integer> {

}

}

0 comments on commit c4a06cc

Please sign in to comment.