I have a generics class, Foo
. In a method of Foo
, I want to get the class instance of type T
, but I just can\'t call T.
Here is a working solution:
@SuppressWarnings("unchecked")
private Class<T> getGenericTypeClass() {
try {
String className = ((ParameterizedType) getClass().getGenericSuperclass()).getActualTypeArguments()[0].getTypeName();
Class<?> clazz = Class.forName(className);
return (Class<T>) clazz;
} catch (Exception e) {
throw new IllegalStateException("Class is not parametrized with generic type!!! Please use extends <> ");
}
}
NOTES: Can be used only as superclass
Child extends Generic<Integer>
)OR
new Generic<Integer>() {};
) There is a small loophole however: if you define your Foo
class as abstract.
That would mean you have to instantiate you class as:
Foo<MyType> myFoo = new Foo<MyType>(){};
(Note the double braces at the end.)
Now you can retrieve the type of T
at runtime:
Type mySuperclass = myFoo.getClass().getGenericSuperclass();
Type tType = ((ParameterizedType)mySuperclass).getActualTypeArguments()[0];
Note however that mySuperclass
has to be the superclass of the class definition actually defining the final type for T
.
It is also not very elegant, but you have to decide whether you prefer new Foo<MyType>(){}
or new Foo<MyType>(MyType.class);
in your code.
For example:
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.ArrayDeque;
import java.util.Deque;
import java.util.NoSuchElementException;
/**
* Captures and silently ignores stack exceptions upon popping.
*/
public abstract class SilentStack<E> extends ArrayDeque<E> {
public E pop() {
try {
return super.pop();
}
catch( NoSuchElementException nsee ) {
return create();
}
}
public E create() {
try {
Type sooper = getClass().getGenericSuperclass();
Type t = ((ParameterizedType)sooper).getActualTypeArguments()[ 0 ];
return (E)(Class.forName( t.toString() ).newInstance());
}
catch( Exception e ) {
return null;
}
}
}
Then:
public class Main {
// Note the braces...
private Deque<String> stack = new SilentStack<String>(){};
public static void main( String args[] ) {
// Returns a new instance of String.
String s = stack.pop();
System.out.printf( "s = '%s'\n", s );
}
}
I wanted to pass T.class to a method which make use of Generics
The method readFile reads a .csv file specified by the fileName with fullpath. There can be csv files with different contents hence i need to pass the model file class so that i can get the appropriate objects. Since this is reading csv file i wanted to do in a generic way. For some reason or other none of the above solutions worked for me. I need to use
Class<? extends T> type
to make it work. I use opencsv library for parsing the CSV files.
private <T>List<T> readFile(String fileName, Class<? extends T> type) {
List<T> dataList = new ArrayList<T>();
try {
File file = new File(fileName);
Reader reader = new BufferedReader(new InputStreamReader(new FileInputStream(file)));
Reader headerReader = new BufferedReader(new InputStreamReader(new FileInputStream(file)));
CSVReader csvReader = new CSVReader(headerReader);
// create csv bean reader
CsvToBean<T> csvToBean = new CsvToBeanBuilder(reader)
.withType(type)
.withIgnoreLeadingWhiteSpace(true)
.build();
dataList = csvToBean.parse();
}
catch (Exception ex) {
logger.error("Error: ", ex);
}
return dataList;
}
This is how the readFile method is called
List<RigSurfaceCSV> rigSurfaceCSVDataList = readSurfaceFile(surfaceFileName, RigSurfaceCSV.class);
A better route than the Class the others suggested is to pass in an object that can do what you would have done with the Class, e.g., create a new instance.
interface Factory<T> {
T apply();
}
<T> void List<T> make10(Factory<T> factory) {
List<T> result = new ArrayList<T>();
for (int a = 0; a < 10; a++)
result.add(factory.apply());
return result;
}
class FooFactory<T> implements Factory<Foo<T>> {
public Foo<T> apply() {
return new Foo<T>();
}
}
List<Foo<Integer>> foos = make10(new FooFactory<Integer>());
The short answer is, that there is no way to find out the runtime type of generic type parameters in Java. I suggest reading the chapter about type erasure in the Java Tutorial for more details.
A popular solution to this is to pass the Class
of the type parameter into the constructor of the generic type, e.g.
class Foo<T> {
final Class<T> typeParameterClass;
public Foo(Class<T> typeParameterClass) {
this.typeParameterClass = typeParameterClass;
}
public void bar() {
// you can access the typeParameterClass here and do whatever you like
}
}
A standard approach/workaround/solution is to add a class
object to the constructor(s), like:
public class Foo<T> {
private Class<T> type;
public Foo(Class<T> type) {
this.type = type;
}
public Class<T> getType() {
return type;
}
public T newInstance() {
return type.newInstance();
}
}