Skip to content

Instantly share code, notes, and snippets.

@GotoFinal
Created February 20, 2020 22:43
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save GotoFinal/33b9e282f270dbfe61907aa830c27587 to your computer and use it in GitHub Desktop.
Save GotoFinal/33b9e282f270dbfe61907aa830c27587 to your computer and use it in GitHub Desktop.
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);
}
}
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);
}
}
}
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