diff --git a/hutool-core/src/main/java/org/dromara/hutool/core/collection/set/ConcurrentLinkedHashSet.java b/hutool-core/src/main/java/org/dromara/hutool/core/collection/set/ConcurrentLinkedHashSet.java index 748f08d3a..db67a0ce6 100644 --- a/hutool-core/src/main/java/org/dromara/hutool/core/collection/set/ConcurrentLinkedHashSet.java +++ b/hutool-core/src/main/java/org/dromara/hutool/core/collection/set/ConcurrentLinkedHashSet.java @@ -36,7 +36,7 @@ public class ConcurrentLinkedHashSet extends SetFromMap { * 构造 */ public ConcurrentLinkedHashSet() { - super(new ConcurrentLinkedHashMap.Builder().build()); + super(new ConcurrentLinkedHashMap.Builder().maximumWeightedCapacity(64).build()); } /** @@ -46,7 +46,10 @@ public class ConcurrentLinkedHashSet extends SetFromMap { * @param initialCapacity 初始大小 */ public ConcurrentLinkedHashSet(final int initialCapacity) { - super(new ConcurrentLinkedHashMap.Builder().initialCapacity(initialCapacity).build()); + super(new ConcurrentLinkedHashMap.Builder() + .initialCapacity(initialCapacity) + .maximumWeightedCapacity(initialCapacity) + .build()); } /** @@ -58,6 +61,7 @@ public class ConcurrentLinkedHashSet extends SetFromMap { public ConcurrentLinkedHashSet(final int initialCapacity, final int concurrencyLevel) { super(new ConcurrentLinkedHashMap.Builder() .initialCapacity(initialCapacity) + .maximumWeightedCapacity(initialCapacity) .concurrencyLevel(concurrencyLevel) .build()); } diff --git a/hutool-core/src/main/java/org/dromara/hutool/core/convert/AbstractConverter.java b/hutool-core/src/main/java/org/dromara/hutool/core/convert/AbstractConverter.java index a57fe5a2f..02731dc50 100644 --- a/hutool-core/src/main/java/org/dromara/hutool/core/convert/AbstractConverter.java +++ b/hutool-core/src/main/java/org/dromara/hutool/core/convert/AbstractConverter.java @@ -56,7 +56,7 @@ public abstract class AbstractConverter implements Converter, Serializable { // 尝试强转 if (targetClass.isInstance(value)) { // 除Map外,已经是目标类型,不需要转换(Map类型涉及参数类型,需要单独转换) - return CastUtil.castTo(targetClass, value); + return value; } return convertInternal(targetClass, value); } diff --git a/hutool-core/src/main/java/org/dromara/hutool/core/convert/CompositeConverter.java b/hutool-core/src/main/java/org/dromara/hutool/core/convert/CompositeConverter.java index b256f4746..4fb56579c 100644 --- a/hutool-core/src/main/java/org/dromara/hutool/core/convert/CompositeConverter.java +++ b/hutool-core/src/main/java/org/dromara/hutool/core/convert/CompositeConverter.java @@ -17,19 +17,13 @@ package org.dromara.hutool.core.convert; import org.dromara.hutool.core.bean.BeanUtil; -import org.dromara.hutool.core.bean.RecordUtil; -import org.dromara.hutool.core.convert.impl.*; +import org.dromara.hutool.core.convert.impl.BeanConverter; 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.TypeUtil; -import org.dromara.hutool.core.reflect.kotlin.KClassUtil; import org.dromara.hutool.core.util.ObjUtil; import java.lang.reflect.Type; -import java.util.Collection; -import java.util.Date; -import java.util.Map; import java.util.Optional; /** @@ -164,7 +158,7 @@ public class CompositeConverter extends RegisterConverter { } // 特殊类型转换,包括Collection、Map、强转、Array等 - final T result = convertSpecial(type, rawType, value, defaultValue); + final T result = (T) SpecialConverter.getInstance().convert(type, rawType, value); if (null != 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()); } - - // ----------------------------------------------------------- Private method start - - /** - * 特殊类型转换
- * 包括: - * - *
-	 * Collection
-	 * Map
-	 * 强转(无需转换)
-	 * 数组
-	 * 
- * - * @param 转换的目标类型(转换器转换到的类型) - * @param type 类型 - * @param rawType 原始类型 - * @param value 值 - * @param defaultValue 默认值 - * @return 转换后的值 - */ - @SuppressWarnings("unchecked") - private T convertSpecial(final Type type, final Class 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 } diff --git a/hutool-core/src/main/java/org/dromara/hutool/core/convert/CustomConverter.java b/hutool-core/src/main/java/org/dromara/hutool/core/convert/CustomConverter.java index 66cd7a52b..7f0bb955d 100644 --- a/hutool-core/src/main/java/org/dromara/hutool/core/convert/CustomConverter.java +++ b/hutool-core/src/main/java/org/dromara/hutool/core/convert/CustomConverter.java @@ -25,7 +25,7 @@ import java.util.Set; /** * 用户自定义转换器
- * 通过自定义实现{@link PredicateConverter},可以通过{@link PredicateConverter#test(Object)} 检查目标类型是否匹配
+ * 通过自定义实现{@link MatcherConverter},可以通过{@link MatcherConverter#match(Type, Class, Object)} 检查目标类型是否匹配
* 如果匹配,则直接转换,否则使用默认转换器转换。 * * @author Looly @@ -41,37 +41,38 @@ public class CustomConverter implements Converter, Serializable { /** * 静态初始化器,由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; } /** * 用户自定义类型转换器 */ - private volatile Set converterSet; + private volatile Set converterSet; @Override 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); } /** * 获得匹配类型的自定义转换器 * - * @param type 类型 + * @param type 类型 + * @param value 被转换的值 * @return 转换器 */ - public Converter getCustomConverter(final Type type) { - return getConverterFromSet(this.converterSet, type); + public Converter getCustomConverter(final Type type, final Object value) { + return getConverterFromSet(this.converterSet, type, value); } /** @@ -80,7 +81,7 @@ public class CustomConverter implements Converter, Serializable { * @param converter 转换器 * @return ConverterRegistry */ - public CustomConverter add(final PredicateConverter converter) { + public CustomConverter add(final MatcherConverter converter) { if (null == this.converterSet) { synchronized (this) { if (null == this.converterSet) { @@ -98,7 +99,7 @@ public class CustomConverter implements Converter, Serializable { * @param type 类型 * @return 转换器 */ - private static Converter getConverterFromSet(final Set converterSet, final Type type) { - return StreamUtil.of(converterSet).filter((predicate) -> predicate.test(type)).findFirst().orElse(null); + private static Converter getConverterFromSet(final Set converterSet, final Type type, final Object value) { + return StreamUtil.of(converterSet).filter((predicate) -> predicate.match(type, value)).findFirst().orElse(null); } } diff --git a/hutool-core/src/main/java/org/dromara/hutool/core/convert/MatcherConverter.java b/hutool-core/src/main/java/org/dromara/hutool/core/convert/MatcherConverter.java new file mode 100644 index 000000000..597b4d11f --- /dev/null +++ b/hutool-core/src/main/java/org/dromara/hutool/core/convert/MatcherConverter.java @@ -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; + +/** + * 带有匹配的转换器
+ * 判断目标对象是否满足条件,满足则转换,否则跳过
+ * 实现此接口同样可以不判断断言而直接转换 + * + * @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); + } +} diff --git a/hutool-core/src/main/java/org/dromara/hutool/core/convert/PredicateConverter.java b/hutool-core/src/main/java/org/dromara/hutool/core/convert/PredicateConverter.java deleted file mode 100644 index e9705e10e..000000000 --- a/hutool-core/src/main/java/org/dromara/hutool/core/convert/PredicateConverter.java +++ /dev/null @@ -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; - -/** - * 断言转换器
- * 判断目标对象是否满足断言,满足则转换,否则跳过
- * 实现此接口同样可以不判断断言而直接转换 - * - * @author Looly - * @since 6.0.0 - */ -public interface PredicateConverter extends Converter, Predicate { -} diff --git a/hutool-core/src/main/java/org/dromara/hutool/core/convert/RegisterConverter.java b/hutool-core/src/main/java/org/dromara/hutool/core/convert/RegisterConverter.java index 84bdec46d..4c934a3bd 100644 --- a/hutool-core/src/main/java/org/dromara/hutool/core/convert/RegisterConverter.java +++ b/hutool-core/src/main/java/org/dromara/hutool/core/convert/RegisterConverter.java @@ -170,11 +170,12 @@ public class RegisterConverter implements Converter, Serializable { final Map, Converter> defaultConverterMap = new SafeConcurrentHashMap<>(64); // 包装类转换器 - defaultConverterMap.put(Character.class, new CharacterConverter()); - defaultConverterMap.put(Boolean.class, new BooleanConverter()); - defaultConverterMap.put(AtomicBoolean.class, new AtomicBooleanConverter());// since 3.0.8 - defaultConverterMap.put(CharSequence.class, new StringConverter()); - defaultConverterMap.put(String.class, new StringConverter()); + defaultConverterMap.put(Character.class, CharacterConverter.INSTANCE); + defaultConverterMap.put(Boolean.class, BooleanConverter.INSTANCE); + defaultConverterMap.put(AtomicBoolean.class, AtomicBooleanConverter.INSTANCE);// since 3.0.8 + final StringConverter stringConverter = new StringConverter(); + defaultConverterMap.put(CharSequence.class, stringConverter); + defaultConverterMap.put(String.class, stringConverter); // URI and URL defaultConverterMap.put(URI.class, new URIConverter()); diff --git a/hutool-core/src/main/java/org/dromara/hutool/core/convert/SpecialConverter.java b/hutool-core/src/main/java/org/dromara/hutool/core/convert/SpecialConverter.java new file mode 100644 index 000000000..575c8729e --- /dev/null +++ b/hutool-core/src/main/java/org/dromara/hutool/core/convert/SpecialConverter.java @@ -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}继续其它转换规则
+ * 对于特殊对象(如集合、Map、Enum、数组)等的转换器,实现转换
+ * 注意:此类中的转换器查找是通过遍历方式 + * + * @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; + } + + /** + * 类型转换器集合
+ * 此集合初始化后不再加入新值,因此单例使用线程安全 + */ + private final Set converterSet; + + /** + * 构造 + */ + private SpecialConverter() { + final Set 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 converterSet, final Type type, final Class rawType, final Object value) { + return StreamUtil.of(converterSet).filter((predicate) -> predicate.match(type, rawType, value)).findFirst().orElse(null); + } +} diff --git a/hutool-core/src/main/java/org/dromara/hutool/core/convert/impl/ArrayConverter.java b/hutool-core/src/main/java/org/dromara/hutool/core/convert/impl/ArrayConverter.java index 870a42143..383da03e9 100644 --- a/hutool-core/src/main/java/org/dromara/hutool/core/convert/impl/ArrayConverter.java +++ b/hutool-core/src/main/java/org/dromara/hutool/core/convert/impl/ArrayConverter.java @@ -22,6 +22,7 @@ import org.dromara.hutool.core.collection.ListUtil; import org.dromara.hutool.core.collection.iter.IterUtil; import org.dromara.hutool.core.convert.AbstractConverter; 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.text.StrUtil; 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.lang.reflect.Array; +import java.lang.reflect.Type; import java.util.Collection; import java.util.Iterator; import java.util.List; @@ -38,7 +40,7 @@ import java.util.List; * * @author Looly */ -public class ArrayConverter extends AbstractConverter { +public class ArrayConverter extends AbstractConverter implements MatcherConverter { private static final long serialVersionUID = 1L; /** @@ -67,6 +69,11 @@ public class ArrayConverter extends AbstractConverter { this.ignoreElementError = ignoreElementError; } + @Override + public boolean match(final Type targetType, final Class rawType, final Object value) { + return rawType.isArray(); + } + @Override protected Object convertInternal(final Class targetClass, final Object value) { final Class targetComponentType; diff --git a/hutool-core/src/main/java/org/dromara/hutool/core/convert/impl/AtomicBooleanConverter.java b/hutool-core/src/main/java/org/dromara/hutool/core/convert/impl/AtomicBooleanConverter.java index 7c60de9e8..5a09e649a 100644 --- a/hutool-core/src/main/java/org/dromara/hutool/core/convert/impl/AtomicBooleanConverter.java +++ b/hutool-core/src/main/java/org/dromara/hutool/core/convert/impl/AtomicBooleanConverter.java @@ -30,6 +30,11 @@ import java.util.concurrent.atomic.AtomicBoolean; public class AtomicBooleanConverter extends AbstractConverter { private static final long serialVersionUID = 1L; + /** + * 单例 + */ + public static final AtomicBooleanConverter INSTANCE = new AtomicBooleanConverter(); + @Override protected AtomicBoolean convertInternal(final Class targetClass, final Object value) { if (value instanceof Boolean) { diff --git a/hutool-core/src/main/java/org/dromara/hutool/core/convert/impl/BooleanConverter.java b/hutool-core/src/main/java/org/dromara/hutool/core/convert/impl/BooleanConverter.java index d85f69680..7632cb0ab 100644 --- a/hutool-core/src/main/java/org/dromara/hutool/core/convert/impl/BooleanConverter.java +++ b/hutool-core/src/main/java/org/dromara/hutool/core/convert/impl/BooleanConverter.java @@ -35,6 +35,11 @@ import org.dromara.hutool.core.util.BooleanUtil; public class BooleanConverter extends AbstractConverter { private static final long serialVersionUID = 1L; + /** + * 单例 + */ + public static final BooleanConverter INSTANCE = new BooleanConverter(); + @Override protected Boolean convertInternal(final Class targetClass, final Object value) { if (value instanceof Number) { diff --git a/hutool-core/src/main/java/org/dromara/hutool/core/convert/impl/CastConverter.java b/hutool-core/src/main/java/org/dromara/hutool/core/convert/impl/CastConverter.java index 2735d46b4..d7ab19617 100644 --- a/hutool-core/src/main/java/org/dromara/hutool/core/convert/impl/CastConverter.java +++ b/hutool-core/src/main/java/org/dromara/hutool/core/convert/impl/CastConverter.java @@ -16,23 +16,34 @@ 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.MatcherConverter; + +import java.io.Serializable; +import java.lang.reflect.Type; /** * 强转转换器 * * @author Looly - * @param 强制转换到的类型 * @since 4.0.2 */ -public class CastConverter extends AbstractConverter { +public class CastConverter implements MatcherConverter, Serializable { private static final long serialVersionUID = 1L; + /** + * 单例 + */ + public static final CastConverter INSTANCE = new CastConverter(); + @Override - protected T convertInternal(final Class targetClass, final Object value) { - // 由于在AbstractConverter中已经有类型判断并强制转换,因此当在上一步强制转换失败时直接抛出异常 - throw new ConvertException("Can not cast value to [{}]", targetClass); + public boolean match(final Type targetType, final Class rawType, final Object value) { + return rawType.isInstance(value); } + @Override + public Object convert(final Type targetType, final Object value) throws ConvertException{ + // 此处无需逻辑,目标对象类型是value的父类或接口,直接返回匹配即可 + return value; + } } diff --git a/hutool-core/src/main/java/org/dromara/hutool/core/convert/impl/CharacterConverter.java b/hutool-core/src/main/java/org/dromara/hutool/core/convert/impl/CharacterConverter.java index f0405102e..02dcf5e32 100644 --- a/hutool-core/src/main/java/org/dromara/hutool/core/convert/impl/CharacterConverter.java +++ b/hutool-core/src/main/java/org/dromara/hutool/core/convert/impl/CharacterConverter.java @@ -29,6 +29,11 @@ import org.dromara.hutool.core.text.StrUtil; public class CharacterConverter extends AbstractConverter { private static final long serialVersionUID = 1L; + /** + * 单例 + */ + public static final CharacterConverter INSTANCE = new CharacterConverter(); + @Override protected Character convertInternal(final Class targetClass, final Object value) { if (value instanceof Boolean) { diff --git a/hutool-core/src/main/java/org/dromara/hutool/core/convert/impl/ClassConverter.java b/hutool-core/src/main/java/org/dromara/hutool/core/convert/impl/ClassConverter.java index 61ef87a1c..a292316d3 100644 --- a/hutool-core/src/main/java/org/dromara/hutool/core/convert/impl/ClassConverter.java +++ b/hutool-core/src/main/java/org/dromara/hutool/core/convert/impl/ClassConverter.java @@ -18,6 +18,9 @@ package org.dromara.hutool.core.convert.impl; import org.dromara.hutool.core.convert.AbstractConverter; import org.dromara.hutool.core.classloader.ClassLoaderUtil; +import org.dromara.hutool.core.convert.MatcherConverter; + +import java.lang.reflect.Type; /** * 类转换器
@@ -25,7 +28,7 @@ import org.dromara.hutool.core.classloader.ClassLoaderUtil; * * @author Looly */ -public class ClassConverter extends AbstractConverter { +public class ClassConverter extends AbstractConverter implements MatcherConverter { private static final long serialVersionUID = 1L; /** @@ -52,6 +55,11 @@ public class ClassConverter extends AbstractConverter { this.isInitialized = isInitialized; } + @Override + public boolean match(final Type targetType, final Class rawType, final Object value) { + return "java.lang.Class".equals(rawType.getName()); + } + @Override protected Class convertInternal(final Class targetClass, final Object value) { return ClassLoaderUtil.loadClass(convertToStr(value), isInitialized); diff --git a/hutool-core/src/main/java/org/dromara/hutool/core/convert/impl/CollectionConverter.java b/hutool-core/src/main/java/org/dromara/hutool/core/convert/impl/CollectionConverter.java index fe9c24a36..240e34bc0 100644 --- a/hutool-core/src/main/java/org/dromara/hutool/core/convert/impl/CollectionConverter.java +++ b/hutool-core/src/main/java/org/dromara/hutool/core/convert/impl/CollectionConverter.java @@ -17,10 +17,11 @@ package org.dromara.hutool.core.convert.impl; 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.TypeUtil; +import java.io.Serializable; import java.lang.reflect.Type; import java.util.Collection; @@ -30,13 +31,19 @@ import java.util.Collection; * @author Looly * @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(); + @Override + public boolean match(final Type targetType, final Class rawType, final Object value) { + return Collection.class.isAssignableFrom(rawType); + } + @Override public Collection convert(Type targetType, final Object value) { if (targetType instanceof TypeReference) { diff --git a/hutool-core/src/main/java/org/dromara/hutool/core/convert/impl/DateConverter.java b/hutool-core/src/main/java/org/dromara/hutool/core/convert/impl/DateConverter.java index ace4f10b6..a38a7a152 100644 --- a/hutool-core/src/main/java/org/dromara/hutool/core/convert/impl/DateConverter.java +++ b/hutool-core/src/main/java/org/dromara/hutool/core/convert/impl/DateConverter.java @@ -18,11 +18,13 @@ 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.MatcherConverter; import org.dromara.hutool.core.date.DateTime; import org.dromara.hutool.core.date.DateUtil; import org.dromara.hutool.core.date.SqlDateUtil; import org.dromara.hutool.core.text.StrUtil; +import java.lang.reflect.Type; import java.time.temporal.TemporalAccessor; import java.util.Calendar; import java.util.Date; @@ -32,7 +34,7 @@ import java.util.Date; * * @author Looly */ -public class DateConverter extends AbstractConverter { +public class DateConverter extends AbstractConverter implements MatcherConverter { private static final long serialVersionUID = 1L; /** @@ -79,6 +81,11 @@ public class DateConverter extends AbstractConverter { this.format = format; } + @Override + public boolean match(final Type targetType, final Class rawType, final Object value) { + return Date.class.isAssignableFrom(rawType); + } + @Override protected java.util.Date convertInternal(final Class targetClass, final Object value) { if (value == null || (value instanceof CharSequence && StrUtil.isBlank(value.toString()))) { diff --git a/hutool-core/src/main/java/org/dromara/hutool/core/convert/impl/EmptyBeanConverter.java b/hutool-core/src/main/java/org/dromara/hutool/core/convert/impl/EmptyBeanConverter.java new file mode 100644 index 000000000..d9ac65675 --- /dev/null +++ b/hutool-core/src/main/java/org/dromara/hutool/core/convert/impl/EmptyBeanConverter.java @@ -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); + } +} diff --git a/hutool-core/src/main/java/org/dromara/hutool/core/convert/impl/EntryConverter.java b/hutool-core/src/main/java/org/dromara/hutool/core/convert/impl/EntryConverter.java index 1c303fd87..869d2db06 100644 --- a/hutool-core/src/main/java/org/dromara/hutool/core/convert/impl/EntryConverter.java +++ b/hutool-core/src/main/java/org/dromara/hutool/core/convert/impl/EntryConverter.java @@ -19,15 +19,16 @@ package org.dromara.hutool.core.convert.impl; import org.dromara.hutool.core.bean.BeanUtil; import org.dromara.hutool.core.convert.CompositeConverter; 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.map.MapUtil; import org.dromara.hutool.core.reflect.ConstructorUtil; import org.dromara.hutool.core.reflect.TypeReference; 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.StrUtil; +import java.io.Serializable; import java.lang.reflect.Type; import java.util.Map; @@ -42,13 +43,19 @@ import java.util.Map; * * @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(); + @Override + public boolean match(final Type targetType, final Class rawType, final Object value) { + return Map.Entry.class.isAssignableFrom(rawType); + } + @Override public Object convert(Type targetType, final Object value) throws ConvertException { if (targetType instanceof TypeReference) { diff --git a/hutool-core/src/main/java/org/dromara/hutool/core/convert/impl/EnumConverter.java b/hutool-core/src/main/java/org/dromara/hutool/core/convert/impl/EnumConverter.java index 9df63b84a..f4aee2887 100644 --- a/hutool-core/src/main/java/org/dromara/hutool/core/convert/impl/EnumConverter.java +++ b/hutool-core/src/main/java/org/dromara/hutool/core/convert/impl/EnumConverter.java @@ -18,6 +18,7 @@ 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.MatcherConverter; import org.dromara.hutool.core.lang.EnumItem; import org.dromara.hutool.core.map.MapUtil; 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 java.lang.reflect.Method; +import java.lang.reflect.Type; import java.util.Arrays; import java.util.Map; import java.util.stream.Collectors; @@ -38,7 +40,7 @@ import java.util.stream.Collectors; * @since 4.0.2 */ @SuppressWarnings({"unchecked", "rawtypes"}) -public class EnumConverter extends AbstractConverter { +public class EnumConverter extends AbstractConverter implements MatcherConverter { private static final long serialVersionUID = 1L; /** @@ -48,6 +50,11 @@ public class EnumConverter extends AbstractConverter { private static final WeakConcurrentMap, Map, Method>> VALUE_OF_METHOD_CACHE = new WeakConcurrentMap<>(); + @Override + public boolean match(final Type targetType, final Class rawType, final Object value) { + return rawType.isEnum(); + } + @Override protected Object convertInternal(final Class targetClass, final Object value) { Enum enumValue = tryConvertEnum(value, targetClass); diff --git a/hutool-core/src/main/java/org/dromara/hutool/core/convert/impl/KBeanConverter.java b/hutool-core/src/main/java/org/dromara/hutool/core/convert/impl/KBeanConverter.java index 59ab7ebb2..82156c689 100755 --- a/hutool-core/src/main/java/org/dromara/hutool/core/convert/impl/KBeanConverter.java +++ b/hutool-core/src/main/java/org/dromara/hutool/core/convert/impl/KBeanConverter.java @@ -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.convert.ConvertException; 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.reflect.TypeUtil; import org.dromara.hutool.core.reflect.kotlin.KClassUtil; @@ -40,7 +41,7 @@ import java.util.Map; * * @author Looly */ -public class KBeanConverter implements Converter, Serializable { +public class KBeanConverter implements MatcherConverter, Serializable { private static final long serialVersionUID = 1L; /** @@ -48,6 +49,11 @@ public class KBeanConverter implements Converter, Serializable { */ public static KBeanConverter INSTANCE = new KBeanConverter(); + @Override + public boolean match(final Type targetType, final Class rawType, final Object value) { + return KClassUtil.isKotlinClass(rawType); + } + @Override public Object convert(final Type targetType, final Object value) throws ConvertException { Assert.notNull(targetType); diff --git a/hutool-core/src/main/java/org/dromara/hutool/core/convert/impl/MapConverter.java b/hutool-core/src/main/java/org/dromara/hutool/core/convert/impl/MapConverter.java index d653c365b..dbf48afa2 100644 --- a/hutool-core/src/main/java/org/dromara/hutool/core/convert/impl/MapConverter.java +++ b/hutool-core/src/main/java/org/dromara/hutool/core/convert/impl/MapConverter.java @@ -19,7 +19,7 @@ package org.dromara.hutool.core.convert.impl; import org.dromara.hutool.core.bean.BeanUtil; import org.dromara.hutool.core.convert.CompositeConverter; 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.reflect.TypeReference; import org.dromara.hutool.core.reflect.TypeUtil; @@ -40,7 +40,7 @@ import java.util.Objects; * @author Looly * @since 3.0.8 */ -public class MapConverter implements Converter, Serializable { +public class MapConverter implements MatcherConverter, Serializable { private static final long serialVersionUID = 1L; /** @@ -48,6 +48,11 @@ public class MapConverter implements Converter, Serializable { */ 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 public Object convert(Type targetType, final Object value) throws ConvertException { if (targetType instanceof TypeReference) { diff --git a/hutool-core/src/main/java/org/dromara/hutool/core/convert/impl/NumberConverter.java b/hutool-core/src/main/java/org/dromara/hutool/core/convert/impl/NumberConverter.java index ad494b7ed..be925da88 100644 --- a/hutool-core/src/main/java/org/dromara/hutool/core/convert/impl/NumberConverter.java +++ b/hutool-core/src/main/java/org/dromara/hutool/core/convert/impl/NumberConverter.java @@ -18,12 +18,14 @@ 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.MatcherConverter; import org.dromara.hutool.core.date.DateUtil; import org.dromara.hutool.core.math.NumberUtil; import org.dromara.hutool.core.text.StrUtil; import org.dromara.hutool.core.util.BooleanUtil; import org.dromara.hutool.core.util.ByteUtil; +import java.lang.reflect.Type; import java.math.BigDecimal; import java.math.BigInteger; import java.time.temporal.TemporalAccessor; @@ -53,7 +55,7 @@ import java.util.function.Function; * * @author Looly */ -public class NumberConverter extends AbstractConverter { +public class NumberConverter extends AbstractConverter implements MatcherConverter { private static final long serialVersionUID = 1L; /** @@ -61,6 +63,11 @@ public class NumberConverter extends AbstractConverter { */ 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") @Override protected Number convertInternal(final Class targetClass, final Object value) { diff --git a/hutool-core/src/main/java/org/dromara/hutool/core/convert/impl/PrimitiveConverter.java b/hutool-core/src/main/java/org/dromara/hutool/core/convert/impl/PrimitiveConverter.java index 1165465a1..884cfcbb4 100644 --- a/hutool-core/src/main/java/org/dromara/hutool/core/convert/impl/PrimitiveConverter.java +++ b/hutool-core/src/main/java/org/dromara/hutool/core/convert/impl/PrimitiveConverter.java @@ -17,12 +17,11 @@ package org.dromara.hutool.core.convert.impl; 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.text.StrUtil; -import org.dromara.hutool.core.util.ObjUtil; +import org.dromara.hutool.core.convert.MatcherConverter; -import java.util.function.Function; +import java.io.Serializable; +import java.lang.reflect.Type; /** * 原始类型转换器
@@ -40,7 +39,7 @@ import java.util.function.Function; * * @author Looly */ -public class PrimitiveConverter extends AbstractConverter { +public class PrimitiveConverter extends AbstractConverter implements MatcherConverter, Serializable { private static final long serialVersionUID = 1L; /** @@ -48,51 +47,38 @@ public class PrimitiveConverter extends AbstractConverter { */ public static final PrimitiveConverter INSTANCE = new PrimitiveConverter(); - /** - * 构造
- * - * @throws IllegalArgumentException 传入的转换类型非原始类型时抛出 - */ - public PrimitiveConverter() { + @Override + public boolean match(final Type targetType, final Class rawType, final Object value) { + return rawType.isPrimitive(); } @Override - protected Object convertInternal(final Class targetClass, final Object value) { - return PrimitiveConverter.convert(value, targetClass, this::convertToStr); - } - - @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 toStringFunc) { + protected Object convertInternal(final Class primitiveClass, final Object value) { + final Object result; 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) { - return ObjUtil.defaultIfNull(NumberConverter.convert(value, Short.class, toStringFunc), 0); + result = NumberConverter.INSTANCE.convert(Short.class, value); } 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) { - return ObjUtil.defaultIfNull(NumberConverter.convert(value, Long.class, toStringFunc), 0); + result = NumberConverter.INSTANCE.convert(Long.class, value); } 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) { - return ObjUtil.defaultIfNull(NumberConverter.convert(value, Double.class, toStringFunc), 0); + result = NumberConverter.INSTANCE.convert(Double.class, value); } else if (char.class == primitiveClass) { - return ConvertUtil.convert(Character.class, value); + result = CharacterConverter.INSTANCE.convert(Character.class, value); } 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; } } diff --git a/hutool-core/src/main/java/org/dromara/hutool/core/convert/impl/RecordConverter.java b/hutool-core/src/main/java/org/dromara/hutool/core/convert/impl/RecordConverter.java index 025835ed2..8cfbc41fa 100644 --- a/hutool-core/src/main/java/org/dromara/hutool/core/convert/impl/RecordConverter.java +++ b/hutool-core/src/main/java/org/dromara/hutool/core/convert/impl/RecordConverter.java @@ -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.convert.AbstractConverter; import org.dromara.hutool.core.convert.ConvertException; +import org.dromara.hutool.core.convert.MatcherConverter; +import java.lang.reflect.Type; import java.util.Map; /** @@ -34,7 +36,7 @@ import java.util.Map; * ValueProvider =》 Record * */ -public class RecordConverter extends AbstractConverter { +public class RecordConverter extends AbstractConverter implements MatcherConverter { private static final long serialVersionUID = 1L; /** @@ -42,6 +44,11 @@ public class RecordConverter extends AbstractConverter { */ 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") @Override protected Object convertInternal(final Class targetClass, final Object value) { diff --git a/hutool-core/src/test/java/org/dromara/hutool/core/thread/ratelimiter/SemaphoreRateLimiterTest.java b/hutool-core/src/test/java/org/dromara/hutool/core/thread/ratelimiter/SemaphoreRateLimiterTest.java index ea38915e2..22926363e 100644 --- a/hutool-core/src/test/java/org/dromara/hutool/core/thread/ratelimiter/SemaphoreRateLimiterTest.java +++ b/hutool-core/src/test/java/org/dromara/hutool/core/thread/ratelimiter/SemaphoreRateLimiterTest.java @@ -23,7 +23,7 @@ public class SemaphoreRateLimiterTest { final boolean b1 = rateLimiter.tryAcquire(1); Assertions.assertFalse(b1); - ThreadUtil.sleep(310); + ThreadUtil.sleep(400); // 填充新的许可 final boolean b2 = rateLimiter.tryAcquire(5); diff --git a/hutool-json/src/main/java/org/dromara/hutool/json/convert/JSONConverter.java b/hutool-json/src/main/java/org/dromara/hutool/json/convert/JSONConverter.java index b01df16c2..e4bf4e8aa 100644 --- a/hutool-json/src/main/java/org/dromara/hutool/json/convert/JSONConverter.java +++ b/hutool-json/src/main/java/org/dromara/hutool/json/convert/JSONConverter.java @@ -18,13 +18,13 @@ package org.dromara.hutool.json.convert; import org.dromara.hutool.core.array.ArrayUtil; 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.convert.ConvertUtil; 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.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.map.MapWrapper; import org.dromara.hutool.core.reflect.ConstructorUtil; @@ -40,7 +40,9 @@ import org.dromara.hutool.json.serialize.JSONStringer; import java.io.Serializable; import java.lang.reflect.Type; import java.time.temporal.TemporalAccessor; -import java.util.*; +import java.util.Date; +import java.util.Iterator; +import java.util.Optional; /** * JSON转换器,实现Object对象转换为{@link JSON},支持的对象: @@ -232,7 +234,7 @@ public class JSONConverter implements Converter, Serializable { //throw new JSONException("Can not get class from type: {}", targetType); } // 特殊类型转换,包括Collection、Map、强转、Array等 - final T result = toSpecial(targetType, rawType, json); + final T result = (T) JSONSpecialConverter.getInstance().convert(targetType, rawType, json); if (null != result) { return result; } @@ -265,74 +267,6 @@ public class JSONConverter implements Converter, Serializable { json.getClass().getName(), json, targetType.getTypeName()); } - /** - * 特殊类型转换
- * 包括: - * - *
-	 * Collection
-	 * Map
-	 * Map.Entry
-	 * 强转(无需转换)
-	 * 数组
-	 * 
- * - * @param 转换的目标类型(转换器转换到的类型) - * @param type 类型 - * @param value 值 - * @return 转换后的值 - */ - @SuppressWarnings("unchecked") - private T toSpecial(final Type type, final Class 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) { // 日期转换,支持自定义日期格式 final String format = config.getDateFormat(); diff --git a/hutool-json/src/main/java/org/dromara/hutool/json/convert/JSONSpecialConverter.java b/hutool-json/src/main/java/org/dromara/hutool/json/convert/JSONSpecialConverter.java new file mode 100644 index 000000000..5de959c8f --- /dev/null +++ b/hutool-json/src/main/java/org/dromara/hutool/json/convert/JSONSpecialConverter.java @@ -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}继续其它转换规则
+ * 对于特殊对象(如集合、Map、Enum、数组)等的转换器,实现转换
+ * 注意:此类中的转换器查找是通过遍历方式 + * + * @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; + } + + /** + * 类型转换器集合
+ * 此集合初始化后不再加入新值,因此单例使用线程安全 + */ + private final Set converterSet; + + /** + * 构造 + */ + private JSONSpecialConverter() { + final Set 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 converterSet, final Type type, final Class rawType, final Object value) { + return StreamUtil.of(converterSet).filter((predicate) -> predicate.match(type, rawType, value)).findFirst().orElse(null); + } +}