Skip to content

Commit

Permalink
Added support for getting constructor parameter types
Browse files Browse the repository at this point in the history
Fixed hashCode() inconsistency in various type impls
  • Loading branch information
Bojan Tomic committed Mar 6, 2017
1 parent 68c569f commit b616414
Show file tree
Hide file tree
Showing 5 changed files with 36 additions and 17 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@
import java.lang.reflect.AnnotatedParameterizedType;
import java.lang.reflect.AnnotatedType;
import java.lang.reflect.ParameterizedType;
import java.util.Arrays;

import static io.leangen.geantyref.GenericTypeReflector.typeArraysEqual;

Expand Down Expand Up @@ -32,6 +31,6 @@ public boolean equals(Object other) {

@Override
public int hashCode() {
return super.hashCode() ^ Arrays.hashCode(typeArguments);
return 31 * super.hashCode() ^ GenericTypeReflector.hashCode(typeArguments);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@
import java.lang.reflect.AnnotatedType;
import java.lang.reflect.AnnotatedTypeVariable;
import java.lang.reflect.TypeVariable;
import java.util.Arrays;

import static io.leangen.geantyref.GenericTypeReflector.typeArraysEqual;

Expand Down Expand Up @@ -40,6 +39,6 @@ public boolean equals(Object other) {

@Override
public int hashCode() {
return super.hashCode() ^ Arrays.hashCode(annotatedBounds);
return super.hashCode() ^ GenericTypeReflector.hashCode(annotatedBounds);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@
import java.lang.reflect.AnnotatedType;
import java.lang.reflect.AnnotatedWildcardType;
import java.lang.reflect.WildcardType;
import java.util.Arrays;

import static io.leangen.geantyref.GenericTypeReflector.typeArraysEqual;

Expand Down Expand Up @@ -44,6 +43,6 @@ public boolean equals(Object other) {

@Override
public int hashCode() {
return super.hashCode() ^ Arrays.hashCode(lowerBounds) ^ Arrays.hashCode(upperBounds);
return 31 * super.hashCode() ^ GenericTypeReflector.hashCode(lowerBounds) ^ GenericTypeReflector.hashCode(upperBounds);
}
}
31 changes: 22 additions & 9 deletions src/main/java/io/leangen/geantyref/GenericTypeReflector.java
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
import java.lang.reflect.AnnotatedType;
import java.lang.reflect.AnnotatedTypeVariable;
import java.lang.reflect.AnnotatedWildcardType;
import java.lang.reflect.Executable;
import java.lang.reflect.Field;
import java.lang.reflect.GenericArrayType;
import java.lang.reflect.Method;
Expand All @@ -21,6 +22,7 @@
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.OptionalInt;
import java.util.Set;
import java.util.stream.Stream;

Expand Down Expand Up @@ -491,15 +493,15 @@ public static Type getExactFieldType(Field f, Type type) {
}

/**
* Returns the exact parameter types of the given method in the given type.
* This may be different from {@code m.getGenericParameterTypes()} when the method was declared in a superclass,
* Returns the exact parameter types of the given method/constructor in the given type.
* This may be different from {@code exe.getAnnotatedParameterTypes()} when the method was declared in a superclass,
* or {@code declaringType} has a type parameter that is used in one of the parameters, or {@code declaringType} is a raw type.
*/
public static AnnotatedType[] getExactParameterTypes(Method m, AnnotatedType declaringType) {
AnnotatedType[] parameterTypes = m.getAnnotatedParameterTypes();
AnnotatedType exactDeclaringType = getExactSuperType(capture(declaringType), m.getDeclaringClass());
if (exactDeclaringType == null) { // capture(type) is not a subtype of m.getDeclaringClass()
throw new IllegalArgumentException("The method " + m + " is not a member of type " + declaringType);
public static AnnotatedType[] getExactParameterTypes(Executable exe, AnnotatedType declaringType) {
AnnotatedType[] parameterTypes = exe.getAnnotatedParameterTypes();
AnnotatedType exactDeclaringType = getExactSuperType(capture(declaringType), exe.getDeclaringClass());
if (exactDeclaringType == null) { // capture(type) is not a subtype of exe.getDeclaringClass()
throw new IllegalArgumentException("The method/constructor " + exe + " is not a member of type " + declaringType);
}

AnnotatedType[] result = new AnnotatedType[parameterTypes.length];
Expand All @@ -509,8 +511,8 @@ public static AnnotatedType[] getExactParameterTypes(Method m, AnnotatedType dec
return result;
}

public static Type[] getExactParameterTypes(Method m, Type declaringType) {
return stream(getExactParameterTypes(m, annotate(declaringType))).map(AnnotatedType::getType).toArray(Type[]::new);
public static Type[] getExactParameterTypes(Executable exe, Type declaringType) {
return stream(getExactParameterTypes(exe, annotate(declaringType))).map(AnnotatedType::getType).toArray(Type[]::new);
}

/**
Expand Down Expand Up @@ -793,6 +795,17 @@ public static boolean typeArraysEqual(AnnotatedType[] t1, AnnotatedType[] t2) {
return true;
}

public static int hashCode(AnnotatedType[] t1) {
OptionalInt typeHash = Arrays.stream(t1)
.mapToInt(t -> t.getType().hashCode())
.reduce((x,y) -> x ^ y);
OptionalInt annotationHash = Arrays.stream(t1)
.flatMap(t -> Arrays.stream(t.getAnnotations()))
.mapToInt(Annotation::hashCode)
.reduce((x,y) -> x ^ y);
return 31 * typeHash.orElse(0) ^ annotationHash.orElse(0);
}

/**
* Checks whether the two provided types are of the same structure and annotations on all levels.
*
Expand Down
13 changes: 11 additions & 2 deletions src/test/java/io/leangen/geantyref/GenericTypeReflectorTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
import java.lang.reflect.AnnotatedArrayType;
import java.lang.reflect.AnnotatedParameterizedType;
import java.lang.reflect.AnnotatedType;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.Type;
Expand Down Expand Up @@ -71,13 +72,20 @@ public void testGetExactFieldTypeIllegalArgument() throws SecurityException, NoS
public void testGetExactParameterTypes() throws SecurityException, NoSuchMethodException {
// method: boolean add(int index, E o), erasure is boolean add(int index, Object o)
Method getMethod = List.class.getMethod("add", int.class, Object.class);
Type[] result = GenericTypeReflector.getExactParameterTypes(getMethod, new TypeToken<ArrayList<String>>() {
}.getType());
Type[] result = GenericTypeReflector.getExactParameterTypes(getMethod, new TypeToken<ArrayList<String>>() {}.getType());
assertEquals(2, result.length);
assertEquals(int.class, result[0]);
assertEquals(String.class, result[1]);
}

public void testGetExactConstructorParameterTypes() throws SecurityException, NoSuchMethodException {
// constructor: D(T o), erasure is D(Object o)
Constructor ctor = D.class.getDeclaredConstructor(Object.class);
Type[] result = GenericTypeReflector.getExactParameterTypes(ctor, new TypeToken<D<String>>() {}.getType());
assertEquals(1, result.length);
assertEquals(String.class, result[0]);
}

public void testGetExactSubType() {
AnnotatedParameterizedType parent = (AnnotatedParameterizedType) new TypeToken<P<String, Integer>>(){}.getAnnotatedType();
AnnotatedParameterizedType subType = (AnnotatedParameterizedType) getExactSubType(parent, C.class);
Expand Down Expand Up @@ -120,4 +128,5 @@ private class P<S, K> {}
private class M<U, R> extends P<U, R>{}
private class C<X, Y> extends M<Y, X>{}
private class C1<X, Y, Z> extends M<Y, X>{}
private static class D<T> { D(T t){}}
}

0 comments on commit b616414

Please sign in to comment.