Showing posts with label Reified. Show all posts
Showing posts with label Reified. Show all posts

Sunday, October 22, 2017

Java Advanced Generics Examples Part 2 - Reflection


When a generic type information is required at runtime, it has to be passed in:


public class GenericClass<T> {

     private final Class<T> type;

     public GenericClass(Class<T> type) {
          this.type = type;
     }

     public Class<T> getMyType() {
         return this.type;
     }
}

Java Generics is not reified. The type information is erased from Java runtime, however declared parameter types are available from Java reflection. The parameter type information of a field, a return type and types of parameters of methods are available at runtime.

e.g. The following parameter information is available:
  1. the field parameters of its generic declaration by using: field.getGenericType()
  2. the parameter declared in the return generic type by using: method.getGenericReturnType()
  3. the parameter declared against all the method parameters by using: method.getGenericParameterTypes
Here are some examples the type information is retrieved, many methods used have been added to Java reflection framework for dealing generics.



public class GenericReflection {
       public static class B {
             private A<String, Integer> a;

             public A<String, Integer> get(final List<String> ls, final List<Integer> li) {
                    a = new A<String, Integer>(ls.get(0), li.get(0));
                    return a;
             }
       }

       public static class A<U, V> {
             private final U u;
             private final V v;

             public A(final U u, V v) {
                    this.u = u;
                    this.v = v;
             }

             @A1
             public U getU() {
                    return u;
             }

             public V getV() {
                    return v;
             }
       }

       public static class IndexedType {
             public int getIndex() {
                    return index;
             }

             public Class<?> getType() {
                    return type;
             }

             private final int index;
             private final Class<?> type;

             public IndexedType(final int index, final Class<?> type) {
                    this.index = index;
                    this.type = type;
             }

             @Override
             public int hashCode() {
                    final int prime = 31;
                    int result = 1;
                    result = prime * result + index;
                    result = prime * result + ((type == null) ? 0 : type.hashCode());
                    return result;
             }

             @Override
             public boolean equals(Object obj) {
                    if (this == obj)
                           return true;
                    if (obj == null)
                           return false;
                    if (getClass() != obj.getClass())
                           return false;
                    IndexedType other = (IndexedType) obj;
                    if (index != other.index)
                           return false;
                    if (type == null) {
                           if (other.type != null)
                                 return false;
                    } else if (!type.equals(other.type))
                           return false;
                    return true;
             }

             @Override
             public String toString() {
                    return "IndexedType [index=" + index + ", type=" + type + "]";
             }
       }

       @Test
       public void test() throws NoSuchMethodException, SecurityException, NoSuchFieldException {
             System.out.println("===test===");
             final B b = new B();
             Map<IndexedType, List<Class<?>>> parameterizedReturnTypeMap = getParameterizedReturnTypes(b, "get", List.class, List.class);
             System.out.println("returnParameterTypes:" + parameterizedReturnTypeMap);
             Map<IndexedType, List<Class<?>>> parameterizedParameterTypeMap = getParameterizedParameterMap(b, "get", List.class,
                           List.class);
             System.out.println("parameterParametersMap:" + parameterizedParameterTypeMap);
             Map<IndexedType, List<Class<?>>> parameterizedFieldArgumentTypeMap = getParameterizedFieldArgumentTypeMap(b, "a");
             System.out.println("fieldArgTypeList:" + parameterizedFieldArgumentTypeMap);
       }

       public static Map<IndexedType, List<Class<?>>> getParameterizedReturnTypes(Object obj, String methodName, Class<?>... parameterTypes)
                    throws NoSuchMethodException, SecurityException {
             final Method method = obj.getClass().getMethod(methodName, parameterTypes);
             final Type genericReturnType = method.getGenericReturnType();
             final Type[] genericTypes = new Type[]{genericReturnType};
             final Map<IndexedType, List<Class<?>>> parameterizedTypeMap = getParameterizedTypeMap(genericTypes);
             return parameterizedTypeMap;
       }

       public static Map<IndexedType, List<Class<?>>> getParameterizedParameterMap(Object obj, String methodName,
                    Class<?>... parameterTypes) throws NoSuchMethodException, SecurityException {
             Method method = obj.getClass().getMethod(methodName, parameterTypes);
             Type[] genericTypes = method.getGenericParameterTypes();

             final Map<IndexedType, List<Class<?>>> parameterizedTypeMap = getParameterizedTypeMap(genericTypes);
             return parameterizedTypeMap;
       }

       public static Map<IndexedType, List<Class<?>>> getParameterizedFieldArgumentTypeMap(Object obj, String fieldName) throws NoSuchFieldException, SecurityException {
             final Field field = obj.getClass().getDeclaredField(fieldName);
             final Type genericFieldType = field.getGenericType();
             final Type[] genericTypes = new Type[]{genericFieldType};
             final Map<IndexedType, List<Class<?>>> parameterizedTypeMap = getParameterizedTypeMap(genericTypes);
             return parameterizedTypeMap;
       }

       private static Map<IndexedType, List<Class<?>>> getParameterizedTypeMap(Type[] genericTypes) {
             final Map<IndexedType, List<Class<?>>> parameterizedTypeMap = new HashMap<>();
             int index = 0;
             for (Type genericType : genericTypes) {
                    if (genericType instanceof ParameterizedType) {
                           ParameterizedType aParameterizedType = (ParameterizedType) genericType;
                           final List<Class<?>> actualTypeArgumentClassList = new ArrayList<>();
                           Class<?> aParameterizedTypeClass = (Class<?>) aParameterizedType.getRawType();
                           parameterizedTypeMap.put(new IndexedType(index++, aParameterizedTypeClass), actualTypeArgumentClassList);
                           Type[] actualTypeArguments = aParameterizedType.getActualTypeArguments();
                           for (Type typeArgument : actualTypeArguments) {
                                 Class<?> actualTypeArgumentClass = (Class<?>) typeArgument;
                                  actualTypeArgumentClassList.add(actualTypeArgumentClass);
                           }
                    }
             }
             return parameterizedTypeMap;
       }

       @Test
       public void testTypeSafeCache() {
             System.out.println("===testTypeSafeCache===");
             final TypeSafeCache cache = new TypeSafeCache();
             cache.put(Integer.class, 1);
             cache.put(Long.class, 1L);
             cache.put(Double.class, 1.0);
             System.out.println(cache.get(Integer.class));
             System.out.println(cache.get(Long.class));
             System.out.println(cache.get(Double.class));
       }

       @Test
       public void testTypeSafeAnnotationType() throws NoSuchMethodException, SecurityException {
             System.out.println("===testTypeSafeAnnotationType===");
             final A<String, Integer> a = new A<>("A", 1);
             Method method = a.getClass().getMethod("getU");
             System.out.println("method" + method);
             Annotation a1 = method.getAnnotation(A1.class);
             System.out.println("annotationType:" + a1.annotationType());
             System.out.println("annotationType().getCanonicalName():" + a1.annotationType().getCanonicalName());
             System.out.println(
                           "annotationType().getClass().getCanonicalName():" + a1.annotationType().getClass().getCanonicalName());
             System.out.println(".annotationType().getTypeName():" + a1.annotationType().getTypeName());
             System.out.println(".annotationType().getGenericSuperclass():" + a1.annotationType().getGenericSuperclass());
             Annotation result = getAnnotation(method, a1.annotationType().getTypeName());
             System.out.println("result:" + result);
       }
}


===testTypeSafeCache===
1
1
1.0
===test===
returnParameterTypes:{IndexedType [index=0, type=class com.americanexpress.ssae.sample.generic.GenericReflection$A]=[class java.lang.String, class java.lang.Integer]}
parameterParametersMap:{IndexedType [index=0, type=interface java.util.List]=[class java.lang.String], IndexedType [index=1, type=interface java.util.List]=[class java.lang.Integer]}

===testTypeSafeAnnotationType===
method:public java.lang.Object com.americanexpress.ssae.sample.generic.GenericReflection$A.getU()
annotationType:interface com.americanexpress.ssae.sample.generic.GenericReflection$A1
annotationType().getCanonicalName():com.americanexpress.ssae.sample.generic.GenericReflection.A1
annotationType().getClass().getCanonicalName():java.lang.Class
.annotationType().getTypeName():com.americanexpress.ssae.sample.generic.GenericReflection$A1
.annotationType().getGenericSuperclass():null
annotationType:interface com.americanexpress.ssae.sample.generic.GenericReflection$A1
result:@com.americanexpress.ssae.sample.generic.GenericReflection$A1()