From b616414565bb6675c6b1d969c528f880c27a4837 Mon Sep 17 00:00:00 2001 From: Bojan Tomic Date: Mon, 6 Mar 2017 11:28:48 +0100 Subject: [PATCH] Added support for getting constructor parameter types Fixed hashCode() inconsistency in various type impls --- .../AnnotatedParameterizedTypeImpl.java | 3 +- .../geantyref/AnnotatedTypeVariableImpl.java | 3 +- .../geantyref/AnnotatedWildcardTypeImpl.java | 3 +- .../geantyref/GenericTypeReflector.java | 31 +++++++++++++------ .../geantyref/GenericTypeReflectorTest.java | 13 ++++++-- 5 files changed, 36 insertions(+), 17 deletions(-) diff --git a/src/main/java/io/leangen/geantyref/AnnotatedParameterizedTypeImpl.java b/src/main/java/io/leangen/geantyref/AnnotatedParameterizedTypeImpl.java index d1e0188..c5e295d 100644 --- a/src/main/java/io/leangen/geantyref/AnnotatedParameterizedTypeImpl.java +++ b/src/main/java/io/leangen/geantyref/AnnotatedParameterizedTypeImpl.java @@ -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; @@ -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); } } diff --git a/src/main/java/io/leangen/geantyref/AnnotatedTypeVariableImpl.java b/src/main/java/io/leangen/geantyref/AnnotatedTypeVariableImpl.java index b9e2cc5..463f6c0 100644 --- a/src/main/java/io/leangen/geantyref/AnnotatedTypeVariableImpl.java +++ b/src/main/java/io/leangen/geantyref/AnnotatedTypeVariableImpl.java @@ -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; @@ -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); } } diff --git a/src/main/java/io/leangen/geantyref/AnnotatedWildcardTypeImpl.java b/src/main/java/io/leangen/geantyref/AnnotatedWildcardTypeImpl.java index ce6ff69..ca2ea17 100644 --- a/src/main/java/io/leangen/geantyref/AnnotatedWildcardTypeImpl.java +++ b/src/main/java/io/leangen/geantyref/AnnotatedWildcardTypeImpl.java @@ -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; @@ -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); } } diff --git a/src/main/java/io/leangen/geantyref/GenericTypeReflector.java b/src/main/java/io/leangen/geantyref/GenericTypeReflector.java index b2bb8fa..1bb712f 100644 --- a/src/main/java/io/leangen/geantyref/GenericTypeReflector.java +++ b/src/main/java/io/leangen/geantyref/GenericTypeReflector.java @@ -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; @@ -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; @@ -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]; @@ -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); } /** @@ -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. * diff --git a/src/test/java/io/leangen/geantyref/GenericTypeReflectorTest.java b/src/test/java/io/leangen/geantyref/GenericTypeReflectorTest.java index d2facca..f0fa73a 100644 --- a/src/test/java/io/leangen/geantyref/GenericTypeReflectorTest.java +++ b/src/test/java/io/leangen/geantyref/GenericTypeReflectorTest.java @@ -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; @@ -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>() { - }.getType()); + Type[] result = GenericTypeReflector.getExactParameterTypes(getMethod, new TypeToken>() {}.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>() {}.getType()); + assertEquals(1, result.length); + assertEquals(String.class, result[0]); + } + public void testGetExactSubType() { AnnotatedParameterizedType parent = (AnnotatedParameterizedType) new TypeToken>(){}.getAnnotatedType(); AnnotatedParameterizedType subType = (AnnotatedParameterizedType) getExactSubType(parent, C.class); @@ -120,4 +128,5 @@ private class P {} private class M extends P{} private class C extends M{} private class C1 extends M{} + private static class D { D(T t){}} }