-
-
Save GotoFinal/33b9e282f270dbfe61907aa830c27587 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package com.github.gregorycallea.generics; | |
/** | |
* Common superclass of exceptions thrown by Java Generics operations within this project | |
* | |
* @author gregorycallea <gregory.callea@hotmail.it> | |
* @since 2019-02-15 yyyy/mm/dd | |
*/ | |
public class GenericsException extends Exception { | |
/** | |
* Constructs a new exception with {@code null} as its detail | |
* message. The cause is not initialized, and may subsequently be | |
* initialized by a call to {@link #initCause}. | |
*/ | |
public GenericsException() { | |
super(); | |
} | |
/** | |
* Constructs a new exception with the specified detail message. | |
* The cause is not initialized, and may subsequently be | |
* initialized by a call to {@link #initCause}. | |
* | |
* @param message the detail message. The detail message is saved for | |
* later retrieval by the {@link #getMessage()} method. | |
*/ | |
public GenericsException(String message) { | |
super(message); | |
} | |
/** | |
* Constructs a new exception with the specified detail message | |
* and cause. | |
* | |
* <p>Note that the detail message associated with | |
* {@code cause} is <em>not</em> automatically incorporated in | |
* this exception's detail message. | |
* | |
* @param message the detail message (which is saved for later retrieval | |
* by the {@link #getMessage()} method). | |
* @param cause the cause (which is saved for later retrieval by the | |
* {@link #getCause()} method). (A {@code null} value is | |
* permitted, and indicates that the cause is nonexistent or | |
* unknown.) | |
*/ | |
public GenericsException(String message, Throwable cause) { | |
super(message, cause); | |
} | |
/** | |
* Constructs a new exception with the specified cause and a detail | |
* message of {@code (cause==null ? null : cause.toString())} (which | |
* typically contains the class and detail message of {@code cause}). | |
* | |
* @param cause the cause (which is saved for later retrieval by the | |
* {@link #getCause()} method). (A {@code null} value is | |
* permitted, and indicates that the cause is nonexistent or | |
* unknown.) | |
*/ | |
public GenericsException(Throwable cause) { | |
super(cause); | |
} | |
} | |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package com.github.gregorycallea.generics; | |
import java.lang.reflect.Array; | |
import java.lang.reflect.GenericArrayType; | |
import java.lang.reflect.ParameterizedType; | |
import java.lang.reflect.Type; | |
import java.lang.reflect.TypeVariable; | |
import java.lang.reflect.WildcardType; | |
import java.util.Arrays; | |
import java.util.HashMap; | |
import java.util.Map; | |
import java.util.Objects; | |
import java.util.StringJoiner; | |
/** | |
* Generics utility methods | |
* | |
* @author gregorycallea <gregory.callea@hotmail.it> | |
* @author bratlomiej.mazur <gotofinal@gotofinal.com> | |
* @since 2019-02-15 yyyy/mm/dd | |
*/ | |
public class GenericsUtils { | |
/** | |
* Given a target class it searches and navigates on its hierarchy until the specified root class and returns the type of generic parameter with specific number declared on the root class. | |
* <p> | |
* Example) | |
* If you have the following parent class and child hierarchy: | |
* <p> | |
* Base<I,E,F> | |
* BaseA<F> extends Base<String,Boolean,F> | |
* BaseB extends BaseA<Integer> | |
* <p> | |
* Starting from *BaseB* ( klass ) you can dinamically evaluate the type of each generic defined on the parent class *Base* ( rootClass ) | |
* via the parameter type number on the parent class ( paramTypeNumber ) that are: | |
* <p> | |
* I = 0 | |
* E = 1 | |
* F = 2 | |
* | |
* @param klass The target class | |
* @param rootClass The parameterized root class | |
* @param paramTypeNumber The parameter type number on the parameterized root class | |
* @return The requested type | |
* @throws GenericsException | |
*/ | |
public static Type getParameterizedType(final Class<?> klass, final Class<?> rootClass, final int paramTypeNumber) throws GenericsException { | |
final int targetClassParametersNumber = rootClass.getTypeParameters().length; | |
if (targetClassParametersNumber == 0) { | |
throw new GenericsException(String.format("Target class [%s] has no parameters type", rootClass.getName())); | |
} else if (targetClassParametersNumber - 1 < paramTypeNumber) | |
throw new GenericsException(String.format("Target class [%s] has parameters type which index start from [0] to [%s]. You requested instead parameter with index [%s]", rootClass, paramTypeNumber - 1, targetClassParametersNumber)); | |
Type type = analyzeParameterizedTypes(klass, klass, rootClass, paramTypeNumber, null); | |
if (!isDefined(type)) | |
throw new GenericsException(String.format("Parameter [%s] with index [%d] defined on class [%s] has not been valued yet on child class [%s]", type, paramTypeNumber, rootClass.getName(), klass.getName())); | |
return type; | |
} | |
private static boolean isDefined(Type type) { | |
if (type instanceof Class) { | |
return true; | |
} | |
if (type instanceof GenericArrayType) { | |
return isDefined(((GenericArrayType) type).getGenericComponentType()); | |
} | |
if (type instanceof WildcardType) { | |
for (Type lowerBound : ((WildcardType) type).getLowerBounds()) { | |
if (!isDefined(lowerBound)) { | |
return false; | |
} | |
} | |
for (Type upperBound : ((WildcardType) type).getUpperBounds()) { | |
if (!isDefined(upperBound)) { | |
return false; | |
} | |
} | |
return true; | |
} | |
if (!(type instanceof ParameterizedType)) { | |
return false; | |
} | |
for (Type typeArgument : ((ParameterizedType) type).getActualTypeArguments()) { | |
if (!isDefined(typeArgument)) { | |
return false; | |
} | |
} | |
return true; | |
} | |
/** | |
* Given a target class if recursively searches and navigates on its hierarchy until the specified root class and return the type of parameter type with specific number | |
* On each step, it passes the child class types values on the next step in order to know what is the type implemented by the child class on the parent one. | |
* | |
* @param klass The recursive step class | |
* @param targetClass The target class | |
* @param rootClass The parameterized root class | |
* @param paramTypeNumber The parameter type number on the parameterized root class | |
* @param childClassTypes The child class types map | |
* @return The requested type | |
* @throws GenericsException | |
*/ | |
public static Type analyzeParameterizedTypes(final Class<?> klass, final Class<?> targetClass, final Class<?> rootClass, final int paramTypeNumber, Map<Integer, Type> childClassTypes) throws GenericsException { | |
Type superclassType = klass.getGenericSuperclass(); | |
Map<TypeVariable<?>, Type> currentClassTypes = new HashMap<>(); | |
int z = 0; | |
if (childClassTypes != null) { | |
for (TypeVariable<?> variable : klass.getTypeParameters()) { | |
currentClassTypes.put(variable, childClassTypes.get(z)); | |
z++; | |
} | |
} | |
Map<Integer, Type> superClassesTypes = new HashMap<>(); | |
if (superclassType instanceof ParameterizedType) { | |
int i = 0; | |
for (final Type argType : ((ParameterizedType) superclassType).getActualTypeArguments()) { | |
if (argType instanceof TypeVariable) { | |
superClassesTypes.put(i, currentClassTypes.getOrDefault(argType, argType)); | |
} else { | |
superClassesTypes.put(i, refineType(argType, currentClassTypes)); | |
} | |
i++; | |
} | |
} | |
if (klass != rootClass) { | |
final Class<?> superClass = klass.getSuperclass(); | |
if (superClass == null) | |
throw new GenericsException(String.format("Class [%s] not found on class parent hierarchy [%s]", rootClass, targetClass)); | |
return analyzeParameterizedTypes(superClass, targetClass, rootClass, paramTypeNumber, superClassesTypes); | |
} | |
return childClassTypes.get(paramTypeNumber); | |
} | |
private static Type refineType(Type type, Map<TypeVariable<?>, Type> typeVariablesMap) throws GenericsException { | |
if (type instanceof Class) { | |
return type; | |
} | |
if (type instanceof GenericArrayType) { | |
return refineArrayType((GenericArrayType) type, typeVariablesMap); | |
} | |
if (type instanceof ParameterizedType) { | |
return refineParameterizedType((ParameterizedType) type, typeVariablesMap); | |
} | |
if (type instanceof WildcardType) { | |
return refineWildcardType((WildcardType) type, typeVariablesMap); | |
} | |
if (type instanceof TypeVariable) { | |
return typeVariablesMap.get(type); | |
} | |
throw new GenericsException("Unsolvable generic type: " + type); | |
} | |
private static Type[] refineTypes(Type[] types, Map<TypeVariable<?>, Type> typeVariablesMap) throws GenericsException { | |
Type[] refinedTypes = new Type[types.length]; | |
for (int i = 0; i < types.length; i++) { | |
refinedTypes[i] = refineType(types[i], typeVariablesMap); | |
} | |
return refinedTypes; | |
} | |
private static Type refineWildcardType(WildcardType wildcardType, Map<TypeVariable<?>, Type> typeVariablesMap) throws GenericsException { | |
Type[] refinedUpperBounds = refineTypes(wildcardType.getUpperBounds(), typeVariablesMap); | |
Type[] refinedLowerBounds = refineTypes(wildcardType.getLowerBounds(), typeVariablesMap); | |
return new ResolvedWildcardType(refinedUpperBounds, refinedLowerBounds); | |
} | |
private static Type refineParameterizedType(ParameterizedType parameterizedType, Map<TypeVariable<?>, Type> typeVariablesMap) throws GenericsException { | |
Type[] refinedTypeArguments = refineTypes(parameterizedType.getActualTypeArguments(), typeVariablesMap); | |
return new ResolvedParameterizedType(parameterizedType.getRawType(), refinedTypeArguments, parameterizedType.getOwnerType()); | |
} | |
private static Type refineArrayType( GenericArrayType genericArrayType, Map<TypeVariable<?>, Type> typeVariablesMap) throws GenericsException { | |
int levels = getArrayDimensions(genericArrayType); | |
Type arrayComponentType = refineType(getArrayNestedComponentType(genericArrayType), typeVariablesMap); | |
return buildArrayType(arrayComponentType, levels); | |
} | |
private static Type buildArrayType(Type componentType, int levels) throws GenericsException { | |
if (componentType instanceof Class) { | |
return Array.newInstance(((Class<?>) componentType), new int[levels]).getClass(); | |
} else if (componentType instanceof ParameterizedType) { | |
GenericArrayType genericArrayType = new ResolvedGenericArrayType(componentType); | |
for (int i = 1; i < levels; i++) { | |
genericArrayType = new ResolvedGenericArrayType(genericArrayType); | |
} | |
return genericArrayType; | |
} else { | |
throw new GenericsException("Array can't be of generic type"); | |
} | |
} | |
private static int getArrayDimensions(GenericArrayType genericArrayType) { | |
int levels = 1; | |
GenericArrayType currentArrayLevel = genericArrayType; | |
while (currentArrayLevel.getGenericComponentType() instanceof GenericArrayType) { | |
currentArrayLevel = (GenericArrayType) currentArrayLevel.getGenericComponentType(); | |
levels += 1; | |
} | |
return levels; | |
} | |
private static Type getArrayNestedComponentType(GenericArrayType genericArrayType) { | |
GenericArrayType currentArrayLevel = genericArrayType; | |
while (currentArrayLevel.getGenericComponentType() instanceof GenericArrayType) { | |
currentArrayLevel = (GenericArrayType) currentArrayLevel.getGenericComponentType(); | |
} | |
return currentArrayLevel.getGenericComponentType(); | |
} | |
private static class ResolvedGenericArrayType implements GenericArrayType { | |
private final Type genericComponentType; | |
ResolvedGenericArrayType(Type genericComponentType) { | |
this.genericComponentType = genericComponentType; | |
} | |
@Override | |
public Type getGenericComponentType() { | |
return genericComponentType; | |
} | |
public String toString() { | |
return getGenericComponentType().toString() + "[]"; | |
} | |
@Override | |
public boolean equals(Object o) { | |
if (o instanceof GenericArrayType) { | |
GenericArrayType that = (GenericArrayType) o; | |
return Objects.equals(genericComponentType, that.getGenericComponentType()); | |
} else | |
return false; | |
} | |
@Override | |
public int hashCode() { | |
return Objects.hashCode(genericComponentType); | |
} | |
} | |
private static class ResolvedParameterizedType implements ParameterizedType { | |
private final Type[] actualTypeArguments; | |
private final Class<?> rawType; | |
private final Type ownerType; | |
private ResolvedParameterizedType(Type rawType, Type[] actualTypeArguments, Type ownerType) { | |
this.actualTypeArguments = actualTypeArguments; | |
this.rawType = (Class<?>) rawType; | |
this.ownerType = (ownerType != null) ? ownerType : this.rawType.getDeclaringClass(); | |
} | |
public Type[] getActualTypeArguments() { | |
return actualTypeArguments.clone(); | |
} | |
public Class<?> getRawType() { | |
return rawType; | |
} | |
public Type getOwnerType() { | |
return ownerType; | |
} | |
@Override | |
public boolean equals(Object o) { | |
if (!(o instanceof ParameterizedType)) { | |
return false; | |
} | |
ParameterizedType that = (ParameterizedType) o; | |
if (this == that) | |
return true; | |
Type thatOwner = that.getOwnerType(); | |
Type thatRawType = that.getRawType(); | |
return Objects.equals(ownerType, thatOwner) && Objects.equals(rawType, thatRawType) && | |
Arrays.equals(actualTypeArguments, that.getActualTypeArguments()); | |
} | |
@Override | |
public int hashCode() { | |
return Arrays.hashCode(actualTypeArguments) ^ | |
Objects.hashCode(ownerType) ^ | |
Objects.hashCode(rawType); | |
} | |
public String toString() { | |
StringBuilder sb = new StringBuilder(); | |
if (ownerType != null) { | |
sb.append(ownerType.getTypeName()); | |
sb.append("$"); | |
if (ownerType instanceof ResolvedParameterizedType) { | |
sb.append(rawType.getName().replace(((ResolvedParameterizedType) ownerType).rawType.getName() + "$", "")); | |
} else | |
sb.append(rawType.getSimpleName()); | |
} else | |
sb.append(rawType.getName()); | |
if (actualTypeArguments != null) { | |
StringJoiner sj = new StringJoiner(", ", "<", ">"); | |
sj.setEmptyValue(""); | |
for (Type t : actualTypeArguments) { | |
sj.add(t.getTypeName()); | |
} | |
sb.append(sj.toString()); | |
} | |
return sb.toString(); | |
} | |
} | |
private static class ResolvedWildcardType implements WildcardType { | |
private final Type[] upperBounds; | |
private final Type[] lowerBounds; | |
public ResolvedWildcardType(Type[] upperBounds, Type[] lowerBounds) { | |
this.upperBounds = upperBounds; | |
this.lowerBounds = lowerBounds; | |
} | |
public Type[] getUpperBounds() { | |
return upperBounds.clone(); | |
} | |
public Type[] getLowerBounds() { | |
return lowerBounds.clone(); | |
} | |
public String toString() { | |
Type[] lowerBounds = getLowerBounds(); | |
Type[] bounds = lowerBounds; | |
StringBuilder sb = new StringBuilder(); | |
if (lowerBounds.length > 0) | |
sb.append("? super "); | |
else { | |
Type[] upperBounds = getUpperBounds(); | |
if (upperBounds.length > 0 && !upperBounds[0].equals(Object.class)) { | |
bounds = upperBounds; | |
sb.append("? extends "); | |
} else | |
return "?"; | |
} | |
StringJoiner sj = new StringJoiner(" & "); | |
for (Type bound : bounds) { | |
sj.add(bound.getTypeName()); | |
} | |
sb.append(sj.toString()); | |
return sb.toString(); | |
} | |
@Override | |
public boolean equals(Object o) { | |
if (o instanceof WildcardType) { | |
WildcardType that = (WildcardType) o; | |
return Arrays.equals(this.getLowerBounds(), that.getLowerBounds()) && Arrays.equals(this.getUpperBounds(), that.getUpperBounds()); | |
} else | |
return false; | |
} | |
@Override | |
public int hashCode() { | |
Type[] lowerBounds = getLowerBounds(); | |
Type[] upperBounds = getUpperBounds(); | |
return Arrays.hashCode(lowerBounds) ^ Arrays.hashCode(upperBounds); | |
} | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package com.github.gregorycallea.generics; | |
import com.google.gson.Gson; | |
import org.junit.Test; | |
import java.lang.reflect.Type; | |
import java.util.List; | |
import java.util.ArrayList; | |
import java.util.Objects; | |
import static org.hamcrest.CoreMatchers.is; | |
import static org.hamcrest.MatcherAssert.assertThat; | |
/** | |
* Generics utility methods unit tests | |
* | |
* @author gregorycallea <gregory.callea@hotmail.it> | |
* @since 2019-02-15 yyyy/mm/dd | |
*/ | |
public class GenericsUtilsTest { | |
//===== | |
// Test case n°1 | |
// | |
// Discussed on Stackoverflow question: https://stackoverflow.com/questions/53686017/using-a-generic-type-of-any-nested-subclass-within-its-abstract-superclass | |
//===== | |
//Data classes | |
class User { | |
private String name; | |
public User(String name){ | |
this.name=name; | |
} | |
@Override | |
public boolean equals(Object o) { | |
if (this == o) return true; | |
if (!(o instanceof User)) return false; | |
User user = (User) o; | |
return Objects.equals(name, user.name); | |
} | |
@Override | |
public int hashCode() { | |
return Objects.hash(name); | |
} | |
} | |
class UniversityUser extends User { | |
private int universityID; | |
public UniversityUser(String name, int universityID) { | |
super(name); | |
this.universityID = universityID; | |
} | |
@Override | |
public boolean equals(Object o) { | |
if (this == o) return true; | |
if (!(o instanceof UniversityUser)) return false; | |
if (!super.equals(o)) return false; | |
UniversityUser that = (UniversityUser) o; | |
return universityID == that.universityID; | |
} | |
@Override | |
public int hashCode() { | |
return Objects.hash(super.hashCode(), universityID); | |
} | |
} | |
class Student extends UniversityUser { | |
private int studentID; | |
public Student(String name, int universityID, int studentID) { | |
super(name, universityID); | |
this.studentID = studentID; | |
} | |
@Override | |
public boolean equals(Object o) { | |
if (this == o) return true; | |
if (!(o instanceof Student)) return false; | |
if (!super.equals(o)) return false; | |
Student student = (Student) o; | |
return studentID == student.studentID; | |
} | |
@Override | |
public int hashCode() { | |
return Objects.hash(super.hashCode(), studentID); | |
} | |
} | |
class Teacher extends UniversityUser { | |
private int teacherID; | |
public Teacher(String name, int universityID, int teacherID) { | |
super(name, universityID); | |
this.teacherID = teacherID; | |
} | |
@Override | |
public boolean equals(Object o) { | |
if (this == o) return true; | |
if (!(o instanceof Teacher)) return false; | |
Teacher teacher = (Teacher) o; | |
return teacherID == teacher.teacherID; | |
} | |
@Override | |
public int hashCode() { | |
return Objects.hash(teacherID); | |
} | |
} | |
//Data handler classes | |
abstract class AbstractRequestHandler<I,O> { | |
I input; | |
O output; | |
public AbstractRequestHandler(String inputJson) throws GenericsException { | |
this.input = new Gson().fromJson(inputJson,GenericsUtils.getParameterizedType(getClass(), AbstractRequestHandler.class, 0)); | |
} | |
} | |
class WorkerRequestHandler extends AbstractRequestHandler<List<String>, Boolean> { | |
public WorkerRequestHandler(String inputJson) throws GenericsException { | |
super(inputJson); | |
} | |
} | |
abstract class AbstractUserRequestHandler<I extends User,O> extends AbstractRequestHandler<I,O>{ | |
public AbstractUserRequestHandler(String inputJson) throws GenericsException { | |
super(inputJson); | |
} | |
} | |
abstract class AbstractUniversityRequestHandler<I extends UniversityUser> extends AbstractUserRequestHandler<I,String>{ | |
public AbstractUniversityRequestHandler(String inputJson) throws GenericsException { | |
super(inputJson); | |
} | |
} | |
class StudentRequestHandler extends AbstractUniversityRequestHandler<Student>{ | |
public StudentRequestHandler(String inputJson) throws GenericsException { | |
super(inputJson); | |
} | |
} | |
class TeacherRequestHandler extends AbstractUniversityRequestHandler<Teacher>{ | |
public TeacherRequestHandler(String inputJson) throws GenericsException { | |
super(inputJson); | |
} | |
} | |
@Test | |
public void testCase1() throws Exception { | |
final Teacher teacher = new Teacher("Greg",1,1); | |
final String requestJson = new Gson().toJson(teacher); | |
final Teacher inputRequestObj = new TeacherRequestHandler(requestJson).input; | |
if(!teacher.equals(inputRequestObj)) | |
throw new Exception("Input json has not been parsed correctly from handler!"); | |
final List<String> worker = new ArrayList<>(); | |
worker.add("name"); | |
worker.add("surname"); | |
final String request2Json = new Gson().toJson(worker); | |
final List<String> inputRequest2Obj = new WorkerRequestHandler(request2Json).input; | |
if (!worker.equals(inputRequest2Obj)) | |
throw new Exception("Input2 json has not been parsed correctly from handler!"); | |
} | |
//===== | |
// Test case n°2 | |
// | |
// Classes hierarchy: | |
// | |
// Base<I,E,F> | |
// BaseA<G,H> extends Base<H,Boolean,G> | |
// BaseB<T> extends BaseA<T,String> | |
// BaseC<H> extends BaseB<H> | |
// BaseD extends BaseC<Integer> | |
// BaseE extends BaseD | |
// BaseF extends BaseE | |
// BaseG extends BaseF | |
// BaseH<H> extends BaseG<H,Double> | |
// BaseI<T> extends BaseF<T> | |
// BaseL<J> extends BaseI<J> | |
// BaseM extends BaseL<Float> | |
// BaseN extends BaseM | |
// | |
//===== | |
private class Base<I, E, F> { | |
I var1; | |
E var2; | |
F var3; | |
} | |
private class BaseA<G, H> extends Base<H, Boolean, G> { | |
} | |
//First hierarchy childs | |
private class BaseB<T> extends BaseA<T, String> { | |
} | |
private class BaseC<H> extends BaseB<H> { | |
} | |
private class BaseD extends BaseC<Integer> { | |
} | |
private class BaseE extends BaseD { | |
} | |
private class BaseF extends BaseE { | |
} | |
private class BaseG<H,I> extends BaseF{ | |
} | |
private class BaseH<H> extends BaseG<H,Double>{ | |
} | |
private class BaseI<T> extends BaseH<T>{ | |
} | |
private class BaseL<J> extends BaseH<J>{ | |
} | |
private class BaseM extends BaseL<Float>{ | |
} | |
private class BaseN extends BaseM{ | |
} | |
//Second hierarchy childs | |
private class BaseB2<T> extends BaseA<T, List<String>> { | |
} | |
private class BaseC2 extends BaseB2<Integer> { | |
} | |
//Others classes | |
private class NotBaseChild { | |
} | |
private class BaseWithArrayA<A> extends Base<A[], A[][], A> { | |
} | |
private class BaseWithArrayB extends BaseWithArrayA<Void> { | |
} | |
private class BaseWithNestedGenericA<A> extends Base<List<A>, List<A[]>, List<Void>[]> { | |
} | |
private class BaseWithNestedGenericB extends BaseWithNestedGenericA<Void> { | |
} | |
private class BaseWithGenericArrayA<A> extends Base<List<Void>[], List<? extends A>[], List<? extends A[][][]>[][][]> {} | |
private class BaseWithGenericArrayB extends BaseWithGenericArrayA<Void> {} | |
@Test | |
public void testCase2() throws Exception { | |
Type parameterizedType; | |
parameterizedType = GenericsUtils.getParameterizedType(BaseG.class, Base.class, 0); | |
assertThat((((Class) parameterizedType).getSimpleName()), is("String")); | |
parameterizedType = GenericsUtils.getParameterizedType(BaseH.class, Base.class, 0); | |
assertThat((((Class) parameterizedType).getSimpleName()), is("String")); | |
parameterizedType = GenericsUtils.getParameterizedType(BaseF.class, Base.class, 1); | |
assertThat((((Class) parameterizedType).getSimpleName()), is("Boolean")); | |
parameterizedType = GenericsUtils.getParameterizedType(BaseF.class, Base.class, 2); | |
assertThat((((Class) parameterizedType).getSimpleName()), is("Integer")); | |
parameterizedType = GenericsUtils.getParameterizedType(BaseH.class, Base.class, 2); | |
assertThat((((Class) parameterizedType).getSimpleName()), is("Integer")); | |
parameterizedType = GenericsUtils.getParameterizedType(BaseWithArrayB.class, Base.class, 0); | |
assertThat((((Class) parameterizedType).getSimpleName()), is("Void[]")); | |
parameterizedType = GenericsUtils.getParameterizedType(BaseWithArrayB.class, Base.class, 1); | |
assertThat((((Class) parameterizedType).getSimpleName()), is("Void[][]")); | |
parameterizedType = GenericsUtils.getParameterizedType(BaseB2.class, Base.class, 0); | |
assertThat(parameterizedType.toString(), is("java.util.List<java.lang.String>")); | |
parameterizedType = GenericsUtils.getParameterizedType(BaseWithNestedGenericB.class, Base.class, 0); | |
assertThat(parameterizedType.toString(), is("java.util.List<java.lang.Void>")); | |
parameterizedType = GenericsUtils.getParameterizedType(BaseWithNestedGenericB.class, Base.class, 1); | |
assertThat(parameterizedType.toString(), is("java.util.List<java.lang.Void[]>")); | |
parameterizedType = GenericsUtils.getParameterizedType(BaseWithNestedGenericB.class, Base.class, 2); | |
assertThat(parameterizedType.toString(), is("java.util.List<java.lang.Void>[]")); | |
parameterizedType = GenericsUtils.getParameterizedType(BaseWithGenericArrayB.class, Base.class, 0); | |
assertThat(parameterizedType.toString(), is("java.util.List<java.lang.Void>[]")); | |
parameterizedType = GenericsUtils.getParameterizedType(BaseWithGenericArrayB.class, Base.class, 1); | |
assertThat(parameterizedType.toString(), is("java.util.List<? extends java.lang.Void>[]")); | |
parameterizedType = GenericsUtils.getParameterizedType(BaseWithGenericArrayB.class, Base.class, 2); | |
assertThat(parameterizedType.toString(), is("java.util.List<? extends java.lang.Void[][][]>[][][]")); | |
//Expected exception => Class with no target class on parents hierarchy | |
try { | |
GenericsUtils.getParameterizedType(NotBaseChild.class, Base.class, 2); | |
throw new Exception("Exception should be raised because class with no target class on parents hierarchy"); | |
} catch (GenericsException e) { | |
printExceptedException(e); | |
} | |
//Expected exception => Generic Parameter with index 2 declared on Base class has not yet a type assigned at class BaseB | |
try { | |
GenericsUtils.getParameterizedType(BaseB.class, Base.class, 2); | |
throw new Exception("Exception should be raised because generic Parameter with index 2 declared on Base class has not yet a type assigned at class BaseB"); | |
} catch (GenericsException e) { | |
printExceptedException(e); | |
} | |
//Expected exception => Base declares 3 generic parameters. So available index are from 0 to 2. | |
try { | |
GenericsUtils.getParameterizedType(BaseF.class, Base.class, 3); | |
throw new Exception("Exception should be raised because Base declares 3 generic parameters. So available index are from 0 to 2."); | |
} catch (GenericsException e) { | |
printExceptedException(e); | |
} | |
//Expected exception => BaseE doesn't declare generic parameters | |
try { | |
GenericsUtils.getParameterizedType(BaseF.class, BaseE.class, 2); | |
throw new Exception("Exception should be raised because BaseE doesn't declare generic parameters"); | |
} catch (GenericsException e) { | |
printExceptedException(e); | |
} | |
parameterizedType = GenericsUtils.getParameterizedType(BaseN.class, Base.class, 2); | |
assertThat((((Class) parameterizedType).getSimpleName()), is("Integer")); | |
} | |
/** | |
* Prints excepted exception message: | |
* @param e The expected exception raised | |
*/ | |
private void printExceptedException(final Exception e){ | |
System.out.println("Excepted exception has been raised: " + e.getMessage()); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment