Java - Runtime获得Generic Type的Type Argument

Java的Type Erasure是一个我个人非常不喜欢的“特性”。在Runtime无法获得Generic type的Generic Type Argument会使很多代码看上去非常冗余。我们举个例子。
假设要实现如下的功能:class Factory<T>有一个方法createInstance,其parameterized type需要能通过这个方法创建type T对应的实例。比如new Factory<String>().createInstance()需要返回一个String instance.

class Factory {
    public Object createInstance() {
        // create an instance of T
    }
    // other stuffs ...
}

看上去很简单的需求在Java里却几乎没办法实现。因为Java的Type Erasure使你无法在createInstance这个方法中在运行时获取type parameter T的class object。所以一般我们只能按照下面这样去实现这个方法:

class Factory {
    // 传入class object
    public Object createInstance(Class clazz) {
        clazz.newInstnace();
    }
    // other stuffs ...
}

new Factory().createInstance(Integer.class);

这里Integer.class这个参数看着非常不舒服,因为明明这个factory对象已经从Generic Type Argument中获得了String这个名字,但我们不得不重复一遍。另外,Java的ArrayList的toArray()方法就是一个实际的例子,你必须要这样去用它:

List aList = new ArrayList();
aList.toArray(new Integer[0]);

最近习得了一个trick能够利用Java类型继承获取Generic Parameter Type,记录一下也正好可以分享给碰到同样问题的朋友。

Java的类型继承有一个特性就是:如果父类是一个Generic的Abstract Class,同时在子类中指定了Generic Type Argument,那么通过访问父类的class可以获取子类的Generic Type Argument的Type...听上去有点绕,上代码:

import java.lang.reflect.ParameterizedType;

public class Main {
    
    static class Person {
        public String toString() {
            return "I'm a person";
        }
    }

    static class PersonDAO extends TypeExtractor {
    }

    static abstract class TypeExtractor {
        public T createInstance() throws Exception {
            Class clazz = getBeanClass();
            return clazz.newInstance();
        }

        @SuppressWarnings("unchecked")
        public Class getBeanClass() {
            return (Class)
                   ((ParameterizedType)(getClass().getGenericSuperclass()))
                   .getActualTypeArguments()[0];
        }
    }

    public static void main(String[] args) throws Exception {
        PersonDAO dao = new PersonDAO();
        Person p = dao.createInstance();
        System.out.println(p.toString());
    }
}

可以看到在所有继承自TypeExtractor类型并且指定Type parameter的类型都可以通过getBeanClass这个方法获得当前的Type parameter。然后就可以调用newInstance方法构造实例。
简单解释一些getBeanClass方法,它首先获得当前的class,也就是getClass(),这个class是指定了Type argument的子类的class对象,然后调用getGenericSuperclass获得TypeExtractor这个类型,Java会将子类的Generic Argument Type存放在这个类型的TypeArguments里,因为我们只有一个Type parameter,所以直接取第一个即得到了我们想要的class。

不得不说这是一个奇技淫巧,它只能适用于一层继承关系的类中,所以需要谨慎使用。但确实可以让你的code好看很多。

Reference:

http://blog.xebia.com/2009/02/07/acessing-generic-types-at-runtime-in-java/

Comments (2)

  1. kzdhcster  / Reply

    主题 simple dark 不更新了吗,博主? :?:

  2. Justice  / Reply

    @kzdhcster
    应该是。

Leave a Reply

Allowed Tags - You may use these HTML tags and attributes in your comment.

<a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>

 :wink:  :-|  :-x  :twisted:  :)  8-O  :(  :roll:  :-P  :oops:  :-o  :mrgreen:  :lol:  :idea:  :-D  :evil:  :cry:  8-)  :arrow:  :-?  :?:  :!:

Pingbacks (0)

› No pingbacks yet.