1

I have two classes and one interface

public class ExcelReader<T> extends Reader<T> implements RowColumnReader<T> {

    // private final TypeToken<T> typeToken = new TypeToken<T>(getClass()) {};
    // private final Type type = typeToken.getType(); 
    /* public Type getType() {
        return type;
        }*/
    // Useing Guava getting System.out.println(reader.getType()) T as ouput

        @Override
        public List<T> readFile(String file) {
          //my code
        }
    }
public abstract class Reader<T> {
    protected Class<T> clazz;

    /*public Reader() {
        clazz = (Class<T>) ((ParameterizedType) getClass().getGenericSuperclass()).getActualTypeArguments()[0];
    }*/
    // Constructor is commented beacuse getting Exception java.lang.ClassCastException: sun.reflect.generics.reflectiveObjects.TypeVariableImpl cannot be cast to java.lang.Class
}

public interface RowColumnReader<T> extends HeaderFields<T>{

    public List<T> readFile(String file);
}

public interface HeaderFields<T> {

    default List<String> getHeader(Class<T> clazz) throws HeaderNotDefiendException {
        //my code
        return headerList;
    }

}

I have tried all possible way to get class of generic type. I followed Get generic type of class at runtime and even tried all solution given in that page. But still not getting any luck.

Even with Gauva

https://stackoverflow.com/a/19775924/2290078 getting the output of getType() is T not the actual generic class

My test code is

Reader<Test> reader = new ExcelReader<>();
//System.out.println(reader.getType());  ---> output is T 
List<Test> list = reader.readFile("Sample.xlsx");
System.out.println(list);
Nikhil Mishra
  • 397
  • 1
  • 6
  • 21

2 Answers2

3

I have tried all possible way to get class of generic type.

This way that you found here is not working to retrieve the generic :

public Reader() {
    clazz = (Class<T>) ((ParameterizedType) getClass().getGenericSuperclass()).getActualTypeArguments()[0];
}

because it retrieves statically the parameterized type defined in the immediate superclass of Reader, that in this case is Object.

Moving this code in the subclass constructor will not work either :

public ExcelReader() {
    clazz = (Class<T>) ((ParameterizedType) getClass().getGenericSuperclass()).getActualTypeArguments()[0];
}

as it retrieves statically the parameterized type defined in the Reader class, that is T. As T doesn't have any bound, you will still get the Object class as returned class.

Which solution to apply in your case ?

Generics are erased after compilation. So to be aware that the parameterized type information used at runtime is Test here :

Reader<Test> reader = new ExcelReader<>();

you have to explicitly "pass" the Class representing the generic in your actual code.
To do that, add in the constructor a Class parameter representing the generic type and store it in a field. Now you have a way to refer the type at runtime.

public class ExcelReader<T> extends Reader<T> implements RowColumnReader<T> {

   private Class<T> genericType;

   public ExcelReader(Class<T> genericType){
       this.genericType = genericType;
   }
   /*...*/
}

Now you can instantiate ExcelReader in this way :

ExcelReader<Test> excelReader = new ExcelReader<>(Test.class);
davidxxx
  • 125,838
  • 23
  • 214
  • 215
  • I am using the same. But I was looking for a better option. – Nikhil Mishra Jun 24 '18 at 09:31
  • Ah ? While it is not what you wrote in the posted code. And you also told that it didn't work, you don't told that you searched a better option. – davidxxx Jun 24 '18 at 09:35
  • I was trying to create util class with the solution like in given link https://stackoverflow.com/questions/3403909/get-generic-type-of-class-at-runtime – Nikhil Mishra Jun 24 '18 at 09:40
  • 1
    You probably didn't see the comment about this answer : https://stackoverflow.com/a/3403976/270371. "this technique works where the type parameter is defined on the immediate superclass". In your class, the type is defined in the current class and not in the superclass. – davidxxx Jun 24 '18 at 09:46
2

It is not just a matter of "What API should I use to get the generic type at runtime?". It depends upon what you want to do with the generic type(T).

Generics are more of a compile-time checking feature and can impose some useful restrictions using T extends and T super. That's it. This might be useful Difference between <? super T> and <? extends T> in Java

If you want to know the type T at runtime and do condition implementation using something like a switch then the class you are declaring generic is not really generic class by definition as the implementation differs for the different type and hence it is not gracefully supported.

  • Thanks. Now, I got it is even not possible. @davidxxx's answer is the only solution. – Nikhil Mishra Jun 24 '18 at 09:43
  • Happy to help. Also, the technique that he refers is often used but that defeats the purpose of the class being generic. Better examples of where a class should be generic are demonstrated by the Collections framework and the Spring core code if you are interested. – Siddharth Shishulkar Jun 24 '18 at 09:51