!786 优化ObjUtil相关方法注释,并补充测试用例

Merge pull request !786 from Createsequence/fix-obj
This commit is contained in:
Looly 2022-09-01 09:48:33 +00:00 committed by Gitee
commit 83df1673f0
No known key found for this signature in database
GPG Key ID: 173E9B9CA92EEF8F
2 changed files with 241 additions and 118 deletions

View File

@ -1,5 +1,6 @@
package cn.hutool.core.util; package cn.hutool.core.util;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.collection.iter.IterUtil; import cn.hutool.core.collection.iter.IterUtil;
import cn.hutool.core.comparator.CompareUtil; import cn.hutool.core.comparator.CompareUtil;
import cn.hutool.core.convert.Convert; import cn.hutool.core.convert.Convert;
@ -9,15 +10,13 @@ import cn.hutool.core.map.MapUtil;
import cn.hutool.core.math.NumberUtil; import cn.hutool.core.math.NumberUtil;
import cn.hutool.core.reflect.ClassUtil; import cn.hutool.core.reflect.ClassUtil;
import cn.hutool.core.reflect.MethodUtil; import cn.hutool.core.reflect.MethodUtil;
import cn.hutool.core.text.CharSequenceUtil;
import cn.hutool.core.text.StrUtil; import cn.hutool.core.text.StrUtil;
import java.io.Serializable;
import java.lang.reflect.Array; import java.lang.reflect.Array;
import java.math.BigDecimal; import java.math.BigDecimal;
import java.util.Collection; import java.util.*;
import java.util.Enumeration;
import java.util.Iterator;
import java.util.Map;
import java.util.Objects;
import java.util.function.Function; import java.util.function.Function;
import java.util.function.Supplier; import java.util.function.Supplier;
@ -29,13 +28,12 @@ import java.util.function.Supplier;
public class ObjUtil { public class ObjUtil {
/** /**
* 比较两个对象是否相等 * <p>比较两个对象是否相等满足下述任意条件即返回{@code true}
* 相同的条件有两个满足其一即可<br> * <ul>
* <ol> * <li>若两对象皆为{@link BigDecimal}且满足{@code 0 == obj1.compareTo(obj2)}</li>
* <li>obj1 == null &amp;&amp; obj2 == null</li> * <li>{@code obj1 == null && obj2 == null}</li>
* <li>obj1.equals(obj2)</li> * <li>{@code obj1.equals(obj2)}</li>
* <li>如果是BigDecimal比较0 == obj1.compareTo(obj2)</li> * </ul>
* </ol>
* *
* @param obj1 对象1 * @param obj1 对象1
* @param obj2 对象2 * @param obj2 对象2
@ -50,27 +48,27 @@ public class ObjUtil {
} }
/** /**
* 比较两个对象是否不相等<br> * 比较两个对象是否不相等
* *
* @param obj1 对象1 * @param obj1 对象1
* @param obj2 对象2 * @param obj2 对象2
* @return 是否不等 * @return 是否不等
* @since 3.0.7 * @since 3.0.7
* @see #equals(Object, Object)
*/ */
public static boolean notEquals(final Object obj1, final Object obj2) { public static boolean notEquals(final Object obj1, final Object obj2) {
return false == equals(obj1, obj2); return false == equals(obj1, obj2);
} }
/** /**
* 计算对象长度如果是字符串调用其length函数集合类调用其size函数数组调用其length属性其他可遍历对象遍历计算长度<br> * <p>计算对象长度支持类型包括
* 支持的类型包括
* <ul> * <ul>
* <li>CharSequence</li> * <li>{@code null}默认返回{@code 0}</li>
* <li>Map</li> * <li>数组返回数组长度</li>
* <li>Iterator</li> * <li>{@link CharSequence}返回{@link CharSequence#length()}</li>
* <li>Iterable</li> * <li>{@link Collection}返回{@link Collection#size()}</li>
* <li>Enumeration</li> * <li>{@link Iterator}{@link Iterable}可迭代的元素数量</li>
* <li>Array</li> * <li>{@link Enumeration}返回可迭代的元素数量</li>
* </ul> * </ul>
* *
* @param obj 被计算长度的对象 * @param obj 被计算长度的对象
@ -100,6 +98,9 @@ public class ObjUtil {
} }
return count; return count;
} }
if (obj.getClass().isArray()) {
return Array.getLength(obj);
}
if (obj instanceof Enumeration) { if (obj instanceof Enumeration) {
final Enumeration<?> enumeration = (Enumeration<?>) obj; final Enumeration<?> enumeration = (Enumeration<?>) obj;
count = 0; count = 0;
@ -109,23 +110,21 @@ public class ObjUtil {
} }
return count; return count;
} }
if (obj.getClass().isArray()) {
return Array.getLength(obj);
}
return -1; return -1;
} }
/** /**
* 对象中是否包含元素<br> * <p>检查{@code obj}中是否包含{@code element}{@code obj}{@code null}则直接返回{@code false}<br>
* 支持的对象类型包括 * 支持类型包括
* <ul> * <ul>
* <li>String</li> * <li>{@code null}默认返回{@code false}</li>
* <li>Collection</li> * <li>{@link String}等同{@link String#contains(CharSequence)}</li>
* <li>Map</li> * <li>{@link Collection}等同{@link Collection#contains(Object)}</li>
* <li>Iterator</li> * <li>{@link Map}等同{@link Map#containsValue(Object)}</li>
* <li>Iterable</li> * <li>
* <li>Enumeration</li> * {@link Iterator}{@link Iterable}{@link Enumeration}或数组
* <li>Array</li> * 等同于遍历后对其元素调用{@link #equals(Object, Object)}方法
* </li>
* </ul> * </ul>
* *
* @param obj 对象 * @param obj 对象
@ -182,12 +181,7 @@ public class ObjUtil {
} }
/** /**
* 检查对象是否为null<br> * 检查对象是否为{@code null}
* 判断标准为
*
* <pre>
* 1. == null
* </pre>
* *
* @param obj 对象 * @param obj 对象
* @return 是否为null * @return 是否为null
@ -197,7 +191,7 @@ public class ObjUtil {
} }
/** /**
* 检查对象是否不为null * 检查对象是否不为{@code null}
* *
* @param obj 对象 * @param obj 对象
* @return 是否为null * @return 是否为null
@ -207,19 +201,26 @@ public class ObjUtil {
} }
/** /**
* 判断指定对象是否为空支持 * 判断指定对象是否为空支持类型包括
* * <ul>
* <pre> * <li>{@code null}默认返回{@code true}</li>
* 1. CharSequence * <li>数组等同于{@link ArrayUtil#isEmpty(Object)}</li>
* 2. Map * <li>{@link CharSequence}等同于{@link CharSequenceUtil#isEmpty(CharSequence)}</li>
* 3. Iterable * <li>{@link Collection}等同于{@link CollUtil#isEmpty(Collection)}</li>
* 4. Iterator * <li>{@link Map}等同于{@link MapUtil#isEmpty(Map)}</li>
* 5. Array * <li>
* </pre> * {@link Iterator}{@link Iterable}等同于{@link IterUtil#isEmpty(Iterator)}
* {@link IterUtil#isEmpty(Iterable)}
* </li>
* </ul>
* *
* @param obj 被判断的对象 * @param obj 被判断的对象
* @return 是否为空如果类型不支持返回false * @return 是否为空如果类型不支持返回false
* @since 4.5.7 * @since 4.5.7
* @see StrUtil#isEmpty(CharSequence)
* @see MapUtil#isEmpty(Map)
* @see IterUtil#isEmpty(Iterable)
* @see IterUtil#isEmpty(Iterator)
*/ */
@SuppressWarnings("rawtypes") @SuppressWarnings("rawtypes")
public static boolean isEmpty(final Object obj) { public static boolean isEmpty(final Object obj) {
@ -229,7 +230,9 @@ public class ObjUtil {
if (obj instanceof CharSequence) { if (obj instanceof CharSequence) {
return StrUtil.isEmpty((CharSequence) obj); return StrUtil.isEmpty((CharSequence) obj);
} else if (obj instanceof Map) { } else if(obj instanceof Collection){
return CollUtil.isEmpty((Collection)obj);
}else if (obj instanceof Map) {
return MapUtil.isEmpty((Map) obj); return MapUtil.isEmpty((Map) obj);
} else if (obj instanceof Iterable) { } else if (obj instanceof Iterable) {
return IterUtil.isEmpty((Iterable) obj); return IterUtil.isEmpty((Iterable) obj);
@ -243,34 +246,26 @@ public class ObjUtil {
} }
/** /**
* 判断指定对象是否为非空支持 * 判断指定对象是否为非空
*
* <pre>
* 1. CharSequence
* 2. Map
* 3. Iterable
* 4. Iterator
* 5. Array
* </pre>
* *
* @param obj 被判断的对象 * @param obj 被判断的对象
* @return 是否为空如果类型不支持返回true * @return 是否为空如果类型不支持返回true
* @since 4.5.7 * @since 4.5.7
* @see #isEmpty(Object)
*/ */
public static boolean isNotEmpty(final Object obj) { public static boolean isNotEmpty(final Object obj) {
return false == isEmpty(obj); return false == isEmpty(obj);
} }
/** /**
* 如果给定对象为{@code null}返回默认值 * <p>如果给定对象为{@code null}返回默认值
* * <pre>{@code
* <pre> * ObjectUtil.defaultIfNull(null, null); // = null
* ObjectUtil.defaultIfNull(null, null) = null * ObjectUtil.defaultIfNull(null, ""); // = ""
* ObjectUtil.defaultIfNull(null, "") = "" * ObjectUtil.defaultIfNull(null, "zz"); // = "zz"
* ObjectUtil.defaultIfNull(null, "zz") = "zz" * ObjectUtil.defaultIfNull("abc", *); // = "abc"
* ObjectUtil.defaultIfNull("abc", *) = "abc" * ObjectUtil.defaultIfNull(Boolean.TRUE, *); // = Boolean.TRUE
* ObjectUtil.defaultIfNull(Boolean.TRUE, *) = Boolean.TRUE * }</pre>
* </pre>
* *
* @param <T> 对象类型 * @param <T> 对象类型
* @param object 被检查对象可能为{@code null} * @param object 被检查对象可能为{@code null}
@ -333,14 +328,20 @@ public class ObjUtil {
} }
/** /**
* 克隆对象<br> * <p>克隆对象
* 如果对象实现Cloneable接口调用其clone方法<br> * <ol>
* 如果实现Serializable接口执行深度克隆<br> * <li>如果对象是数组则等同于{@link ArrayUtil#clone(Object)}</li>
* 否则返回{@code null} * <li>如果对象实现了{@link Cloneable}接口调用其{@link Cloneable#clone()}方法</li>
* <li>如果对象实现了{@link Serializable}接口执行深度克隆</li>
* <li>不符合上述任意情况则返回{@code null}</li>
* </ol>
* *
* @param <T> 对象类型 * @param <T> 对象类型
* @param obj 被克隆对象 * @param obj 被克隆对象
* @return 克隆后的对象 * @return 克隆后的对象
* @see ArrayUtil#clone(Object)
* @see Object#clone()
* @see #cloneByStream(Object)
*/ */
public static <T> T clone(final T obj) { public static <T> T clone(final T obj) {
T result = ArrayUtil.clone(obj); T result = ArrayUtil.clone(obj);
@ -360,6 +361,7 @@ public class ObjUtil {
* @param <T> 对象类型 * @param <T> 对象类型
* @param obj 对象 * @param obj 对象
* @return 克隆后或原对象 * @return 克隆后或原对象
* @see #clone(Object)
*/ */
public static <T> T cloneIfPossible(final T obj) { public static <T> T cloneIfPossible(final T obj) {
T clone = null; T clone = null;
@ -373,12 +375,13 @@ public class ObjUtil {
/** /**
* 序列化后拷贝流的方式克隆<br> * 序列化后拷贝流的方式克隆<br>
* 对象必须实现Serializable接口 * 若对象未实现{@link Serializable}接口则默认返回{@code null}
* *
* @param <T> 对象类型 * @param <T> 对象类型
* @param obj 被克隆对象 * @param obj 被克隆对象
* @return 克隆后的对象 * @return 克隆后的对象
* @throws UtilException IO异常和ClassNotFoundException封装 * @throws UtilException IO异常和ClassNotFoundException封装
* @see SerializeUtil#clone(Object)
*/ */
public static <T> T cloneByStream(final T obj) { public static <T> T cloneByStream(final T obj) {
return SerializeUtil.clone(obj); return SerializeUtil.clone(obj);
@ -399,12 +402,15 @@ public class ObjUtil {
} }
/** /**
* 检查是否为有效的数字<br> * 检查是否为有效的数字若对象不为{@link Number}则直接返回{@code true}否则
* 检查Double和Float是否为无限大或者Not a Number<br> * <ul>
* 非数字类型和Null将返回true * <li>若对象类型为{@link Double}则检查{@link Double#isInfinite()}{@link Double#isNaN()}</li>
* <li>若对象类型为{@link Float}则检查{@link Float#isInfinite()}{@link Float#isNaN()}</li>
* </ul>
* *
* @param obj 被检查类型 * @param obj 被检查类型
* @return 检查结果非数字类型和Null将返回true * @return 检查结果非数字类型和Null将返回true
* @see NumberUtil#isValidNumber(Number)
*/ */
public static boolean isValidIfNumber(final Object obj) { public static boolean isValidIfNumber(final Object obj) {
if (obj instanceof Number) { if (obj instanceof Number) {
@ -454,7 +460,7 @@ public class ObjUtil {
} }
/** /**
* 获得给定类第一个泛型参数 * 获得给定类指定下标的泛型参数
* *
* @param obj 被检查的对象 * @param obj 被检查的对象
* @param index 泛型类型的索引号即第几个泛型类型 * @param index 泛型类型的索引号即第几个泛型类型
@ -466,16 +472,17 @@ public class ObjUtil {
} }
/** /**
* 将Object转为String<br> * <p>将对象转为字符串
* 策略为 * <ul>
* <pre> * <li>若对象为{@code null}则返回null</li>
* 1null转为"null" * <li>若对象为{@link Map}则返回{@link Map#toString()}</li>
* 2调用Convert.toStr(Object)转换 * <li>若对象为其他类型则调用{@link Convert#toStr(Object)}进行转换</li>
* </pre> * </ul>
* *
* @param obj Bean对象 * @param obj Bean对象
* @return Bean所有字段转为Map后的字符串 * @return 转换后的字符串
* @since 3.2.0 * @since 3.2.0
* @see Convert#toStr(Object)
*/ */
public static String toString(final Object obj) { public static String toString(final Object obj) {
if (null == obj) { if (null == obj) {
@ -484,7 +491,6 @@ public class ObjUtil {
if (obj instanceof Map) { if (obj instanceof Map) {
return obj.toString(); return obj.toString();
} }
return Convert.toStr(obj); return Convert.toStr(obj);
} }
} }

View File

@ -1,17 +1,23 @@
package cn.hutool.core.util; package cn.hutool.core.util;
import cn.hutool.core.collection.ListUtil; import cn.hutool.core.collection.ListUtil;
import cn.hutool.core.exceptions.CloneRuntimeException; import lombok.EqualsAndHashCode;
import lombok.RequiredArgsConstructor;
import org.junit.Assert; import org.junit.Assert;
import org.junit.Test; import org.junit.Test;
import java.io.Serializable;
import java.math.BigDecimal; import java.math.BigDecimal;
import java.time.LocalDateTime; import java.time.LocalDateTime;
import java.util.ArrayList; import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap; import java.util.HashMap;
import java.util.Map; import java.util.Map;
import java.util.function.Function; import java.util.function.Function;
/**
* test for {@link ObjUtil}
*/
public class ObjUtilTest { public class ObjUtilTest {
@Test @Test
@ -56,43 +62,49 @@ public class ObjUtilTest {
final Iterable<Integer> list = ListUtil.of(1, 2, 3); final Iterable<Integer> list = ListUtil.of(1, 2, 3);
Assert.assertEquals(3, ObjUtil.length(list)); Assert.assertEquals(3, ObjUtil.length(list));
Assert.assertEquals(3, ObjUtil.length(Arrays.asList(1, 2, 3).iterator()));
} }
@Test @Test
public void containsTest(){ public void containsTest(){
final int[] array = new int[]{1,2,3,4,5}; Assert.assertTrue(ObjUtil.contains(new int[]{1,2,3,4,5}, 1));
Assert.assertFalse(ObjUtil.contains(null, 1));
final boolean contains = ObjUtil.contains(array, 1); Assert.assertTrue(ObjUtil.contains("123", "3"));
Assert.assertTrue(contains); Map<Integer, Integer> map = new HashMap<>();
map.put(1, 1);
map.put(2, 2);
Assert.assertTrue(ObjUtil.contains(map, 1));
Assert.assertTrue(ObjUtil.contains(Arrays.asList(1, 2, 3).iterator(), 2));
} }
@Test @Test
public void cloneTest() { public void isNullTest() {
final Obj obj = new Obj(); Assert.assertTrue(ObjUtil.isNull(null));
final Obj obj2 = ObjUtil.clone(obj);
Assert.assertEquals("OK", obj2.doSomeThing());
}
static class Obj implements Cloneable {
public String doSomeThing() {
return "OK";
}
@Override
public Obj clone() {
try {
return (Obj) super.clone();
} catch (final CloneNotSupportedException e) {
throw new CloneRuntimeException(e);
}
}
} }
@Test @Test
public void toStringTest() { public void isNotNullTest() {
final ArrayList<String> strings = ListUtil.of("1", "2"); Assert.assertFalse(ObjUtil.isNotNull(null));
final String result = ObjUtil.toString(strings); }
Assert.assertEquals("[1, 2]", result);
@Test
public void isEmptyTest() {
Assert.assertTrue(ObjUtil.isEmpty(null));
Assert.assertTrue(ObjUtil.isEmpty(new int[0]));
Assert.assertTrue(ObjUtil.isEmpty(""));
Assert.assertTrue(ObjUtil.isEmpty(Collections.emptyList()));
Assert.assertTrue(ObjUtil.isEmpty(Collections.emptyMap()));
Assert.assertTrue(ObjUtil.isEmpty(Collections.emptyIterator()));
}
@Test
public void isNotEmptyTest() {
Assert.assertFalse(ObjUtil.isNotEmpty(null));
Assert.assertFalse(ObjUtil.isNotEmpty(new int[0]));
Assert.assertFalse(ObjUtil.isNotEmpty(""));
Assert.assertFalse(ObjUtil.isNotEmpty(Collections.emptyList()));
Assert.assertFalse(ObjUtil.isNotEmpty(Collections.emptyMap()));
Assert.assertFalse(ObjUtil.isNotEmpty(Collections.emptyIterator()));
} }
@Test @Test
@ -113,6 +125,48 @@ public class ObjUtilTest {
Assert.assertSame(val2, ObjUtil.defaultIfNull(null, Function.identity(), val2)); Assert.assertSame(val2, ObjUtil.defaultIfNull(null, Function.identity(), val2));
} }
@Test
public void cloneTest() {
Assert.assertNull(ObjUtil.clone(null));
final CloneableBean cloneableBean1 = new CloneableBean(1);
final CloneableBean cloneableBean2 = ObjUtil.clone(cloneableBean1);
Assert.assertEquals(cloneableBean1, cloneableBean2);
final SerializableBean serializableBean1 = new SerializableBean(2);
final SerializableBean serializableBean2 = ObjUtil.clone(serializableBean1);
Assert.assertEquals(serializableBean1, serializableBean2);
final Bean bean1 = new Bean(3);
Assert.assertNull(ObjUtil.clone(bean1));
}
@Test
public void cloneIfPossibleTest() {
Assert.assertNull(ObjUtil.clone(null));
final CloneableBean cloneableBean1 = new CloneableBean(1);
Assert.assertEquals(cloneableBean1, ObjUtil.cloneIfPossible(cloneableBean1));
final SerializableBean serializableBean1 = new SerializableBean(2);
Assert.assertEquals(serializableBean1, ObjUtil.cloneIfPossible(serializableBean1));
final Bean bean1 = new Bean(3);
Assert.assertSame(bean1, ObjUtil.cloneIfPossible(bean1));
final ExceptionCloneableBean exceptionBean1 = new ExceptionCloneableBean(3);
Assert.assertSame(exceptionBean1, ObjUtil.cloneIfPossible(exceptionBean1));
}
@Test
public void cloneByStreamTest() {
Assert.assertNull(ObjUtil.cloneByStream(null));
Assert.assertNull(ObjUtil.cloneByStream(new CloneableBean(1)));
final SerializableBean serializableBean1 = new SerializableBean(2);
Assert.assertEquals(serializableBean1, ObjUtil.cloneByStream(serializableBean1));
Assert.assertNull(ObjUtil.cloneByStream(new Bean(1)));
}
@Test @Test
public void isBasicTypeTest(){ public void isBasicTypeTest(){
final int a = 1; final int a = 1;
@ -121,9 +175,72 @@ public class ObjUtilTest {
} }
@Test @Test
public void cloneIfPossibleTest() { public void isValidIfNumberTest() {
final String a = "a"; Assert.assertTrue(ObjUtil.isValidIfNumber(null));
final String a2 = ObjUtil.cloneIfPossible(a); Assert.assertFalse(ObjUtil.isValidIfNumber(Double.NEGATIVE_INFINITY));
Assert.assertNotSame(a, a2); Assert.assertFalse(ObjUtil.isValidIfNumber(Double.NaN));
Assert.assertTrue(ObjUtil.isValidIfNumber(Double.MIN_VALUE));
Assert.assertFalse(ObjUtil.isValidIfNumber(Float.NEGATIVE_INFINITY));
Assert.assertFalse(ObjUtil.isValidIfNumber(Float.NaN));
Assert.assertTrue(ObjUtil.isValidIfNumber(Float.MIN_VALUE));
} }
@Test
public void compareTest() {
Assert.assertEquals(0, ObjUtil.compare(1, 1));
Assert.assertEquals(1, ObjUtil.compare(1, null));
Assert.assertEquals(-1, ObjUtil.compare(null, 1));
Assert.assertEquals(-1, ObjUtil.compare(1, null, true));
Assert.assertEquals(1, ObjUtil.compare(null, 1, true));
}
@Test
public void getTypeArgumentTest() {
final Bean bean = new Bean(1);
Assert.assertEquals(Integer.class, ObjUtil.getTypeArgument(bean));
Assert.assertEquals(String.class, ObjUtil.getTypeArgument(bean, 1));
}
@Test
public void toStringTest() {
Assert.assertEquals("null", ObjUtil.toString(null));
Assert.assertEquals(Collections.emptyMap().toString(), ObjUtil.toString(Collections.emptyMap()));
Assert.assertEquals("[1, 2]", Arrays.asList("1", "2").toString());
}
@RequiredArgsConstructor
@EqualsAndHashCode
private static class ExceptionCloneableBean implements Cloneable {
private final Integer id;
@Override
protected Object clone() throws CloneNotSupportedException {
throw new RuntimeException("can not clone this object");
}
}
@RequiredArgsConstructor
@EqualsAndHashCode
private static class CloneableBean implements Cloneable {
private final Integer id;
@Override
protected Object clone() throws CloneNotSupportedException {
return super.clone();
}
}
@RequiredArgsConstructor
@EqualsAndHashCode
private static class SerializableBean implements Serializable {
private final Integer id;
}
@RequiredArgsConstructor
@EqualsAndHashCode
private static class Bean implements TypeArgument<Integer, String> {
private final Integer id;
}
private interface TypeArgument<A, B> {};
} }