Java 高级教程系列 - 构造器反射

Java Tutorial for Language Adavanced - Constructor Reflection

Posted by zihengCat on 2019-03-26

构造器反射(Constructor Reflection)

Java 类java.lang.reflect.Constructor实例是对类构造器(Constructor)的反射。Constructor类继承自通用抽象父类Executable,其自身是不可变(Immutable)类。

public final class Constructor<T>
extends Executable

代码清单:Constructor类声明

Class类提供了获取类构造器的方法,如下所示。

Constructor<?>[] getConstructors()
Constructor<?>[] getDeclaredConstructors()
Constructor<T> getConstructor(Class<?>... parameterTypes)
Constructor<T> getDeclaredConstructor(Class<?>... parameterTypes)

代码清单:构造器(Constructor)反射 API

方法 描述
Constructor<?>[] getConstructors() 返回目标类中所有公开的构造方法。
Constructor<?>[] getDeclaredConstructors() 返回目标类中所有构造方法。
Constructor<T> getConstructor(Class<?>... parameterTypes) 根据参数类型获取公开构造方法。
Constructor<T> getDeclaredConstructor(Class<?>... parameterTypes) 根据参数类型获取构造方法。

表:构造器(Constructor)反射 API 表

getConstructors()方法返回目标类中所有公开(public)构造方法,返回值为构造器对象数组Constructor<?>[]

getDeclaredConstructors()方法返回目标类中所有(publicprivateprotecteddefault)构造方法,返回值为构造器对象数组Constructor<?>[]

getConstructor(Class<?>... parameterTypes)getDeclaredConstructor(Class<?>... parameterTypes)方法根据构造方法名与参数类型取得目标构造方法对象Constructor,如果构造方法不存在,则抛出NoSuchMethodException异常。

import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.lang.reflect.Parameter;
public class Main {
    public static void main(String[] args) {
        for (Constructor c : TestClass.class.getDeclaredConstructors()) {
            System.out.printf(
                /*
                 * Modifier
                 * ConstructorName(Parameter arg0, Parameter arg1, ...)
                 * throws Exception
                 */
                "%s %s %s(%s) %s\n",
                Modifier.toString(c.getModifiers()),
                " ",
                c.getName(),
                getParameterString(c),
                getExceptionString(c).equals("") ?
                getExceptionString(c) : "throws " + getExceptionString(c)
            );
        }
    }
    public static String getParameterString(Constructor c) {
        StringBuilder sb = new StringBuilder();
        for (Parameter p : c.getParameters()) {
            sb.append(
                p.getType().getSimpleName()
            );
            sb.append(" ");
            sb.append(
                p.getName()
            );
            sb.append(", ");
        }
        return sb.toString();
    }
    public static String getExceptionString(Constructor c) {
        StringBuilder sb = new StringBuilder();
        for (Class<?> cls : c.getExceptionTypes()) {
            sb.append(cls.getSimpleName());
            sb.append(", ");
        }
        return sb.toString();
    }
}
class TestSuperClass {
    public TestSuperClass() {
        // ...
    }
    public void sayHello() {
        System.out.println("Hello!");
    }
}
class TestClass<T> extends TestSuperClass {
    private static Integer _id = 0;
    public TestClass() throws Exception {
        _id++;
        System.out.println("Public constructor Void called: " + _id);
    }
    public TestClass(Integer i) {
        _id = i;
        System.out.println("Public constructor Integer called: " + _id);
    }
    public TestClass(T t) {
        // ...
    }
    private TestClass(Double d) {
        _id = d.intValue();
        System.out.println("Private constructor Double called: " + _id);
    }
}

代码清单:反射获取类构造器(Constructor)信息

反射生成类实例

对于构造器(Constructor)反射,我们主要关心如何调用构造方法创建类实例。我们可以使用反射动态创建类实例,有以下两种方式。

  • 使用无参构造函数

  • 使用有参构造函数

如果已有一枚Class对象,我们可以使用newInstance()方法创建类实例,该方法参数列表置空,等同于使用new操作符调用类无参构造方法创建类实例。

public T newInstance(Object... initargs)
throws InstantiationException,
       IllegalAccessException,
       IllegalArgumentException,
       InvocationTargetException

代码清单:newInstance()方法声明

...
    public static void main(String[] args) {
        try {
            /* Create class instance using `new` operator */
            TestClass t1 = new TestClass();
            System.out.println(t1);
            /* Create class instance using `Constructor` reflection
             * without arguments.
             */
            TestClass t2 = TestClass.class
                                    .getDeclaredConstructor()
                                    .newInstance();
            System.out.println(t2);
            /* Create class instance using `Constructor` reflection
             * with arguments.
             */
            TestClass t3 = TestClass.class
                                    .getDeclaredConstructor(Integer.class)
                                    .newInstance(100);
            System.out.println(t3);
            /* Create class instance using `Constructor` reflection
             * with private constructor.
             */
            Constructor<TestClass> doubleConstructor =
                TestClass.class.getDeclaredConstructor(Double.class);
            doubleConstructor.setAccessible(true);
            TestClass t4 = doubleConstructor
                .newInstance(Double.valueOf(1.414));
            System.out.println(t4);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
...

代码清单:使用newInstance()方法动态创建对象

另外,在JDK 9之后,Class类的newInstance()方法被弃用(Deprecated),因其无法很好捕捉无参构造时发生的异常,我们应该使用Constructor.newInstance()方法替换。

参考资料