fix typeUtil

This commit is contained in:
Looly 2020-09-06 02:20:15 +08:00
parent 3e05c5b396
commit 784ad3e8f9
14 changed files with 269 additions and 125 deletions

View File

@ -15,7 +15,7 @@
* 【extra 】 SpringUtil增加注册beanpr#174@Gitee
* 【core 】 修改NetUtil.getMacAddress避免空指针issue#1057@Github
* 【core 】 增加EnumItem接口枚举扩展转换增加SPI自定义转换pr#173@Github
* 【core 】 TypeUtil增加getActualTypeMap方法
* 【core 】 TypeUtil增加getActualType增加ActualTypeMapperPool类issue#I1TBWH@Gitee
### Bug修复
* 【core 】 重新整理农历节假日解决一个pr过来的玩笑导致的问题

View File

@ -7,10 +7,8 @@ import cn.hutool.core.bean.copier.provider.MapValueProvider;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.convert.Convert;
import cn.hutool.core.exceptions.UtilException;
import cn.hutool.core.lang.ParameterizedTypeImpl;
import cn.hutool.core.lang.copier.Copier;
import cn.hutool.core.map.MapUtil;
import cn.hutool.core.util.ArrayUtil;
import cn.hutool.core.util.ModifierUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
@ -19,9 +17,7 @@ import cn.hutool.core.util.TypeUtil;
import java.io.Serializable;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.lang.reflect.TypeVariable;
import java.util.Collection;
import java.util.HashSet;
import java.util.Map;
@ -246,24 +242,8 @@ public class BeanCopier<T> implements Copier<T>, Serializable {
// 获取目标字段真实类型
Type fieldType = (null == setterMethod) ? TypeUtil.getType(field) : TypeUtil.getFirstParamType(setterMethod);
if (fieldType instanceof ParameterizedType) {
// 字段类型为泛型参数类型解析对应泛型类型为真实类型类似于List<T> a
final ParameterizedType fieldParameterizedType = (ParameterizedType) fieldType;
Type[] actualTypeArguments = fieldParameterizedType.getActualTypeArguments();
if (TypeUtil.hasTypeVeriable(actualTypeArguments)) {
// 泛型对象中含有未被转换的泛型变量
actualTypeArguments = TypeUtil.getActualTypes(this.destType, field.getDeclaringClass(), fieldParameterizedType.getActualTypeArguments());
if (ArrayUtil.isNotEmpty(actualTypeArguments)) {
// 替换泛型变量为实际类型
fieldType = new ParameterizedTypeImpl(actualTypeArguments, fieldParameterizedType.getOwnerType(), fieldParameterizedType.getRawType());
}
}
} else if (fieldType instanceof TypeVariable) {
// 字段类型为泛型查找其真实类型适用于泛型方法定义于泛型父类类似于T a
fieldType = TypeUtil.getActualType(this.destType, field.getDeclaringClass(), fieldType);
}
fieldType = TypeUtil.getActualType(this.destType ,fieldType);
//
value = valueProvider.value(providerKey, fieldType);
if (null == value && copyOptions.ignoreNullValue) {
continue;// 当允许跳过空时跳过

View File

@ -2023,7 +2023,7 @@ public class CollUtil {
if (null == collection || null == value) {
return collection;
}
if (TypeUtil.isUnknow(elementType)) {
if (TypeUtil.isUnknown(elementType)) {
// 元素类型为空时使用Object类型来接纳所有类型
elementType = Object.class;
}

View File

@ -226,14 +226,14 @@ public class ConverterRegistry implements Serializable {
*/
@SuppressWarnings("unchecked")
public <T> T convert(Type type, Object value, T defaultValue, boolean isCustomFirst) throws ConvertException {
if (TypeUtil.isUnknow(type) && null == defaultValue) {
if (TypeUtil.isUnknown(type) && null == defaultValue) {
// 对于用户不指定目标类型的情况返回原值
return (T) value;
}
if (ObjectUtil.isNull(value)) {
return defaultValue;
}
if (TypeUtil.isUnknow(type)) {
if (TypeUtil.isUnknown(type)) {
type = defaultValue.getClass();
}

View File

@ -23,7 +23,7 @@ public class AtomicReferenceConverter extends AbstractConverter<AtomicReference>
//尝试将值转换为Reference泛型的类型
Object targetValue = null;
final Type paramType = TypeUtil.getTypeArgument(AtomicReference.class);
if(false == TypeUtil.isUnknow(paramType)){
if(false == TypeUtil.isUnknown(paramType)){
targetValue = ConverterRegistry.getInstance().convert(paramType, value);
}
if(null == targetValue){

View File

@ -86,8 +86,8 @@ public class MapConverter extends AbstractConverter<Map<?, ?>> {
Object key;
Object value;
for (Entry<?, ?> entry : srcMap.entrySet()) {
key = TypeUtil.isUnknow(this.keyType) ? entry.getKey() : convert.convert(this.keyType, entry.getKey());
value = TypeUtil.isUnknow(this.valueType) ? entry.getValue() : convert.convert(this.valueType, entry.getValue());
key = TypeUtil.isUnknown(this.keyType) ? entry.getKey() : convert.convert(this.keyType, entry.getKey());
value = TypeUtil.isUnknown(this.valueType) ? entry.getValue() : convert.convert(this.valueType, entry.getValue());
targetMap.put(key, value);
}
}

View File

@ -37,7 +37,7 @@ public class ReferenceConverter extends AbstractConverter<Reference> {
//尝试将值转换为Reference泛型的类型
Object targetValue = null;
final Type paramType = TypeUtil.getTypeArgument(targetType);
if(false == TypeUtil.isUnknow(paramType)){
if(false == TypeUtil.isUnknown(paramType)){
targetValue = ConverterRegistry.getInstance().convert(paramType, value);
}
if(null == targetValue){

View File

@ -359,6 +359,19 @@ public class CalendarUtil {
return StrUtil.builder().append(cal.get(Calendar.YEAR)).append(cal.get(Calendar.MONTH) / 3 + 1).toString();
}
/**
* 获取指定日期字段的最小值例如分钟的最小值是0
*
* @param calendar {@link Calendar}
* @param dateField {@link DateField}
* @return 字段最小值
* @see Calendar#getActualMinimum(int)
* @since 5.4.2
*/
public static int getBeginValue(Calendar calendar, DateField dateField) {
return getBeginValue(calendar, dateField.getValue());
}
/**
* 获取指定日期字段的最小值例如分钟的最小值是0
*
@ -375,6 +388,19 @@ public class CalendarUtil {
return calendar.getActualMinimum(dateField);
}
/**
* 获取指定日期字段的最大值例如分钟的最大值是59
*
* @param calendar {@link Calendar}
* @param dateField {@link DateField}
* @return 字段最大值
* @see Calendar#getActualMaximum(int)
* @since 5.4.2
*/
public static int getEndValue(Calendar calendar, DateField dateField) {
return getEndValue(calendar, dateField.getValue());
}
/**
* 获取指定日期字段的最大值例如分钟的最大值是59
*

View File

@ -1916,6 +1916,18 @@ public class DateUtil extends CalendarUtil {
return Year.of(year).length();
}
/**
* 获得指定月份的总天数
*
* @param month 年份
* @param isLeapYear 是否闰年
* @return
* @since 5.4.2
*/
public static int lengthOfMonth(int month, boolean isLeapYear) {
return java.time.Month.of(month).length(isLeapYear);
}
// ------------------------------------------------------------------------ Private method start
/**

View File

@ -0,0 +1,101 @@
package cn.hutool.core.lang.reflect;
import cn.hutool.core.lang.SimpleCache;
import cn.hutool.core.util.TypeUtil;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.lang.reflect.TypeVariable;
import java.util.HashMap;
import java.util.Map;
/**
* 泛型变量和泛型实际类型映射关系缓存
*
* @author looly
* @since 5.4.2
*/
public class ActualTypeMapperPool {
private static final SimpleCache<Type, Map<Type, Type>> cache = new SimpleCache<>();
/**
* 获取泛型变量和泛型实际类型的对应关系Map
*
* @param type 被解析的包含泛型参数的类
* @return 泛型对应关系Map
*/
public static Map<Type, Type> get(Type type) {
return cache.get(type, () -> createTypeMap(type));
}
/**
* 获得泛型变量对应的泛型实际类型如果此变量没有对应的实际类型返回null
*
* @param type
* @param typeVariable 泛型变量例如T等
* @return 实际类型可能为Class等
*/
public static Type getActualType(Type type, TypeVariable<?> typeVariable) {
final Map<Type, Type> typeTypeMap = get(type);
Type result = typeTypeMap.get(typeVariable);
while (result instanceof TypeVariable) {
result = typeTypeMap.get(result);
}
return result;
}
/**
* 获取指定泛型变量对应的真实类型<br>
* 由于子类中泛型参数实现和父类接口中泛型定义位置是一一对应的因此可以通过对应关系找到泛型实现类型<br>
*
* @param type 真实类型所在类此类中记录了泛型参数对应的实际类型
* @param typeVariables 泛型变量需要的实际类型对应的泛型参数
* @return 给定泛型参数对应的实际类型如果无对应类型对应位置返回null
*/
public static Type[] getActualTypes(Type type, Type... typeVariables) {
// 查找方法定义所在类或接口中此泛型参数的位置
final Type[] result = new Type[typeVariables.length];
for (int i = 0; i < typeVariables.length; i++) {
result[i] = (typeVariables[i] instanceof TypeVariable)
? getActualType(type, (TypeVariable<?>) typeVariables[i])
: typeVariables[i];
}
return result;
}
/**
* 创建类中所有的泛型变量和泛型实际类型的对应关系Map
*
* @param type 被解析的包含泛型参数的类
* @return 泛型对应关系Map
*/
private static Map<Type, Type> createTypeMap(Type type) {
final Map<Type, Type> typeMap = new HashMap<>();
// 按继承层级寻找泛型变量和实际类型的对应关系
// 在类中对应关系分为两类
// 1. 父类定义变量子类标注实际类型
// 2. 父类定义变量子类继承这个变量让子类的子类去标注以此类推
// 此方法中我们将每一层级的对应关系全部加入到Map中查找实际类型的时候根据传入的泛型变量
// 找到对应关系如果对应的是继承的泛型变量则递归继续找直到找到实际或返回null为止
// 如果传入的非Class例如TypeReference获取到泛型参数中实际的泛型对象类继续按照类处理
while (null != type) {
final ParameterizedType parameterizedType = TypeUtil.toParameterizedType(type);
if(null == parameterizedType){
break;
}
final Type[] typeArguments = parameterizedType.getActualTypeArguments();
final Class<?> rawType = (Class<?>) parameterizedType.getRawType();
final Type[] typeParameters = rawType.getTypeParameters();
for (int i = 0; i < typeParameters.length; i++) {
typeMap.put(typeParameters[i], typeArguments[i]);
}
type = rawType;
}
return typeMap;
}
}

View File

@ -0,0 +1,7 @@
/**
* 提供反射相关功能对象和类
*
* @author looly
* @since 5.4.2
*/
package cn.hutool.core.lang.reflect;

View File

@ -1,6 +1,7 @@
package cn.hutool.core.util;
import cn.hutool.core.map.TableMap;
import cn.hutool.core.lang.ParameterizedTypeImpl;
import cn.hutool.core.lang.reflect.ActualTypeMapperPool;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
@ -8,6 +9,7 @@ import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.lang.reflect.TypeVariable;
import java.lang.reflect.WildcardType;
import java.util.Map;
/**
* 针对 {@link Type} 的工具类封装<br>
@ -281,93 +283,6 @@ public class TypeUtil {
return result;
}
/**
* 获取泛型变量和真实类型的对应表使用{@link TableMap}表示key不会重复<br>
* 由于子类中泛型参数实现和父类接口中泛型定义位置是一一对应的因此可以通过对应关系找到泛型实现类型<br>
* 使用此方法注意
*
* <pre>
* 1. typeDefineClass必须是clazz的父类或者clazz实现的接口
* </pre>
*
* @param actualType 真实类型所在类此类中记录了泛型参数对应的实际类型
* @param typeDefineClass 泛型变量声明所在类或接口此类中定义了泛型类型
* @return 给定泛型参数对应的实际类型如果无对应类型返回null
* @since 5.4.1
*/
public static TableMap<String, Type> getActualTypeMap(Type actualType, Class<?> typeDefineClass) {
if (false == typeDefineClass.isAssignableFrom(getClass(actualType))) {
throw new IllegalArgumentException("Parameter [superClass] must be assignable from [clazz]");
}
// 泛型参数标识符列表
final TypeVariable<?>[] typeVars = typeDefineClass.getTypeParameters();
if (ArrayUtil.isEmpty(typeVars)) {
return new TableMap<>(0);
}
// 实际类型列表
final Type[] actualTypeArguments = TypeUtil.getTypeArguments(actualType);
if (ArrayUtil.isEmpty(actualTypeArguments)) {
return new TableMap<>(0);
}
return new TableMap<>(ArrayUtil.map(typeVars, String.class, TypeVariable::getName), actualTypeArguments);
}
/**
* 获取指定泛型变量对应的真实类型<br>
* 由于子类中泛型参数实现和父类接口中泛型定义位置是一一对应的因此可以通过对应关系找到泛型实现类型<br>
* 使用此方法注意
*
* <pre>
* 1. superClass必须是clazz的父类或者clazz实现的接口
* 2. typeVariable必须在superClass中声明
* </pre>
*
* @param actualType 真实类型所在类此类中记录了泛型参数对应的实际类型
* @param typeDefineClass 泛型变量声明所在类或接口此类中定义了泛型类型
* @param typeVariables 泛型变量需要的实际类型对应的泛型参数
* @return 给定泛型参数对应的实际类型如果无对应类型返回null
* @since 4.5.7
*/
public static Type[] getActualTypes(Type actualType, Class<?> typeDefineClass, Type... typeVariables) {
final TableMap<String, Type> tableMap = getActualTypeMap(actualType, typeDefineClass);
// 查找方法定义所在类或接口中此泛型参数的位置
final Type[] result = new Type[typeVariables.length];
for (int i = 0; i < typeVariables.length; i++) {
result[i] = (typeVariables[i] instanceof TypeVariable)
? tableMap.get(((TypeVariable<?>) typeVariables[i]).getName())
: typeVariables[i];
}
return result;
}
/**
* 获取指定泛型变量对应的真实类型<br>
* 由于子类中泛型参数实现和父类接口中泛型定义位置是一一对应的因此可以通过对应关系找到泛型实现类型<br>
* 使用此方法注意
*
* <pre>
* 1. superClass必须是clazz的父类或者clazz实现的接口
* 2. typeVariable必须在superClass中声明
* </pre>
*
* @param actualType 真实类型所在类此类中记录了泛型参数对应的实际类型
* @param typeDefineClass 泛型变量声明所在类或接口此类中定义了泛型类型
* @param typeVariable 泛型变量需要的实际类型对应的泛型参数
* @return 给定泛型参数对应的实际类型
* @since 4.5.2
*/
public static Type getActualType(Type actualType, Class<?> typeDefineClass, Type typeVariable) {
final Type[] types = getActualTypes(actualType, typeDefineClass, typeVariable);
if (ArrayUtil.isNotEmpty(types)) {
return types[0];
}
return null;
}
/**
* 是否未知类型<br>
* type为null或者{@link TypeVariable} 都视为未知类型
@ -376,7 +291,7 @@ public class TypeUtil {
* @return 是否未知类型
* @since 4.5.2
*/
public static boolean isUnknow(Type type) {
public static boolean isUnknown(Type type) {
return null == type || type instanceof TypeVariable;
}
@ -395,4 +310,84 @@ public class TypeUtil {
}
return false;
}
/**
* 获取泛型变量和泛型实际类型的对应关系Map
*
* @param clazz 被解析的包含泛型参数的类
* @return 泛型对应关系Map
*/
public static Map<Type, Type> getTypeMap(Class<?> clazz) {
return ActualTypeMapperPool.get(clazz);
}
/**
* 获得泛型字段对应的泛型实际类型如果此变量没有对应的实际类型返回null
*
* @param type 实际类型明确的类
* @param field 字段
* @return 实际类型可能为Class等
*/
public static Type getActualType(Type type, Field field) {
if(null == field){
return null;
}
return getActualType(ObjectUtil.defaultIfNull(type, field.getDeclaringClass()), field.getGenericType());
}
/**
* 获得泛型变量对应的泛型实际类型如果此变量没有对应的实际类型返回null
* 此方法可以处理
*
* @param type
* @param typeVariable 泛型变量例如T等
* @return 实际类型可能为Class等
*/
public static Type getActualType(Type type, Type typeVariable) {
if (typeVariable instanceof ParameterizedType) {
return getActualType(type, (ParameterizedType)typeVariable);
}
if (typeVariable instanceof TypeVariable) {
return ActualTypeMapperPool.getActualType(type, (TypeVariable<?>) typeVariable);
}
// 没有需要替换的泛型变量原样输出
return typeVariable;
}
/**
* 获得泛型变量对应的泛型实际类型如果此变量没有对应的实际类型返回null
* 此方法可以处理
*
* @param type
* @param parameterizedType 泛型变量例如List&lt;T&gt;
* @return 实际类型可能为Class等
*/
public static Type getActualType(Type type, ParameterizedType parameterizedType) {
// 字段类型为泛型参数类型解析对应泛型类型为真实类型类似于List<T> a
Type[] actualTypeArguments = parameterizedType.getActualTypeArguments();
// 泛型对象中含有未被转换的泛型变量
if (TypeUtil.hasTypeVeriable(actualTypeArguments)) {
actualTypeArguments = getActualTypes(type , parameterizedType.getActualTypeArguments());
if (ArrayUtil.isNotEmpty(actualTypeArguments)) {
// 替换泛型变量为实际类型例如List<T>变为List<String>
parameterizedType = new ParameterizedTypeImpl(actualTypeArguments, parameterizedType.getOwnerType(), parameterizedType.getRawType());
}
}
return parameterizedType;
}
/**
* 获得泛型变量对应的泛型实际类型如果此变量没有对应的实际类型返回null
*
* @param type
* @param typeVariables 泛型变量数组例如T等
* @return 实际类型数组可能为Class等
*/
public static Type[] getActualTypes(Type type, Type... typeVariables) {
return ActualTypeMapperPool.getActualTypes(type, typeVariables);
}
}

View File

@ -446,4 +446,28 @@ public class BeanUtilTest {
BeanUtil.setProperty(resultMap, "codeList[0].name", "张三");
Console.log(resultMap);
}
@Test
public void beanCopyTest(){
final Station station = new Station();
station.setId(123456L);
final Station station2 = new Station();
BeanUtil.copyProperties(station, station2);
Assert.assertEquals(new Long(123456L), station2.getId());
}
public static class Station extends Tree<Station, Long> {
}
public static class Tree<E, T> extends Entity<T> {
}
@Data
public static class Entity<T>{
private T id;
}
}

View File

@ -60,24 +60,23 @@ public class TypeUtilTest {
@Test
public void getActualTypesTest(){
final Type idType = TypeUtil.getActualType(
Station.class,
Tree.class,
TypeUtil.getFieldType(Station.class, "id"));
// 测试多层级泛型参数是否能获取成功
Type idType = TypeUtil.getActualType(Level3.class,
ReflectUtil.getField(Level3.class, "id"));
Assert.assertEquals(Long.class, idType);
}
public static class Station extends Tree<Station, Long>{
public static class Level3 extends Level2<Level3>{
}
public static class Tree<E, T> extends Entity<T>{
public static class Level2<E> extends Level1<Long>{
}
@Data
public static class Entity<T>{
public static class Level1<T>{
private T id;
}