Home  >  Q&A  >  body text

java - 关于泛型和反射的代码错误(cannot select from a type variable)

问题1:
想写个requestInfo的toString方法,把所有的成员变量都打印出来,子类就不用每次都写个toString方法了,但是父类怎么获取子类成员变量的值?

public class RequestInfo
{
    public String toString()
    {
        StringBuilder sb = new StringBuilder();
        Field[] fields = this.getClass().getDeclaredFields();
        for(Field field : fields)
        {
            sb.append(field.getName(), " = ", (这里怎么获取属性值?), ";");
        }
        return "";
    }
}

问题2
下面那个类P怎么实例化,也没懂错误的原因,用P.getClass()还是不行

public abstract class AbstractService<Q extends RequestInfo, P extends ResponseInfo>
{
    public static final Logger LOGGER = LoggerFactory.getLogger(AbstractService.class);

    private String logTag;

    private P respBean;

    public P execute(Q reqBean)
    {
        init();
        LOGGER.info(StringUtil.appendStr("Request : {}, req = {}", logTag, reqBean.toString()));

        try
        {
            if (checkInput(reqBean))
            {
                handle(reqBean, respBean);
            }
            else
            {
                throw new Exception(StringUtil.appendStr(logTag, " check input param invalid"));
            }
        }
        catch (Exception e)
        {
            LOGGER.error(StringUtil.appendStr(logTag, " Exception: "), e);
        }
        return respBean;
    }

    protected void init()
    {
        logTag = getClass().getSimpleName();
        respBean =P.class.newInsance();//这里报错,cannot select from a type variable
    }

    protected boolean checkInput(Q reqBean)
    {
        return true;
    }

    protected abstract void handle(Q reqBean, P respBean)
            throws Exception;
}
迷茫迷茫2743 days ago1107

reply all(3)I'll reply

  • PHPz

    PHPz2017-04-18 10:53:04

    Generics have been erased after compilation. The jvm cannot see the generic information at all. This is due to historical reasons, so the p.getClass you mentioned cannot exist

    The first question is a good idea, but as far as I know it cannot be implemented. The object cannot know the status of the subclass. Java's polymorphic mechanism can only query methods from the parent class or parent interface.

    I guess the first question you think you can get the member variables of a subclass is that once the toString method of the subclass is executed after inheritance, your own this will also be called, which is wrong. At run time, the jvm will obtain this method from the object space of the parent class and execute it. So, no matter what, they are just member variables of the parent class

    I made a mistake in the above italicized part. After running the IDE to test it, I found that I was wrong about what I understood before. I hope it didn’t cause trouble to the questioner.
    The code posted below can loop to obtain all variables from the subclass to the parent class. Hope it helps

    public String toString() {
            StringBuilder sb = new StringBuilder();
            Class clazz = this.getClass();
            while(clazz.getSuperclass() != null){
                Field[] fields = clazz.getDeclaredFields();
                try {
                    for (Field field : fields) {
                        field.setAccessible(true);
                        sb.append(field.getName()).append("=").append(field.get(this))
                            .append("\n");
                    }
                } catch (Exception e) {
                    e.printStackTrace();
                }
                clazz = clazz.getSuperclass();
            }
            
            return sb.toString();
        }

    reply
    0
  • 怪我咯

    怪我咯2017-04-18 10:53:04

    Reflection tool class

    package cn.hylexus.app.util;
    
    import java.lang.reflect.Field;
    import java.lang.reflect.ParameterizedType;
    import java.lang.reflect.Type;
    import java.util.ArrayList;
    import java.util.Arrays;
    import java.util.List;
    
    public class ReflectionUtils {
    
        public static List<Field> getFields(Class<?> clz) {
            List<Field> ret = new ArrayList<>();
            for (Class<?> c = clz; c != Object.class; c = c.getSuperclass()) {
                Field[] fields = c.getDeclaredFields();
                ret.addAll(Arrays.asList(fields));
            }
            return ret;
        }
    
        /**
         * @param cls
         *            子类对应的Class
         * @param index
         *            子类继承父类时传入的索引,从0开始
         * @return
         */
        public static Class<?> getSuperClassGenericType(Class<?> cls, int index) {
            if (index < 0)
                return null;
    
            Type type = cls.getGenericSuperclass();
            if (!(type instanceof ParameterizedType))
                return null;
    
            ParameterizedType parameterizedType = (ParameterizedType) type;
            Type[] typeArguments = parameterizedType.getActualTypeArguments();
            if (typeArguments == null || typeArguments.length == 0 || index > typeArguments.length - 1)
                return null;
    
            Type t = typeArguments[index];
            if (!(t instanceof Class)) {
                return null;
            }
            return (Class<?>) t;
        }
    
        public static Class<?> getSuperClassGenericType(Class<?> cls) {
            return getSuperClassGenericType(cls, 0);
        }
    }

    Question 1

    public class RequestInfo {
    
        @Override
        public String toString() {
            StringBuilder sb = new StringBuilder();
            //可以拿到多层次基础的属性
            List<Field> fields = ReflectionUtils.getFields(this.getClass());
            for (Field f : fields) {
                f.setAccessible(true);
                try {
                    sb.append(f.getName()).append("=").append(f.get(this)).append("\n");
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
            return sb.toString();
        }
    }

    Question 2

        @SuppressWarnings("unchecked")
        protected void init() {
            logTag = getClass().getSimpleName();
            try {
                //这里可以拿到动态绑定的Class信息
                Class<?> clz = ReflectionUtils.getSuperClassGenericType(this.getClass(), 1);
                respBean = (P) clz.newInstance();
            } catch (Exception e) {
                e.printStackTrace();
            }
        }

    reply
    0
  • 怪我咯

    怪我咯2017-04-18 10:53:04

    The first question can be solved using commons-beanutils.

    reply
    0
  • Cancelreply