[新特性]反射调用支持传递参数的值为null

增加对null参数传递的支持,在传递null参数保持类型信息不丢失
This commit is contained in:
lixiaoyu08 2020-11-05 21:23:26 +08:00
parent f4d357971b
commit 8181519c0d
3 changed files with 112 additions and 78 deletions

View File

@ -0,0 +1,20 @@
package cn.hutool.core.bean;
/**
* 为了解决反射过程中,需要传递null参数,但是会丢失参数类型而设立的包装类
*/
public class NullWrapperBean {
private final Class<?> mClasses;
/**
* @param classes null的类型
*/
public NullWrapperBean(Class<?> classes) {
this.mClasses = classes;
}
public Class<?> getClasses() {
return mClasses;
}
}

View File

@ -1,5 +1,6 @@
package cn.hutool.core.util; package cn.hutool.core.util;
import cn.hutool.core.bean.NullWrapperBean;
import cn.hutool.core.convert.BasicType; import cn.hutool.core.convert.BasicType;
import cn.hutool.core.exceptions.UtilException; import cn.hutool.core.exceptions.UtilException;
import cn.hutool.core.io.FileUtil; import cn.hutool.core.io.FileUtil;
@ -139,15 +140,21 @@ public class ClassUtil {
* @param objects 对象数组如果数组中存在{@code null}元素则此元素被认为是Object类型 * @param objects 对象数组如果数组中存在{@code null}元素则此元素被认为是Object类型
* @return 类数组 * @return 类数组
*/ */
public static Class<?>[] getClasses(Object... objects) { public static Class<?>[] getClasses(Object... objects) {
Class<?>[] classes = new Class<?>[objects.length]; Class<?>[] classes = new Class<?>[objects.length];
Object obj; Object obj;
for (int i = 0; i < objects.length; i++) { for (int i = 0; i < objects.length; i++) {
obj = objects[i]; obj = objects[i];
classes[i] = (null == obj) ? Object.class : obj.getClass(); if (obj instanceof NullWrapperBean) {
} classes[i] = ((NullWrapperBean) obj).getClasses();
return classes; } else if (null == obj) {
} classes[i] = Object.class;
} else {
classes[i] = obj.getClass();
}
}
return classes;
}
/** /**
* 指定类是否与给定的类名相同 * 指定类是否与给定的类名相同

View File

@ -1,6 +1,7 @@
package cn.hutool.core.util; package cn.hutool.core.util;
import cn.hutool.core.annotation.Alias; import cn.hutool.core.annotation.Alias;
import cn.hutool.core.bean.NullWrapperBean;
import cn.hutool.core.collection.CollUtil; import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.convert.Convert; import cn.hutool.core.convert.Convert;
import cn.hutool.core.exceptions.UtilException; import cn.hutool.core.exceptions.UtilException;
@ -864,78 +865,84 @@ public class ReflectUtil {
return invoke(obj, method, args); return invoke(obj, method, args);
} }
/** /**
* 执行方法 * 执行方法
* *
* <p> * <p>
* 对于用户传入参数会做必要检查包括 * 对于用户传入参数会做必要检查包括
* *
* <pre> * <pre>
* 1忽略多余的参数 * 1忽略多余的参数
* 2参数不够补齐默认值 * 2参数不够补齐默认值
* 3传入参数为null但是目标参数类型为原始类型做转换 * 3传入参数为null但是目标参数类型为原始类型做转换
* </pre> * </pre>
* *
* @param <T> 返回对象类型 * @param <T> 返回对象类型
* @param obj 对象如果执行静态方法此值为<code>null</code> * @param obj 对象如果执行静态方法此值为<code>null</code>
* @param method 方法对象方法或static方法都可 * @param method 方法对象方法或static方法都可
* @param args 参数对象 * @param args 参数对象
* @return 结果 * @return 结果
* @throws UtilException 一些列异常的包装 * @throws UtilException 一些列异常的包装
*/ */
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
public static <T> T invoke(Object obj, Method method, Object... args) throws UtilException { public static <T> T invoke(Object obj, Method method, Object... args) throws UtilException {
setAccessible(method); setAccessible(method);
// 检查用户传入参数 // 检查用户传入参数
// 1忽略多余的参数 // 1忽略多余的参数
// 2参数不够补齐默认值 // 2参数不够补齐默认值
// 3传入参数为null但是目标参数类型为原始类型做转换 // 3通过NullWrapperBean传递的参数,会直接赋值null
// 4传入参数类型不对应尝试转换类型 // 4传入参数为null但是目标参数类型为原始类型做转换
final Class<?>[] parameterTypes = method.getParameterTypes(); // 5传入参数类型不对应尝试转换类型
final Object[] actualArgs = new Object[parameterTypes.length]; final Class<?>[] parameterTypes = method.getParameterTypes();
if (null != args) { final Object[] actualArgs = new Object[parameterTypes.length];
for (int i = 0; i < actualArgs.length; i++) { if (null != args) {
if (i >= args.length || null == args[i]) { for (int i = 0; i < actualArgs.length; i++) {
// 越界或者空值 if (i >= args.length || null == args[i]) {
actualArgs[i] = ClassUtil.getDefaultValue(parameterTypes[i]); // 越界或者空值
} else if (false == parameterTypes[i].isAssignableFrom(args[i].getClass())) { actualArgs[i] = ClassUtil.getDefaultValue(parameterTypes[i]);
//对于类型不同的字段尝试转换转换失败则使用原对象类型 } else if (args[i] instanceof NullWrapperBean) {
final Object targetValue = Convert.convert(parameterTypes[i], args[i]); //如果是通过NullWrapperBean传递的null参数,直接赋值null
if (null != targetValue) { actualArgs[i] = null;
actualArgs[i] = targetValue; } else if (!parameterTypes[i].isAssignableFrom(args[i].getClass())) {
} //对于类型不同的字段尝试转换转换失败则使用原对象类型
} else { final Object targetValue = Convert.convert(parameterTypes[i], args[i]);
actualArgs[i] = args[i]; if (null != targetValue) {
} actualArgs[i] = targetValue;
} }
} } else {
actualArgs[i] = args[i];
}
}
}
try { try {
return (T) method.invoke(ClassUtil.isStatic(method) ? null : obj, actualArgs); return (T) method.invoke(ClassUtil.isStatic(method) ? null : obj, actualArgs);
} catch (Exception e) { } catch (Exception e) {
throw new UtilException(e); throw new UtilException(e);
} }
} }
/** /**
* 执行对象中指定方法 * 执行对象中指定方法
* * 如果需要传递的参数为null,请使用NullWrapperBean来传递,不然会丢失类型信息
* @param <T> 返回对象类型 *
* @param obj 方法所在对象 * @param <T> 返回对象类型
* @param methodName 方法名 * @param obj 方法所在对象
* @param args 参数列表 * @param methodName 方法名
* @return 执行结果 * @param args 参数列表
* @throws UtilException IllegalAccessException包装 * @return 执行结果
* @since 3.1.2 * @throws UtilException IllegalAccessException包装
*/ * @see NullWrapperBean
public static <T> T invoke(Object obj, String methodName, Object... args) throws UtilException { * @since 3.1.2
final Method method = getMethodOfObj(obj, methodName, args); */
if (null == method) { public static <T> T invoke(Object obj, String methodName, Object... args) throws UtilException {
throw new UtilException(StrUtil.format("No such method: [{}]", methodName)); final Method method = getMethodOfObj(obj, methodName, args);
} if (null == method) {
return invoke(obj, method, args); throw new UtilException(StrUtil.format("No such method: [{}]", methodName));
} }
return invoke(obj, method, args);
}
/** /**
* 设置方法为可访问私有方法可以被外部调用 * 设置方法为可访问私有方法可以被外部调用