How do I get a class instance of generic type T?

前端 未结 22 1233
猫巷女王i
猫巷女王i 2020-11-21 11:03

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.

相关标签:
22条回答
  • 2020-11-21 11:42

    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

    1. Has to be extended with typed class (Child extends Generic<Integer>)

    OR

    1. Has to be created as anonymous implementation (new Generic<Integer>() {};)
    0 讨论(0)
  • 2020-11-21 11:43

    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 );
        }
    }
    
    0 讨论(0)
  • 2020-11-21 11:43

    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);
    
    0 讨论(0)
  • 2020-11-21 11:44

    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>());
    
    0 讨论(0)
  • 2020-11-21 11:46

    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
        }
    }
    
    0 讨论(0)
  • 2020-11-21 11:48

    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();
        }
     }
    
    0 讨论(0)
提交回复
热议问题