This commit is contained in:
Looly
2024-08-23 12:06:06 +08:00
parent 3ddad8ff6c
commit e185097ce9
27 changed files with 560 additions and 289 deletions

View File

@@ -36,7 +36,7 @@ public class ConcurrentLinkedHashSet<E> extends SetFromMap<E> {
* 构造 * 构造
*/ */
public ConcurrentLinkedHashSet() { public ConcurrentLinkedHashSet() {
super(new ConcurrentLinkedHashMap.Builder<E, Boolean>().build()); super(new ConcurrentLinkedHashMap.Builder<E, Boolean>().maximumWeightedCapacity(64).build());
} }
/** /**
@@ -46,7 +46,10 @@ public class ConcurrentLinkedHashSet<E> extends SetFromMap<E> {
* @param initialCapacity 初始大小 * @param initialCapacity 初始大小
*/ */
public ConcurrentLinkedHashSet(final int initialCapacity) { public ConcurrentLinkedHashSet(final int initialCapacity) {
super(new ConcurrentLinkedHashMap.Builder<E, Boolean>().initialCapacity(initialCapacity).build()); super(new ConcurrentLinkedHashMap.Builder<E, Boolean>()
.initialCapacity(initialCapacity)
.maximumWeightedCapacity(initialCapacity)
.build());
} }
/** /**
@@ -58,6 +61,7 @@ public class ConcurrentLinkedHashSet<E> extends SetFromMap<E> {
public ConcurrentLinkedHashSet(final int initialCapacity, final int concurrencyLevel) { public ConcurrentLinkedHashSet(final int initialCapacity, final int concurrencyLevel) {
super(new ConcurrentLinkedHashMap.Builder<E, Boolean>() super(new ConcurrentLinkedHashMap.Builder<E, Boolean>()
.initialCapacity(initialCapacity) .initialCapacity(initialCapacity)
.maximumWeightedCapacity(initialCapacity)
.concurrencyLevel(concurrencyLevel) .concurrencyLevel(concurrencyLevel)
.build()); .build());
} }

View File

@@ -56,7 +56,7 @@ public abstract class AbstractConverter implements Converter, Serializable {
// 尝试强转 // 尝试强转
if (targetClass.isInstance(value)) { if (targetClass.isInstance(value)) {
// 除Map外已经是目标类型不需要转换Map类型涉及参数类型需要单独转换 // 除Map外已经是目标类型不需要转换Map类型涉及参数类型需要单独转换
return CastUtil.castTo(targetClass, value); return value;
} }
return convertInternal(targetClass, value); return convertInternal(targetClass, value);
} }

View File

@@ -17,19 +17,13 @@
package org.dromara.hutool.core.convert; package org.dromara.hutool.core.convert;
import org.dromara.hutool.core.bean.BeanUtil; import org.dromara.hutool.core.bean.BeanUtil;
import org.dromara.hutool.core.bean.RecordUtil; import org.dromara.hutool.core.convert.impl.BeanConverter;
import org.dromara.hutool.core.convert.impl.*;
import org.dromara.hutool.core.lang.Opt; import org.dromara.hutool.core.lang.Opt;
import org.dromara.hutool.core.reflect.ConstructorUtil;
import org.dromara.hutool.core.reflect.TypeReference; import org.dromara.hutool.core.reflect.TypeReference;
import org.dromara.hutool.core.reflect.TypeUtil; import org.dromara.hutool.core.reflect.TypeUtil;
import org.dromara.hutool.core.reflect.kotlin.KClassUtil;
import org.dromara.hutool.core.util.ObjUtil; import org.dromara.hutool.core.util.ObjUtil;
import java.lang.reflect.Type; import java.lang.reflect.Type;
import java.util.Collection;
import java.util.Date;
import java.util.Map;
import java.util.Optional; import java.util.Optional;
/** /**
@@ -164,7 +158,7 @@ public class CompositeConverter extends RegisterConverter {
} }
// 特殊类型转换包括Collection、Map、强转、Array等 // 特殊类型转换包括Collection、Map、强转、Array等
final T result = convertSpecial(type, rawType, value, defaultValue); final T result = (T) SpecialConverter.getInstance().convert(type, rawType, value);
if (null != result) { if (null != result) {
return result; return result;
} }
@@ -177,101 +171,4 @@ public class CompositeConverter extends RegisterConverter {
// 无法转换 // 无法转换
throw new ConvertException("Can not convert from {}: [{}] to [{}]", value.getClass().getName(), value, type.getTypeName()); throw new ConvertException("Can not convert from {}: [{}] to [{}]", value.getClass().getName(), value, type.getTypeName());
} }
// ----------------------------------------------------------- Private method start
/**
* 特殊类型转换<br>
* 包括:
*
* <pre>
* Collection
* Map
* 强转(无需转换)
* 数组
* </pre>
*
* @param <T> 转换的目标类型(转换器转换到的类型)
* @param type 类型
* @param rawType 原始类型
* @param value 值
* @param defaultValue 默认值
* @return 转换后的值
*/
@SuppressWarnings("unchecked")
private <T> T convertSpecial(final Type type, final Class<T> rawType, final Object value, final T defaultValue) {
if (null == rawType) {
return null;
}
// 日期、java.sql中的日期以及自定义日期统一处理
if (Date.class.isAssignableFrom(rawType)) {
return DateConverter.INSTANCE.convert(type, value, defaultValue);
}
// 集合转换(含有泛型参数,不可以默认强转)
if (Collection.class.isAssignableFrom(rawType)) {
return (T) CollectionConverter.INSTANCE.convert(type, value, (Collection<?>) defaultValue);
}
// Map类型含有泛型参数不可以默认强转
if (Map.class.isAssignableFrom(rawType)) {
return (T) MapConverter.INSTANCE.convert(type, value, (Map<?, ?>) defaultValue);
}
// issue#I6SZYB Entry类含有泛型参数不可以默认强转
if (Map.Entry.class.isAssignableFrom(rawType)) {
return (T) EntryConverter.INSTANCE.convert(type, value);
}
// 默认强转
if (rawType.isInstance(value)) {
return (T) value;
}
// 原始类型转换
if (rawType.isPrimitive()) {
return PrimitiveConverter.INSTANCE.convert(type, value, defaultValue);
}
// 数字类型转换
if (Number.class.isAssignableFrom(rawType)) {
return NumberConverter.INSTANCE.convert(type, value, defaultValue);
}
// 枚举转换
if (rawType.isEnum()) {
return EnumConverter.INSTANCE.convert(type, value, defaultValue);
}
// 数组转换
if (rawType.isArray()) {
return ArrayConverter.INSTANCE.convert(type, value, defaultValue);
}
// Record
if (RecordUtil.isRecord(rawType)) {
return (T) RecordConverter.INSTANCE.convert(type, value);
}
// Kotlin Bean
if (KClassUtil.isKotlinClass(rawType)) {
return (T) KBeanConverter.INSTANCE.convert(type, value);
}
// issue#I7FQ29 Class
if ("java.lang.Class".equals(rawType.getName())) {
return (T) ClassConverter.INSTANCE.convert(type, value);
}
// 空值转空Bean
if (ObjUtil.isEmpty(value)) {
// issue#3649 空值转空对象,则直接实例化
return ConstructorUtil.newInstanceIfPossible(rawType);
}
// 表示非需要特殊转换的对象
return null;
}
// ----------------------------------------------------------- Private method end
} }

View File

@@ -25,7 +25,7 @@ import java.util.Set;
/** /**
* 用户自定义转换器<br> * 用户自定义转换器<br>
* 通过自定义实现{@link PredicateConverter},可以通过{@link PredicateConverter#test(Object)} 检查目标类型是否匹配<br> * 通过自定义实现{@link MatcherConverter},可以通过{@link MatcherConverter#match(Type, Class, Object)} 检查目标类型是否匹配<br>
* 如果匹配,则直接转换,否则使用默认转换器转换。 * 如果匹配,则直接转换,否则使用默认转换器转换。
* *
* @author Looly * @author Looly
@@ -41,26 +41,26 @@ public class CustomConverter implements Converter, Serializable {
/** /**
* 静态初始化器由JVM来保证线程安全 * 静态初始化器由JVM来保证线程安全
*/ */
private static final RegisterConverter INSTANCE = new RegisterConverter(); private static final CustomConverter INSTANCE = new CustomConverter();
} }
/** /**
* 获得单例的 RegisterConverter * 获得单例的 CustomConverter
* *
* @return RegisterConverter * @return CustomConverter
*/ */
public static RegisterConverter getInstance() { public static CustomConverter getInstance() {
return CustomConverter.SingletonHolder.INSTANCE; return CustomConverter.SingletonHolder.INSTANCE;
} }
/** /**
* 用户自定义类型转换器 * 用户自定义类型转换器
*/ */
private volatile Set<PredicateConverter> converterSet; private volatile Set<MatcherConverter> converterSet;
@Override @Override
public Object convert(final Type targetType, final Object value) throws ConvertException { public Object convert(final Type targetType, final Object value) throws ConvertException {
final Converter customConverter = getCustomConverter(targetType); final Converter customConverter = getCustomConverter(targetType, value);
return null == customConverter ? null : customConverter.convert(targetType, value); return null == customConverter ? null : customConverter.convert(targetType, value);
} }
@@ -68,10 +68,11 @@ public class CustomConverter implements Converter, Serializable {
* 获得匹配类型的自定义转换器 * 获得匹配类型的自定义转换器
* *
* @param type 类型 * @param type 类型
* @param value 被转换的值
* @return 转换器 * @return 转换器
*/ */
public Converter getCustomConverter(final Type type) { public Converter getCustomConverter(final Type type, final Object value) {
return getConverterFromSet(this.converterSet, type); return getConverterFromSet(this.converterSet, type, value);
} }
/** /**
@@ -80,7 +81,7 @@ public class CustomConverter implements Converter, Serializable {
* @param converter 转换器 * @param converter 转换器
* @return ConverterRegistry * @return ConverterRegistry
*/ */
public CustomConverter add(final PredicateConverter converter) { public CustomConverter add(final MatcherConverter converter) {
if (null == this.converterSet) { if (null == this.converterSet) {
synchronized (this) { synchronized (this) {
if (null == this.converterSet) { if (null == this.converterSet) {
@@ -98,7 +99,7 @@ public class CustomConverter implements Converter, Serializable {
* @param type 类型 * @param type 类型
* @return 转换器 * @return 转换器
*/ */
private static Converter getConverterFromSet(final Set<? extends PredicateConverter> converterSet, final Type type) { private static Converter getConverterFromSet(final Set<? extends MatcherConverter> converterSet, final Type type, final Object value) {
return StreamUtil.of(converterSet).filter((predicate) -> predicate.test(type)).findFirst().orElse(null); return StreamUtil.of(converterSet).filter((predicate) -> predicate.match(type, value)).findFirst().orElse(null);
} }
} }

View File

@@ -0,0 +1,53 @@
/*
* Copyright (c) 2024 Hutool Team and hutool.cn
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.dromara.hutool.core.convert;
import org.dromara.hutool.core.reflect.TypeUtil;
import java.lang.reflect.Type;
/**
* 带有匹配的转换器<br>
* 判断目标对象是否满足条件,满足则转换,否则跳过<br>
* 实现此接口同样可以不判断断言而直接转换
*
* @author Looly
* @since 6.0.0
*/
public interface MatcherConverter extends Converter {
/**
* 判断需要转换的对象是否匹配当前转换器,满足则转换,否则跳过
*
* @param targetType 转换的目标类型,不能为{@code null}
* @param rawType 目标原始类型当targetType为Class时和此参数一致不能为{@code null}
* @param value 需要转换的值
* @return 是否匹配
*/
boolean match(Type targetType, Class<?> rawType, Object value);
/**
* 判断需要转换的对象是否匹配当前转换器,满足则转换,否则跳过
*
* @param targetType 转换的目标类型
* @param value 需要转换的值
* @return 是否匹配
*/
default boolean match(final Type targetType, final Object value) {
return match(targetType, TypeUtil.getClass(targetType), value);
}
}

View File

@@ -1,31 +0,0 @@
/*
* Copyright (c) 2024 Hutool Team and hutool.cn
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.dromara.hutool.core.convert;
import java.lang.reflect.Type;
import java.util.function.Predicate;
/**
* 断言转换器<br>
* 判断目标对象是否满足断言,满足则转换,否则跳过<br>
* 实现此接口同样可以不判断断言而直接转换
*
* @author Looly
* @since 6.0.0
*/
public interface PredicateConverter extends Converter, Predicate<Type> {
}

View File

@@ -170,11 +170,12 @@ public class RegisterConverter implements Converter, Serializable {
final Map<Class<?>, Converter> defaultConverterMap = new SafeConcurrentHashMap<>(64); final Map<Class<?>, Converter> defaultConverterMap = new SafeConcurrentHashMap<>(64);
// 包装类转换器 // 包装类转换器
defaultConverterMap.put(Character.class, new CharacterConverter()); defaultConverterMap.put(Character.class, CharacterConverter.INSTANCE);
defaultConverterMap.put(Boolean.class, new BooleanConverter()); defaultConverterMap.put(Boolean.class, BooleanConverter.INSTANCE);
defaultConverterMap.put(AtomicBoolean.class, new AtomicBooleanConverter());// since 3.0.8 defaultConverterMap.put(AtomicBoolean.class, AtomicBooleanConverter.INSTANCE);// since 3.0.8
defaultConverterMap.put(CharSequence.class, new StringConverter()); final StringConverter stringConverter = new StringConverter();
defaultConverterMap.put(String.class, new StringConverter()); defaultConverterMap.put(CharSequence.class, stringConverter);
defaultConverterMap.put(String.class, stringConverter);
// URI and URL // URI and URL
defaultConverterMap.put(URI.class, new URIConverter()); defaultConverterMap.put(URI.class, new URIConverter());

View File

@@ -0,0 +1,140 @@
/*
* Copyright (c) 2024 Hutool Team and hutool.cn
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.dromara.hutool.core.convert;
import org.dromara.hutool.core.convert.impl.*;
import org.dromara.hutool.core.reflect.TypeUtil;
import org.dromara.hutool.core.stream.StreamUtil;
import java.io.Serializable;
import java.lang.reflect.Type;
import java.util.LinkedHashSet;
import java.util.Set;
/**
* 特殊类型转换器,如果不符合特殊类型,则返回{@code null}继续其它转换规则<br>
* 对于特殊对象如集合、Map、Enum、数组等的转换器实现转换<br>
* 注意:此类中的转换器查找是通过遍历方式
*
* @author Looly
* @since 6.0.0
*/
public class SpecialConverter implements Converter, Serializable {
private static final long serialVersionUID = 1L;
/**
* 类级的内部类,也就是静态的成员式内部类,该内部类的实例与外部类的实例 没有绑定关系,而且只有被调用到才会装载,从而实现了延迟加载
*/
private static class SingletonHolder {
/**
* 静态初始化器由JVM来保证线程安全
*/
private static final SpecialConverter INSTANCE = new SpecialConverter();
}
/**
* 获得单例的 CustomConverter
*
* @return CustomConverter
*/
public static SpecialConverter getInstance() {
return SpecialConverter.SingletonHolder.INSTANCE;
}
/**
* 类型转换器集合<br>
* 此集合初始化后不再加入新值,因此单例使用线程安全
*/
private final Set<MatcherConverter> converterSet;
/**
* 构造
*/
private SpecialConverter() {
final Set<MatcherConverter> converterSet = new LinkedHashSet<>(64);
// 集合转换(含有泛型参数,不可以默认强转)
converterSet.add(CollectionConverter.INSTANCE);
// Map类型含有泛型参数不可以默认强转
converterSet.add(MapConverter.INSTANCE);
// issue#I6SZYB Entry类含有泛型参数不可以默认强转
converterSet.add(EntryConverter.INSTANCE);
// 默认强转
converterSet.add(CastConverter.INSTANCE);
// 日期、java.sql中的日期以及自定义日期统一处理
converterSet.add(DateConverter.INSTANCE);
// 原始类型转换
converterSet.add(PrimitiveConverter.INSTANCE);
// 数字类型转换
converterSet.add(NumberConverter.INSTANCE);
// 枚举转换
converterSet.add(EnumConverter.INSTANCE);
// 数组转换
converterSet.add(ArrayConverter.INSTANCE);
// Record
converterSet.add(RecordConverter.INSTANCE);
// Kotlin Bean
converterSet.add(KBeanConverter.INSTANCE);
// issue#I7FQ29 Class
converterSet.add(ClassConverter.INSTANCE);
// // 空值转空Bean
converterSet.add(EmptyBeanConverter.INSTANCE);
this.converterSet = converterSet;
}
@Override
public Object convert(final Type targetType, final Object value) throws ConvertException {
return convert(targetType, TypeUtil.getClass(targetType), value);
}
/**
* 转换值
*
* @param targetType 目标类型
* @param rawType 目标原始类型即目标的Class
* @param value 被转换的值
* @return 转换后的值,如果无转换器,返回{@code null}
* @throws ConvertException 转换异常,即找到了对应的转换器,但是转换失败
*/
public Object convert(final Type targetType, final Class<?> rawType, final Object value) throws ConvertException {
final Converter converter = getConverter(targetType, rawType, value);
return null == converter ? null : converter.convert(targetType, value);
}
/**
* 获得匹配的转换器
*
* @param type 类型
* @param rawType 目标类型的Class
* @param value 被转换的值
* @return 转换器
*/
public Converter getConverter(final Type type, final Class<?> rawType, final Object value) {
return getConverterFromSet(this.converterSet, type, rawType, value);
}
/**
* 从指定集合中查找满足条件的转换器
*
* @param type 类型
* @return 转换器
*/
private static Converter getConverterFromSet(final Set<? extends MatcherConverter> converterSet, final Type type, final Class<?> rawType, final Object value) {
return StreamUtil.of(converterSet).filter((predicate) -> predicate.match(type, rawType, value)).findFirst().orElse(null);
}
}

View File

@@ -22,6 +22,7 @@ import org.dromara.hutool.core.collection.ListUtil;
import org.dromara.hutool.core.collection.iter.IterUtil; import org.dromara.hutool.core.collection.iter.IterUtil;
import org.dromara.hutool.core.convert.AbstractConverter; import org.dromara.hutool.core.convert.AbstractConverter;
import org.dromara.hutool.core.convert.ConvertUtil; import org.dromara.hutool.core.convert.ConvertUtil;
import org.dromara.hutool.core.convert.MatcherConverter;
import org.dromara.hutool.core.io.SerializeUtil; import org.dromara.hutool.core.io.SerializeUtil;
import org.dromara.hutool.core.text.StrUtil; import org.dromara.hutool.core.text.StrUtil;
import org.dromara.hutool.core.text.split.SplitUtil; import org.dromara.hutool.core.text.split.SplitUtil;
@@ -29,6 +30,7 @@ import org.dromara.hutool.core.util.ByteUtil;
import java.io.Serializable; import java.io.Serializable;
import java.lang.reflect.Array; import java.lang.reflect.Array;
import java.lang.reflect.Type;
import java.util.Collection; import java.util.Collection;
import java.util.Iterator; import java.util.Iterator;
import java.util.List; import java.util.List;
@@ -38,7 +40,7 @@ import java.util.List;
* *
* @author Looly * @author Looly
*/ */
public class ArrayConverter extends AbstractConverter { public class ArrayConverter extends AbstractConverter implements MatcherConverter {
private static final long serialVersionUID = 1L; private static final long serialVersionUID = 1L;
/** /**
@@ -67,6 +69,11 @@ public class ArrayConverter extends AbstractConverter {
this.ignoreElementError = ignoreElementError; this.ignoreElementError = ignoreElementError;
} }
@Override
public boolean match(final Type targetType, final Class<?> rawType, final Object value) {
return rawType.isArray();
}
@Override @Override
protected Object convertInternal(final Class<?> targetClass, final Object value) { protected Object convertInternal(final Class<?> targetClass, final Object value) {
final Class<?> targetComponentType; final Class<?> targetComponentType;

View File

@@ -30,6 +30,11 @@ import java.util.concurrent.atomic.AtomicBoolean;
public class AtomicBooleanConverter extends AbstractConverter { public class AtomicBooleanConverter extends AbstractConverter {
private static final long serialVersionUID = 1L; private static final long serialVersionUID = 1L;
/**
* 单例
*/
public static final AtomicBooleanConverter INSTANCE = new AtomicBooleanConverter();
@Override @Override
protected AtomicBoolean convertInternal(final Class<?> targetClass, final Object value) { protected AtomicBoolean convertInternal(final Class<?> targetClass, final Object value) {
if (value instanceof Boolean) { if (value instanceof Boolean) {

View File

@@ -35,6 +35,11 @@ import org.dromara.hutool.core.util.BooleanUtil;
public class BooleanConverter extends AbstractConverter { public class BooleanConverter extends AbstractConverter {
private static final long serialVersionUID = 1L; private static final long serialVersionUID = 1L;
/**
* 单例
*/
public static final BooleanConverter INSTANCE = new BooleanConverter();
@Override @Override
protected Boolean convertInternal(final Class<?> targetClass, final Object value) { protected Boolean convertInternal(final Class<?> targetClass, final Object value) {
if (value instanceof Number) { if (value instanceof Number) {

View File

@@ -16,23 +16,34 @@
package org.dromara.hutool.core.convert.impl; package org.dromara.hutool.core.convert.impl;
import org.dromara.hutool.core.convert.AbstractConverter;
import org.dromara.hutool.core.convert.ConvertException; import org.dromara.hutool.core.convert.ConvertException;
import org.dromara.hutool.core.convert.MatcherConverter;
import java.io.Serializable;
import java.lang.reflect.Type;
/** /**
* 强转转换器 * 强转转换器
* *
* @author Looly * @author Looly
* @param <T> 强制转换到的类型
* @since 4.0.2 * @since 4.0.2
*/ */
public class CastConverter<T> extends AbstractConverter { public class CastConverter implements MatcherConverter, Serializable {
private static final long serialVersionUID = 1L; private static final long serialVersionUID = 1L;
/**
* 单例
*/
public static final CastConverter INSTANCE = new CastConverter();
@Override @Override
protected T convertInternal(final Class<?> targetClass, final Object value) { public boolean match(final Type targetType, final Class<?> rawType, final Object value) {
// 由于在AbstractConverter中已经有类型判断并强制转换因此当在上一步强制转换失败时直接抛出异常 return rawType.isInstance(value);
throw new ConvertException("Can not cast value to [{}]", targetClass);
} }
@Override
public Object convert(final Type targetType, final Object value) throws ConvertException{
// 此处无需逻辑目标对象类型是value的父类或接口直接返回匹配即可
return value;
}
} }

View File

@@ -29,6 +29,11 @@ import org.dromara.hutool.core.text.StrUtil;
public class CharacterConverter extends AbstractConverter { public class CharacterConverter extends AbstractConverter {
private static final long serialVersionUID = 1L; private static final long serialVersionUID = 1L;
/**
* 单例
*/
public static final CharacterConverter INSTANCE = new CharacterConverter();
@Override @Override
protected Character convertInternal(final Class<?> targetClass, final Object value) { protected Character convertInternal(final Class<?> targetClass, final Object value) {
if (value instanceof Boolean) { if (value instanceof Boolean) {

View File

@@ -18,6 +18,9 @@ package org.dromara.hutool.core.convert.impl;
import org.dromara.hutool.core.convert.AbstractConverter; import org.dromara.hutool.core.convert.AbstractConverter;
import org.dromara.hutool.core.classloader.ClassLoaderUtil; import org.dromara.hutool.core.classloader.ClassLoaderUtil;
import org.dromara.hutool.core.convert.MatcherConverter;
import java.lang.reflect.Type;
/** /**
* 类转换器<br> * 类转换器<br>
@@ -25,7 +28,7 @@ import org.dromara.hutool.core.classloader.ClassLoaderUtil;
* *
* @author Looly * @author Looly
*/ */
public class ClassConverter extends AbstractConverter { public class ClassConverter extends AbstractConverter implements MatcherConverter {
private static final long serialVersionUID = 1L; private static final long serialVersionUID = 1L;
/** /**
@@ -52,6 +55,11 @@ public class ClassConverter extends AbstractConverter {
this.isInitialized = isInitialized; this.isInitialized = isInitialized;
} }
@Override
public boolean match(final Type targetType, final Class<?> rawType, final Object value) {
return "java.lang.Class".equals(rawType.getName());
}
@Override @Override
protected Class<?> convertInternal(final Class<?> targetClass, final Object value) { protected Class<?> convertInternal(final Class<?> targetClass, final Object value) {
return ClassLoaderUtil.loadClass(convertToStr(value), isInitialized); return ClassLoaderUtil.loadClass(convertToStr(value), isInitialized);

View File

@@ -17,10 +17,11 @@
package org.dromara.hutool.core.convert.impl; package org.dromara.hutool.core.convert.impl;
import org.dromara.hutool.core.collection.CollUtil; import org.dromara.hutool.core.collection.CollUtil;
import org.dromara.hutool.core.convert.Converter; import org.dromara.hutool.core.convert.MatcherConverter;
import org.dromara.hutool.core.reflect.TypeReference; import org.dromara.hutool.core.reflect.TypeReference;
import org.dromara.hutool.core.reflect.TypeUtil; import org.dromara.hutool.core.reflect.TypeUtil;
import java.io.Serializable;
import java.lang.reflect.Type; import java.lang.reflect.Type;
import java.util.Collection; import java.util.Collection;
@@ -30,13 +31,19 @@ import java.util.Collection;
* @author Looly * @author Looly
* @since 3.0.8 * @since 3.0.8
*/ */
public class CollectionConverter implements Converter { public class CollectionConverter implements MatcherConverter, Serializable {
private static final long serialVersionUID = 1L;
/** /**
* 单例实体 * 单例实体
*/ */
public static CollectionConverter INSTANCE = new CollectionConverter(); public static CollectionConverter INSTANCE = new CollectionConverter();
@Override
public boolean match(final Type targetType, final Class<?> rawType, final Object value) {
return Collection.class.isAssignableFrom(rawType);
}
@Override @Override
public Collection<?> convert(Type targetType, final Object value) { public Collection<?> convert(Type targetType, final Object value) {
if (targetType instanceof TypeReference) { if (targetType instanceof TypeReference) {

View File

@@ -18,11 +18,13 @@ package org.dromara.hutool.core.convert.impl;
import org.dromara.hutool.core.convert.AbstractConverter; import org.dromara.hutool.core.convert.AbstractConverter;
import org.dromara.hutool.core.convert.ConvertException; import org.dromara.hutool.core.convert.ConvertException;
import org.dromara.hutool.core.convert.MatcherConverter;
import org.dromara.hutool.core.date.DateTime; import org.dromara.hutool.core.date.DateTime;
import org.dromara.hutool.core.date.DateUtil; import org.dromara.hutool.core.date.DateUtil;
import org.dromara.hutool.core.date.SqlDateUtil; import org.dromara.hutool.core.date.SqlDateUtil;
import org.dromara.hutool.core.text.StrUtil; import org.dromara.hutool.core.text.StrUtil;
import java.lang.reflect.Type;
import java.time.temporal.TemporalAccessor; import java.time.temporal.TemporalAccessor;
import java.util.Calendar; import java.util.Calendar;
import java.util.Date; import java.util.Date;
@@ -32,7 +34,7 @@ import java.util.Date;
* *
* @author Looly * @author Looly
*/ */
public class DateConverter extends AbstractConverter { public class DateConverter extends AbstractConverter implements MatcherConverter {
private static final long serialVersionUID = 1L; private static final long serialVersionUID = 1L;
/** /**
@@ -79,6 +81,11 @@ public class DateConverter extends AbstractConverter {
this.format = format; this.format = format;
} }
@Override
public boolean match(final Type targetType, final Class<?> rawType, final Object value) {
return Date.class.isAssignableFrom(rawType);
}
@Override @Override
protected java.util.Date convertInternal(final Class<?> targetClass, final Object value) { protected java.util.Date convertInternal(final Class<?> targetClass, final Object value) {
if (value == null || (value instanceof CharSequence && StrUtil.isBlank(value.toString()))) { if (value == null || (value instanceof CharSequence && StrUtil.isBlank(value.toString()))) {

View File

@@ -0,0 +1,51 @@
/*
* Copyright (c) 2024 Hutool Team and hutool.cn
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.dromara.hutool.core.convert.impl;
import org.dromara.hutool.core.convert.AbstractConverter;
import org.dromara.hutool.core.convert.MatcherConverter;
import org.dromara.hutool.core.reflect.ConstructorUtil;
import org.dromara.hutool.core.util.ObjUtil;
import java.io.Serializable;
import java.lang.reflect.Type;
/**
* 空值或空对象转换器,转换结果为目标类型对象的实例化对象
*
* @author Looly
* @since 6.0.0
*/
public class EmptyBeanConverter extends AbstractConverter implements MatcherConverter, Serializable {
private static final long serialVersionUID = 1L;
/**
* 单例
*/
public static final EmptyBeanConverter INSTANCE = new EmptyBeanConverter();
@Override
public boolean match(final Type targetType, final Class<?> rawType, final Object value) {
return ObjUtil.isEmpty(value);
}
@Override
protected Object convertInternal(final Class<?> targetClass, final Object value) {
// issue#3649 空值转空对象,则直接实例化
return ConstructorUtil.newInstanceIfPossible(targetClass);
}
}

View File

@@ -19,15 +19,16 @@ package org.dromara.hutool.core.convert.impl;
import org.dromara.hutool.core.bean.BeanUtil; import org.dromara.hutool.core.bean.BeanUtil;
import org.dromara.hutool.core.convert.CompositeConverter; import org.dromara.hutool.core.convert.CompositeConverter;
import org.dromara.hutool.core.convert.ConvertException; import org.dromara.hutool.core.convert.ConvertException;
import org.dromara.hutool.core.convert.Converter; import org.dromara.hutool.core.convert.MatcherConverter;
import org.dromara.hutool.core.lang.tuple.Pair; import org.dromara.hutool.core.lang.tuple.Pair;
import org.dromara.hutool.core.map.MapUtil; import org.dromara.hutool.core.map.MapUtil;
import org.dromara.hutool.core.reflect.ConstructorUtil; import org.dromara.hutool.core.reflect.ConstructorUtil;
import org.dromara.hutool.core.reflect.TypeReference; import org.dromara.hutool.core.reflect.TypeReference;
import org.dromara.hutool.core.reflect.TypeUtil; import org.dromara.hutool.core.reflect.TypeUtil;
import org.dromara.hutool.core.text.StrUtil;
import org.dromara.hutool.core.text.CharUtil; import org.dromara.hutool.core.text.CharUtil;
import org.dromara.hutool.core.text.StrUtil;
import java.io.Serializable;
import java.lang.reflect.Type; import java.lang.reflect.Type;
import java.util.Map; import java.util.Map;
@@ -42,13 +43,19 @@ import java.util.Map;
* *
* @author looly * @author looly
*/ */
public class EntryConverter implements Converter { public class EntryConverter implements MatcherConverter, Serializable {
private static final long serialVersionUID = 1L;
/** /**
* 单例 * 单例
*/ */
public static final EntryConverter INSTANCE = new EntryConverter(); public static final EntryConverter INSTANCE = new EntryConverter();
@Override
public boolean match(final Type targetType, final Class<?> rawType, final Object value) {
return Map.Entry.class.isAssignableFrom(rawType);
}
@Override @Override
public Object convert(Type targetType, final Object value) throws ConvertException { public Object convert(Type targetType, final Object value) throws ConvertException {
if (targetType instanceof TypeReference) { if (targetType instanceof TypeReference) {

View File

@@ -18,6 +18,7 @@ package org.dromara.hutool.core.convert.impl;
import org.dromara.hutool.core.convert.AbstractConverter; import org.dromara.hutool.core.convert.AbstractConverter;
import org.dromara.hutool.core.convert.ConvertException; import org.dromara.hutool.core.convert.ConvertException;
import org.dromara.hutool.core.convert.MatcherConverter;
import org.dromara.hutool.core.lang.EnumItem; import org.dromara.hutool.core.lang.EnumItem;
import org.dromara.hutool.core.map.MapUtil; import org.dromara.hutool.core.map.MapUtil;
import org.dromara.hutool.core.map.reference.WeakConcurrentMap; import org.dromara.hutool.core.map.reference.WeakConcurrentMap;
@@ -27,6 +28,7 @@ import org.dromara.hutool.core.reflect.method.MethodUtil;
import org.dromara.hutool.core.util.EnumUtil; import org.dromara.hutool.core.util.EnumUtil;
import java.lang.reflect.Method; import java.lang.reflect.Method;
import java.lang.reflect.Type;
import java.util.Arrays; import java.util.Arrays;
import java.util.Map; import java.util.Map;
import java.util.stream.Collectors; import java.util.stream.Collectors;
@@ -38,7 +40,7 @@ import java.util.stream.Collectors;
* @since 4.0.2 * @since 4.0.2
*/ */
@SuppressWarnings({"unchecked", "rawtypes"}) @SuppressWarnings({"unchecked", "rawtypes"})
public class EnumConverter extends AbstractConverter { public class EnumConverter extends AbstractConverter implements MatcherConverter {
private static final long serialVersionUID = 1L; private static final long serialVersionUID = 1L;
/** /**
@@ -48,6 +50,11 @@ public class EnumConverter extends AbstractConverter {
private static final WeakConcurrentMap<Class<?>, Map<Class<?>, Method>> VALUE_OF_METHOD_CACHE = new WeakConcurrentMap<>(); private static final WeakConcurrentMap<Class<?>, Map<Class<?>, Method>> VALUE_OF_METHOD_CACHE = new WeakConcurrentMap<>();
@Override
public boolean match(final Type targetType, final Class<?> rawType, final Object value) {
return rawType.isEnum();
}
@Override @Override
protected Object convertInternal(final Class<?> targetClass, final Object value) { protected Object convertInternal(final Class<?> targetClass, final Object value) {
Enum enumValue = tryConvertEnum(value, targetClass); Enum enumValue = tryConvertEnum(value, targetClass);

View File

@@ -22,6 +22,7 @@ import org.dromara.hutool.core.bean.copier.provider.BeanValueProvider;
import org.dromara.hutool.core.bean.copier.provider.MapValueProvider; import org.dromara.hutool.core.bean.copier.provider.MapValueProvider;
import org.dromara.hutool.core.convert.ConvertException; import org.dromara.hutool.core.convert.ConvertException;
import org.dromara.hutool.core.convert.Converter; import org.dromara.hutool.core.convert.Converter;
import org.dromara.hutool.core.convert.MatcherConverter;
import org.dromara.hutool.core.lang.Assert; import org.dromara.hutool.core.lang.Assert;
import org.dromara.hutool.core.reflect.TypeUtil; import org.dromara.hutool.core.reflect.TypeUtil;
import org.dromara.hutool.core.reflect.kotlin.KClassUtil; import org.dromara.hutool.core.reflect.kotlin.KClassUtil;
@@ -40,7 +41,7 @@ import java.util.Map;
* *
* @author Looly * @author Looly
*/ */
public class KBeanConverter implements Converter, Serializable { public class KBeanConverter implements MatcherConverter, Serializable {
private static final long serialVersionUID = 1L; private static final long serialVersionUID = 1L;
/** /**
@@ -48,6 +49,11 @@ public class KBeanConverter implements Converter, Serializable {
*/ */
public static KBeanConverter INSTANCE = new KBeanConverter(); public static KBeanConverter INSTANCE = new KBeanConverter();
@Override
public boolean match(final Type targetType, final Class<?> rawType, final Object value) {
return KClassUtil.isKotlinClass(rawType);
}
@Override @Override
public Object convert(final Type targetType, final Object value) throws ConvertException { public Object convert(final Type targetType, final Object value) throws ConvertException {
Assert.notNull(targetType); Assert.notNull(targetType);

View File

@@ -19,7 +19,7 @@ package org.dromara.hutool.core.convert.impl;
import org.dromara.hutool.core.bean.BeanUtil; import org.dromara.hutool.core.bean.BeanUtil;
import org.dromara.hutool.core.convert.CompositeConverter; import org.dromara.hutool.core.convert.CompositeConverter;
import org.dromara.hutool.core.convert.ConvertException; import org.dromara.hutool.core.convert.ConvertException;
import org.dromara.hutool.core.convert.Converter; import org.dromara.hutool.core.convert.MatcherConverter;
import org.dromara.hutool.core.map.MapUtil; import org.dromara.hutool.core.map.MapUtil;
import org.dromara.hutool.core.reflect.TypeReference; import org.dromara.hutool.core.reflect.TypeReference;
import org.dromara.hutool.core.reflect.TypeUtil; import org.dromara.hutool.core.reflect.TypeUtil;
@@ -40,7 +40,7 @@ import java.util.Objects;
* @author Looly * @author Looly
* @since 3.0.8 * @since 3.0.8
*/ */
public class MapConverter implements Converter, Serializable { public class MapConverter implements MatcherConverter, Serializable {
private static final long serialVersionUID = 1L; private static final long serialVersionUID = 1L;
/** /**
@@ -48,6 +48,11 @@ public class MapConverter implements Converter, Serializable {
*/ */
public static MapConverter INSTANCE = new MapConverter(); public static MapConverter INSTANCE = new MapConverter();
@Override
public boolean match(final Type targetType, final Class<?> rawType, final Object value) {
return Map.class.isAssignableFrom(rawType);
}
@Override @Override
public Object convert(Type targetType, final Object value) throws ConvertException { public Object convert(Type targetType, final Object value) throws ConvertException {
if (targetType instanceof TypeReference) { if (targetType instanceof TypeReference) {

View File

@@ -18,12 +18,14 @@ package org.dromara.hutool.core.convert.impl;
import org.dromara.hutool.core.convert.AbstractConverter; import org.dromara.hutool.core.convert.AbstractConverter;
import org.dromara.hutool.core.convert.ConvertException; import org.dromara.hutool.core.convert.ConvertException;
import org.dromara.hutool.core.convert.MatcherConverter;
import org.dromara.hutool.core.date.DateUtil; import org.dromara.hutool.core.date.DateUtil;
import org.dromara.hutool.core.math.NumberUtil; import org.dromara.hutool.core.math.NumberUtil;
import org.dromara.hutool.core.text.StrUtil; import org.dromara.hutool.core.text.StrUtil;
import org.dromara.hutool.core.util.BooleanUtil; import org.dromara.hutool.core.util.BooleanUtil;
import org.dromara.hutool.core.util.ByteUtil; import org.dromara.hutool.core.util.ByteUtil;
import java.lang.reflect.Type;
import java.math.BigDecimal; import java.math.BigDecimal;
import java.math.BigInteger; import java.math.BigInteger;
import java.time.temporal.TemporalAccessor; import java.time.temporal.TemporalAccessor;
@@ -53,7 +55,7 @@ import java.util.function.Function;
* *
* @author Looly * @author Looly
*/ */
public class NumberConverter extends AbstractConverter { public class NumberConverter extends AbstractConverter implements MatcherConverter {
private static final long serialVersionUID = 1L; private static final long serialVersionUID = 1L;
/** /**
@@ -61,6 +63,11 @@ public class NumberConverter extends AbstractConverter {
*/ */
public static final NumberConverter INSTANCE = new NumberConverter(); public static final NumberConverter INSTANCE = new NumberConverter();
@Override
public boolean match(final Type targetType, final Class<?> rawType, final Object value) {
return Number.class.isAssignableFrom(rawType);
}
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
@Override @Override
protected Number convertInternal(final Class<?> targetClass, final Object value) { protected Number convertInternal(final Class<?> targetClass, final Object value) {

View File

@@ -17,12 +17,11 @@
package org.dromara.hutool.core.convert.impl; package org.dromara.hutool.core.convert.impl;
import org.dromara.hutool.core.convert.AbstractConverter; import org.dromara.hutool.core.convert.AbstractConverter;
import org.dromara.hutool.core.convert.ConvertUtil;
import org.dromara.hutool.core.convert.ConvertException; import org.dromara.hutool.core.convert.ConvertException;
import org.dromara.hutool.core.text.StrUtil; import org.dromara.hutool.core.convert.MatcherConverter;
import org.dromara.hutool.core.util.ObjUtil;
import java.util.function.Function; import java.io.Serializable;
import java.lang.reflect.Type;
/** /**
* 原始类型转换器<br> * 原始类型转换器<br>
@@ -40,7 +39,7 @@ import java.util.function.Function;
* *
* @author Looly * @author Looly
*/ */
public class PrimitiveConverter extends AbstractConverter { public class PrimitiveConverter extends AbstractConverter implements MatcherConverter, Serializable {
private static final long serialVersionUID = 1L; private static final long serialVersionUID = 1L;
/** /**
@@ -48,51 +47,38 @@ public class PrimitiveConverter extends AbstractConverter {
*/ */
public static final PrimitiveConverter INSTANCE = new PrimitiveConverter(); public static final PrimitiveConverter INSTANCE = new PrimitiveConverter();
/** @Override
* 构造<br> public boolean match(final Type targetType, final Class<?> rawType, final Object value) {
* return rawType.isPrimitive();
* @throws IllegalArgumentException 传入的转换类型非原始类型时抛出
*/
public PrimitiveConverter() {
} }
@Override @Override
protected Object convertInternal(final Class<?> targetClass, final Object value) { protected Object convertInternal(final Class<?> primitiveClass, final Object value) {
return PrimitiveConverter.convert(value, targetClass, this::convertToStr); final Object result;
}
@Override
protected String convertToStr(final Object value) {
return StrUtil.trim(super.convertToStr(value));
}
/**
* 将指定值转换为原始类型的值
* @param value 值
* @param primitiveClass 原始类型
* @param toStringFunc 当无法直接转换时,转为字符串后再转换的函数
* @return 转换结果
* @since 5.5.0
*/
protected static Object convert(final Object value, final Class<?> primitiveClass, final Function<Object, String> toStringFunc) {
if (byte.class == primitiveClass) { if (byte.class == primitiveClass) {
return ObjUtil.defaultIfNull(NumberConverter.convert(value, Byte.class, toStringFunc), 0); result = NumberConverter.INSTANCE.convert(Byte.class, value);
} else if (short.class == primitiveClass) { } else if (short.class == primitiveClass) {
return ObjUtil.defaultIfNull(NumberConverter.convert(value, Short.class, toStringFunc), 0); result = NumberConverter.INSTANCE.convert(Short.class, value);
} else if (int.class == primitiveClass) { } else if (int.class == primitiveClass) {
return ObjUtil.defaultIfNull(NumberConverter.convert(value, Integer.class, toStringFunc), 0); result = NumberConverter.INSTANCE.convert(Integer.class, value);
} else if (long.class == primitiveClass) { } else if (long.class == primitiveClass) {
return ObjUtil.defaultIfNull(NumberConverter.convert(value, Long.class, toStringFunc), 0); result = NumberConverter.INSTANCE.convert(Long.class, value);
} else if (float.class == primitiveClass) { } else if (float.class == primitiveClass) {
return ObjUtil.defaultIfNull(NumberConverter.convert(value, Float.class, toStringFunc), 0); result = NumberConverter.INSTANCE.convert(Float.class, value);
} else if (double.class == primitiveClass) { } else if (double.class == primitiveClass) {
return ObjUtil.defaultIfNull(NumberConverter.convert(value, Double.class, toStringFunc), 0); result = NumberConverter.INSTANCE.convert(Double.class, value);
} else if (char.class == primitiveClass) { } else if (char.class == primitiveClass) {
return ConvertUtil.convert(Character.class, value); result = CharacterConverter.INSTANCE.convert(Character.class, value);
} else if (boolean.class == primitiveClass) { } else if (boolean.class == primitiveClass) {
return ConvertUtil.convert(Boolean.class, value); result = BooleanConverter.INSTANCE.convert(Boolean.class, value);
} } else{
throw new ConvertException("Unsupported target type: {}", primitiveClass); throw new ConvertException("Unsupported target type: {}", primitiveClass);
} }
if(null == result){
throw new ConvertException("Can not convert {} to {}", value, primitiveClass);
}
return result;
}
} }

View File

@@ -23,7 +23,9 @@ import org.dromara.hutool.core.bean.copier.provider.BeanValueProvider;
import org.dromara.hutool.core.bean.copier.provider.MapValueProvider; import org.dromara.hutool.core.bean.copier.provider.MapValueProvider;
import org.dromara.hutool.core.convert.AbstractConverter; import org.dromara.hutool.core.convert.AbstractConverter;
import org.dromara.hutool.core.convert.ConvertException; import org.dromara.hutool.core.convert.ConvertException;
import org.dromara.hutool.core.convert.MatcherConverter;
import java.lang.reflect.Type;
import java.util.Map; import java.util.Map;
/** /**
@@ -34,7 +36,7 @@ import java.util.Map;
* ValueProvider =》 Record * ValueProvider =》 Record
* </pre> * </pre>
*/ */
public class RecordConverter extends AbstractConverter { public class RecordConverter extends AbstractConverter implements MatcherConverter {
private static final long serialVersionUID = 1L; private static final long serialVersionUID = 1L;
/** /**
@@ -42,6 +44,11 @@ public class RecordConverter extends AbstractConverter {
*/ */
public static RecordConverter INSTANCE = new RecordConverter(); public static RecordConverter INSTANCE = new RecordConverter();
@Override
public boolean match(final Type targetType, final Class<?> rawType, final Object value) {
return RecordUtil.isRecord(rawType);
}
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
@Override @Override
protected Object convertInternal(final Class<?> targetClass, final Object value) { protected Object convertInternal(final Class<?> targetClass, final Object value) {

View File

@@ -23,7 +23,7 @@ public class SemaphoreRateLimiterTest {
final boolean b1 = rateLimiter.tryAcquire(1); final boolean b1 = rateLimiter.tryAcquire(1);
Assertions.assertFalse(b1); Assertions.assertFalse(b1);
ThreadUtil.sleep(310); ThreadUtil.sleep(400);
// 填充新的许可 // 填充新的许可
final boolean b2 = rateLimiter.tryAcquire(5); final boolean b2 = rateLimiter.tryAcquire(5);

View File

@@ -18,13 +18,13 @@ package org.dromara.hutool.json.convert;
import org.dromara.hutool.core.array.ArrayUtil; import org.dromara.hutool.core.array.ArrayUtil;
import org.dromara.hutool.core.bean.BeanUtil; import org.dromara.hutool.core.bean.BeanUtil;
import org.dromara.hutool.core.bean.RecordUtil;
import org.dromara.hutool.core.bean.copier.BeanCopier; import org.dromara.hutool.core.bean.copier.BeanCopier;
import org.dromara.hutool.core.convert.ConvertUtil;
import org.dromara.hutool.core.convert.ConvertException; import org.dromara.hutool.core.convert.ConvertException;
import org.dromara.hutool.core.convert.ConvertUtil;
import org.dromara.hutool.core.convert.Converter; import org.dromara.hutool.core.convert.Converter;
import org.dromara.hutool.core.convert.RegisterConverter; import org.dromara.hutool.core.convert.RegisterConverter;
import org.dromara.hutool.core.convert.impl.*; import org.dromara.hutool.core.convert.impl.DateConverter;
import org.dromara.hutool.core.convert.impl.TemporalAccessorConverter;
import org.dromara.hutool.core.lang.Opt; import org.dromara.hutool.core.lang.Opt;
import org.dromara.hutool.core.map.MapWrapper; import org.dromara.hutool.core.map.MapWrapper;
import org.dromara.hutool.core.reflect.ConstructorUtil; import org.dromara.hutool.core.reflect.ConstructorUtil;
@@ -40,7 +40,9 @@ import org.dromara.hutool.json.serialize.JSONStringer;
import java.io.Serializable; import java.io.Serializable;
import java.lang.reflect.Type; import java.lang.reflect.Type;
import java.time.temporal.TemporalAccessor; import java.time.temporal.TemporalAccessor;
import java.util.*; import java.util.Date;
import java.util.Iterator;
import java.util.Optional;
/** /**
* JSON转换器实现Object对象转换为{@link JSON},支持的对象: * JSON转换器实现Object对象转换为{@link JSON},支持的对象:
@@ -232,7 +234,7 @@ public class JSONConverter implements Converter, Serializable {
//throw new JSONException("Can not get class from type: {}", targetType); //throw new JSONException("Can not get class from type: {}", targetType);
} }
// 特殊类型转换包括Collection、Map、强转、Array等 // 特殊类型转换包括Collection、Map、强转、Array等
final T result = toSpecial(targetType, rawType, json); final T result = (T) JSONSpecialConverter.getInstance().convert(targetType, rawType, json);
if (null != result) { if (null != result) {
return result; return result;
} }
@@ -265,74 +267,6 @@ public class JSONConverter implements Converter, Serializable {
json.getClass().getName(), json, targetType.getTypeName()); json.getClass().getName(), json, targetType.getTypeName());
} }
/**
* 特殊类型转换<br>
* 包括:
*
* <pre>
* Collection
* Map
* Map.Entry
* 强转(无需转换)
* 数组
* </pre>
*
* @param <T> 转换的目标类型(转换器转换到的类型)
* @param type 类型
* @param value 值
* @return 转换后的值
*/
@SuppressWarnings("unchecked")
private <T> T toSpecial(final Type type, final Class<T> rowType, final JSON value) {
if (null == rowType) {
return null;
}
// 日期、java.sql中的日期以及自定义日期统一处理
if (Date.class.isAssignableFrom(rowType)) {
return (T) DateConverter.INSTANCE.convert(type, value);
}
// 集合转换(含有泛型参数,不可以默认强转)
if (Collection.class.isAssignableFrom(rowType)) {
return (T) CollectionConverter.INSTANCE.convert(type, value);
}
// Map类型含有泛型参数不可以默认强转
if (Map.class.isAssignableFrom(rowType)) {
return (T) MapConverter.INSTANCE.convert(type, value);
}
// issue#I6SZYB Entry类含有泛型参数不可以默认强转
if (Map.Entry.class.isAssignableFrom(rowType)) {
return (T) EntryConverter.INSTANCE.convert(type, value);
}
// 默认强转
if (rowType.isInstance(value)) {
return (T) value;
}
// 数组转换
if (rowType.isArray()) {
return (T) ArrayConverter.INSTANCE.convert(type, value);
}
// Record
if (RecordUtil.isRecord(rowType)) {
return (T) RecordConverter.INSTANCE.convert(type, value);
}
// 空值转空Bean
if (ObjUtil.isEmpty(value)) {
// issue#3649 空值转空对象,则直接实例化
return ConstructorUtil.newInstanceIfPossible(rowType);
}
// 表示非需要特殊转换的对象
return null;
}
private Object toDateWithFormat(final Class<?> targetClass, final Object value) { private Object toDateWithFormat(final Class<?> targetClass, final Object value) {
// 日期转换,支持自定义日期格式 // 日期转换,支持自定义日期格式
final String format = config.getDateFormat(); final String format = config.getDateFormat();

View File

@@ -0,0 +1,141 @@
/*
* Copyright (c) 2024 Hutool Team and hutool.cn
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.dromara.hutool.json.convert;
import org.dromara.hutool.core.convert.ConvertException;
import org.dromara.hutool.core.convert.Converter;
import org.dromara.hutool.core.convert.MatcherConverter;
import org.dromara.hutool.core.convert.impl.*;
import org.dromara.hutool.core.reflect.TypeUtil;
import org.dromara.hutool.core.stream.StreamUtil;
import java.io.Serializable;
import java.lang.reflect.Type;
import java.util.LinkedHashSet;
import java.util.Set;
/**
* 特殊类型转换器,如果不符合特殊类型,则返回{@code null}继续其它转换规则<br>
* 对于特殊对象如集合、Map、Enum、数组等的转换器实现转换<br>
* 注意:此类中的转换器查找是通过遍历方式
*
* @author Looly
* @since 6.0.0
*/
public class JSONSpecialConverter implements Converter, Serializable {
private static final long serialVersionUID = 1L;
/**
* 类级的内部类,也就是静态的成员式内部类,该内部类的实例与外部类的实例 没有绑定关系,而且只有被调用到才会装载,从而实现了延迟加载
*/
private static class SingletonHolder {
/**
* 静态初始化器由JVM来保证线程安全
*/
private static final JSONSpecialConverter INSTANCE = new JSONSpecialConverter();
}
/**
* 获得单例的 CustomConverter
*
* @return CustomConverter
*/
public static JSONSpecialConverter getInstance() {
return JSONSpecialConverter.SingletonHolder.INSTANCE;
}
/**
* 类型转换器集合<br>
* 此集合初始化后不再加入新值,因此单例使用线程安全
*/
private final Set<MatcherConverter> converterSet;
/**
* 构造
*/
private JSONSpecialConverter() {
final Set<MatcherConverter> converterSet = new LinkedHashSet<>(64);
// 集合转换(含有泛型参数,不可以默认强转)
converterSet.add(CollectionConverter.INSTANCE);
// Map类型含有泛型参数不可以默认强转
converterSet.add(MapConverter.INSTANCE);
// issue#I6SZYB Entry类含有泛型参数不可以默认强转
converterSet.add(EntryConverter.INSTANCE);
// 默认强转
converterSet.add(CastConverter.INSTANCE);
// 日期、java.sql中的日期以及自定义日期统一处理
converterSet.add(DateConverter.INSTANCE);
// 原始类型转换
converterSet.add(PrimitiveConverter.INSTANCE);
// 数字类型转换
converterSet.add(NumberConverter.INSTANCE);
// 枚举转换
converterSet.add(EnumConverter.INSTANCE);
// 数组转换
converterSet.add(ArrayConverter.INSTANCE);
// Record
converterSet.add(RecordConverter.INSTANCE);
// issue#I7FQ29 Class
converterSet.add(ClassConverter.INSTANCE);
// // 空值转空Bean
converterSet.add(EmptyBeanConverter.INSTANCE);
this.converterSet = converterSet;
}
@Override
public Object convert(final Type targetType, final Object value) throws ConvertException {
return convert(targetType, TypeUtil.getClass(targetType), value);
}
/**
* 转换值
*
* @param targetType 目标类型
* @param rawType 目标原始类型即目标的Class
* @param value 被转换的值
* @return 转换后的值,如果无转换器,返回{@code null}
* @throws ConvertException 转换异常,即找到了对应的转换器,但是转换失败
*/
public Object convert(final Type targetType, final Class<?> rawType, final Object value) throws ConvertException {
final Converter converter = getConverter(targetType, rawType, value);
return null == converter ? null : converter.convert(targetType, value);
}
/**
* 获得匹配的转换器
*
* @param type 类型
* @param rawType 目标类型的Class
* @param value 被转换的值
* @return 转换器
*/
public Converter getConverter(final Type type, final Class<?> rawType, final Object value) {
return getConverterFromSet(this.converterSet, type, rawType, value);
}
/**
* 从指定集合中查找满足条件的转换器
*
* @param type 类型
* @return 转换器
*/
private static Converter getConverterFromSet(final Set<? extends MatcherConverter> converterSet, final Type type, final Class<?> rawType, final Object value) {
return StreamUtil.of(converterSet).filter((predicate) -> predicate.match(type, rawType, value)).findFirst().orElse(null);
}
}