利用反射式编程编写一个通用的toString()方法

通过Java的反射式编程机制,我们可以通过Field、Constructor以及Method类来获取一个类的域成员、构造函数以及方法的具体信息,以此我们可以编写一个通用的toString()方法,所谓通用,即输入的参数可以接受任何类型的变量,返回描述该对象的所有域成员的字符串。

不过在编程开始之前,我们需要解决几个问题:

  1. 反射机制的默认行为受限于Java的访问控制,为了能够访问一个类的所有域成员,我们需要使用到AccessibleObject类的setAccessible方法来覆盖访问控制。
  2. 如果给定的类对象存在着继承链,我们需要把继承链上的所有类都进行处理。
  3. 一个域成员的数据类型可能已经自定义了toString()的规则,此时我们应该遵循它定义的规则而非强制让它遵循我们的规则,这样增强了程序的鲁棒性,避免出现意想不到的bug。
  4. 如果域成员为数组类型,我们因该依次遍历数据元素,根据元素类型合理选择构造toString()字符串的方法(代码中有相应的注解)。另外,遍历时涉及toString()方法的递归调用,因此可能导致无限递归,需要设置一个标志数组记录已经处理过的对象避免无限递归。

以下将给出代码,代码自带测试主函数:

import java.lang.reflect.AccessibleObject;
import java.lang.reflect.Array;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.util.ArrayList;

/**
 * 一个包含了通用的{@code toString()}方法类.
 *
 * @author szu_dugf
 * @version 1.0 2019-07-09
 */
public class ObjectAnalyzer {

    private ArrayList<Object> visited = new ArrayList<>();

    /**
     * 对于给定的任意对象,将返回一个描述该对象的字符串,该字符串含有该对象所有的域成员.
     * @param obj 给定的一个对象
     * @return 描述该对象的字符串,该字符串包含了对象所属的类名及成员变量的描述
     */
    private String toString(Object obj) {
        if (obj == null)
            return "null";

        if (visited.contains(obj))
            return "...";

        //避免陷入无限循环,确保当前处理的对象不曾加入到处理栈当中
        visited.add(obj);

        Class cl = obj.getClass();

        //如果是字符串,则直接返回
        if (cl == String.class)
            return (String) obj;

        //首先判断传入的对象是否为数组类型
        if (cl.isArray()) {
            StringBuilder r = new StringBuilder(cl.getComponentType() + "[]{");

            for (int i = 0; i < Array.getLength(obj); ++i) {
                if (i > 0)
                    r.append(",");

                //获得数组当中i位置的具体对象元素
                Object val = Array.get(obj, i);
                //getComponentType()能确定数组的类型
                //如果元素类型为基本数据类型,则直接将它添加到字符串描述当中
                //否则调用该对象所属的类,由该类自定义的toString()规则来处理
                if (cl.getComponentType().isPrimitive())
                    r.append(val);
                else
                    r.append(toString(val));
            }

            return r.append("}").toString();
        }

        //如果不是数组类型,先获得该类的名称
        StringBuilder r = new StringBuilder(cl.getName());

        //考虑继承关系,持续调用循环体内容,直到继承链的类已被处理完毕
        do {
            r.append("[");

            Field[] fields = cl.getDeclaredFields();
            //反射机制默认受限于Java访问控制,通过setAccessible()方法覆盖访问控制
            AccessibleObject.setAccessible(fields, true);

            for (Field field : fields) {
                //toString()方法不考虑该类的静态成员变量,因为该变量不属于任何一个具体的实例化对象的域成员
                if (!Modifier.isStatic(field.getModifiers())) {
                    if (!r.toString().endsWith("["))
                        r.append(",");

                    r.append(field.getName()).append("=");

                    try {
                        Class t = field.getType();
                        Object val = field.get(obj);

                        if (t.isPrimitive())
                            r.append(val);
                        else
                            r.append(toString(val));
                    } catch (Exception e) {
                        e.printStackTrace();
                    }
                }
            }

            r.append("]");
            cl = cl.getSuperclass();
        } while (cl != null);

        return r.toString();
    }

    public static void main(String[] args) {
        ArrayList<Integer> squares = new ArrayList<>();

        for (int i = 1; i <= 5; ++i) {
            squares.add(i * i);
        }

        System.out.println(new ObjectAnalyzer().toString(squares));
    }
}

 

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值