From 40bc89511e5f8254ee835ed412b57846dd04a67a Mon Sep 17 00:00:00 2001 From: VampireAchao Date: Fri, 25 Mar 2022 19:34:24 +0800 Subject: [PATCH] =?UTF-8?q?=E7=BB=9F=E4=B8=80LambdaUtil#getRealClass?= =?UTF-8?q?=E6=96=B9=E6=B3=95=EF=BC=8C=E5=B9=B6=E6=8F=90=E4=BE=9B=E6=B5=8B?= =?UTF-8?q?=E8=AF=95=E7=94=A8=E4=BE=8B=EF=BC=8C=E8=BF=98=E5=AF=B9=E4=BC=A0?= =?UTF-8?q?=E5=85=A5lambda=E8=BF=9B=E8=A1=8C=E4=BA=86=E6=A0=A1=E9=AA=8C?= =?UTF-8?q?=EF=BC=8C=E4=B8=94=E5=AE=8C=E5=96=84=E4=BA=86=E6=B3=A8=E9=87=8A?= =?UTF-8?q?...?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../cn/hutool/core/lang/func/LambdaUtil.java | 165 ++++++++++++------ .../java/cn/hutool/core/util/EnumUtil.java | 18 +- .../hutool/core/lang/func/LambdaUtilTest.java | 109 +++++++++--- 3 files changed, 204 insertions(+), 88 deletions(-) diff --git a/hutool-core/src/main/java/cn/hutool/core/lang/func/LambdaUtil.java b/hutool-core/src/main/java/cn/hutool/core/lang/func/LambdaUtil.java index 96d535538..fec85ba75 100644 --- a/hutool-core/src/main/java/cn/hutool/core/lang/func/LambdaUtil.java +++ b/hutool-core/src/main/java/cn/hutool/core/lang/func/LambdaUtil.java @@ -1,14 +1,14 @@ package cn.hutool.core.lang.func; +import java.io.Serializable; +import java.lang.invoke.SerializedLambda; + import cn.hutool.core.bean.BeanUtil; import cn.hutool.core.lang.SimpleCache; import cn.hutool.core.util.ClassUtil; import cn.hutool.core.util.ReflectUtil; import cn.hutool.core.util.StrUtil; -import java.io.Serializable; -import java.lang.invoke.SerializedLambda; - /** * Lambda相关工具类 * @@ -19,6 +19,58 @@ public class LambdaUtil { private static final SimpleCache cache = new SimpleCache<>(); + /** + * 通过对象的方法或类的静态方法引用,获取lambda实现类 + * 传入lambda无参数但含有返回值的情况能够匹配到此方法: + *
{@code
+	 * @Data
+	 * @EqualsAndHashCode(callSuper = true)
+	 * static class MyTeacher extends Entity {
+	 *
+	 * 	public String age;
+	 *
+	 * 	public static String takeAge() {
+	 * 		return new MyTeacher().getAge();
+	 *    }
+	 *
+	 * }
+	 * }
+ * + * 在以下场景无法获取到正确类型 + *
{@code
+	 * // 枚举测试,只能获取到枚举类型
+	 * Class> enumSupplierClass = LambdaUtil.getRealClass(LambdaUtil.LambdaKindEnum.REF_NONE::ordinal);
+	 * Assert.assertEquals(Enum.class, enumSupplierClass);
+	 * // 调用父类方法,只能获取到父类类型
+	 * Class> superSupplierClass = LambdaUtil.getRealClass(myTeacher::getId);
+	 * Assert.assertEquals(Entity.class, superSupplierClass);
+	 * // 引用父类静态带参方法,只能获取到父类类型
+	 * Class> staticSuperFunctionClass = LambdaUtil.getRealClass(MyTeacher::takeId);
+	 * Assert.assertEquals(Entity.class, staticSuperFunctionClass);
+	 * }
+ * + * @param func lambda + * @param 类型 + * @return lambda实现类 + * @throws IllegalArgumentException 如果是不支持的方法引用,抛出该异常,见{@link LambdaUtil#checkLambdaTypeCanGetClass} + * @since 5.8.0 + */ + public static Class getRealClass(Func0 func) { + SerializedLambda lambda = resolve(func); + checkLambdaTypeCanGetClass(lambda.getImplMethodKind()); + return ClassUtil.loadClass(lambda.getImplClass()); + } + /** * 解析lambda表达式,加了缓存。 * 该缓存可能会在任意不定的时间被清除 @@ -68,72 +120,57 @@ public class LambdaUtil { } /** - * 通过对象的方法或类的静态方法引用,获取lambda实现类,两种情况匹配到此方法: + * 通过对象的方法或类的静态方法引用,然后根据{@link SerializedLambda#getInstantiatedMethodType()}获取lambda实现类
+ * 传入lambda有参数且含有返回值的情况能够匹配到此方法: + *
{@code
+	 * @Data
+	 * @EqualsAndHashCode(callSuper = true)
+	 * static class MyTeacher extends Entity {
+	 *
+	 * 	public String age;
+	 *
+	 * 	public static String takeAgeBy(MyTeacher myTeacher) {
+	 * 		return myTeacher.getAge();
+	 *    }
+	 *
+	 * }
+	 * }
*
    - *
  • 对象方法引用,如:myTeacher::getAge
  • - *
  • 类静态方法引用,如:MyTeacher::takeAge
  • + *
  • 引用特定类型的任意对象的实例方法:
    {@code
    +	 * Class functionClass = LambdaUtil.getRealClass(MyTeacher::getAge);
    +	 * Assert.assertEquals(MyTeacher.class, functionClass);
    +	 * }
  • + *
  • 引用静态带参方法:
    {@code
    +	 * Class staticFunctionClass = LambdaUtil.getRealClass(MyTeacher::takeAgeBy);
    +	 * Assert.assertEquals(MyTeacher.class, staticFunctionClass);
    +	 * }
  • *
- * 如想获取调用的方法引用所在类,可以: - *
-	 *     // 返回MyTeacher.class
-	 *     LambdaUtil.getImplClass(myTeacher::getAge);
-	 * 
* * @param func lambda - * @param 类型 + * @param

方法调用方类型 + * @param 返回值类型 * @return lambda实现类 + * @throws IllegalArgumentException 如果是不支持的方法引用,抛出该异常,见{@link LambdaUtil#checkLambdaTypeCanGetClass} * @since 5.8.0 */ - public static Class getImplClass(Func0 func) { - return ClassUtil.loadClass(resolve(func).getImplClass()); - } - - /** - * 通过类的方法引用,获取lambda实现类
- * 类方法引用,相当于获取的方法引用是:MyTeacher.getAge(this) - * 如想获取调用的方法引用所在类,可以: - *

-	 *     // 返回MyTeacher.class
-	 *     LambdaUtil.getImplClass(MyTeacher::getAge);
-	 * 
- * - * @param func lambda - * @param 类型 - * @return lambda实现类 - * @since 5.8.0 - */ - public static Class getImplClass(Func1 func) { - return ClassUtil.loadClass(resolve(func).getImplClass()); - } - - /** - * 通过{@link SerializedLambda#getInstantiatedMethodType()}获取lambda实现类
- * 在使用{@link #getImplClass(Func0)}获取实现类的时候,如果传入的是父类方法引用,会返回父类,导致问题
- * 此类通过方法的名称,截取出类名 - * - * @param func lambda - * @param

类型 - * @return lambda实现类 - * @since 5.8.0 - */ - public static

Class

getInstantiatedClass(Func0 func) { - final String instantiatedMethodType = resolve(func).getInstantiatedMethodType(); + public static Class

getRealClass(Func1 func) { + SerializedLambda lambda = resolve(func); + checkLambdaTypeCanGetClass(lambda.getImplMethodKind()); + String instantiatedMethodType = lambda.getInstantiatedMethodType(); return ClassUtil.loadClass(StrUtil.sub(instantiatedMethodType, 2, StrUtil.indexOf(instantiatedMethodType, ';'))); } /** - * 通过{@link SerializedLambda#getInstantiatedMethodType()}获取lambda实现类
- * 在使用{@link #getImplClass(Func1)}获取实现类的时候,如果传入的是父类方法引用,会返回父类,导致问题
- * 此类通过方法的名称,截取出类名 + * 检查是否为支持的类型 * - * @param func lambda - * @param

类型 - * @return lambda实现类 - * @since 5.8.0 + * @param implMethodKind 支持的lambda类型 + * @throws IllegalArgumentException 如果是不支持的方法引用,抛出该异常 */ - public static

Class

getInstantiatedClass(Func1 func) { - final String instantiatedMethodType = resolve(func).getInstantiatedMethodType(); - return ClassUtil.loadClass(StrUtil.sub(instantiatedMethodType, 2, StrUtil.indexOf(instantiatedMethodType, ';'))); + private static void checkLambdaTypeCanGetClass(int implMethodKind) { + if (implMethodKind != LambdaKindEnum.REF_invokeVirtual.ordinal() && + implMethodKind != LambdaKindEnum.REF_invokeStatic.ordinal()) { + throw new IllegalArgumentException("该lambda不是合适的方法引用"); + } } /** @@ -184,4 +221,20 @@ public class LambdaUtil { private static SerializedLambda _resolve(Serializable func) { return cache.get(func.getClass().getName(), () -> ReflectUtil.invoke(func, "writeReplace")); } + + /** + * Lambda类型枚举 + */ + public enum LambdaKindEnum { + REF_NONE, + REF_getField, + REF_getStatic, + REF_putField, + REF_putStatic, + REF_invokeVirtual, + REF_invokeStatic, + REF_invokeSpecial, + REF_newInvokeSpecial, + } + } diff --git a/hutool-core/src/main/java/cn/hutool/core/util/EnumUtil.java b/hutool-core/src/main/java/cn/hutool/core/util/EnumUtil.java index af9823f02..aa7c788d1 100644 --- a/hutool-core/src/main/java/cn/hutool/core/util/EnumUtil.java +++ b/hutool-core/src/main/java/cn/hutool/core/util/EnumUtil.java @@ -1,10 +1,5 @@ package cn.hutool.core.util; -import cn.hutool.core.lang.Assert; -import cn.hutool.core.lang.func.Func1; -import cn.hutool.core.lang.func.LambdaUtil; -import cn.hutool.core.map.MapUtil; - import java.lang.reflect.Field; import java.util.ArrayList; import java.util.Arrays; @@ -14,6 +9,11 @@ import java.util.Map; import java.util.function.Function; import java.util.function.Predicate; +import cn.hutool.core.lang.Assert; +import cn.hutool.core.lang.func.Func1; +import cn.hutool.core.lang.func.LambdaUtil; +import cn.hutool.core.map.MapUtil; + /** * 枚举工具类 * @@ -239,9 +239,9 @@ public class EnumUtil { * @return 对应枚举 ,获取不到时为 {@code null} */ public static , C> E getBy(Func1 condition, C value) { - Class implClass = LambdaUtil.getImplClass(condition); + Class implClass = LambdaUtil.getRealClass(condition); if (Enum.class.equals(implClass)) { - implClass = LambdaUtil.getInstantiatedClass(condition); + implClass = LambdaUtil.getRealClass(condition); } return Arrays.stream(implClass.getEnumConstants()).filter(e -> condition.callWithRuntimeException(e).equals(value)).findAny().orElse(null); } @@ -260,9 +260,9 @@ public class EnumUtil { */ public static , F, C> F getFieldBy(Func1 field, Function condition, C value) { - Class implClass = LambdaUtil.getImplClass(field); + Class implClass = LambdaUtil.getRealClass(field); if (Enum.class.equals(implClass)) { - implClass = LambdaUtil.getInstantiatedClass(field); + implClass = LambdaUtil.getRealClass(field); } return Arrays.stream(implClass.getEnumConstants()) // 过滤 diff --git a/hutool-core/src/test/java/cn/hutool/core/lang/func/LambdaUtilTest.java b/hutool-core/src/test/java/cn/hutool/core/lang/func/LambdaUtilTest.java index 316153fd3..2facb9336 100644 --- a/hutool-core/src/test/java/cn/hutool/core/lang/func/LambdaUtilTest.java +++ b/hutool-core/src/test/java/cn/hutool/core/lang/func/LambdaUtilTest.java @@ -1,9 +1,12 @@ package cn.hutool.core.lang.func; -import lombok.Data; import org.junit.Assert; import org.junit.Test; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.EqualsAndHashCode; + public class LambdaUtilTest { @Test @@ -19,38 +22,98 @@ public class LambdaUtilTest { } @Test - public void getImplClassTest() { - // 类方法引用,相当于获取的方法引用是:MyTeacher.getAge(this) - // 因此此处会匹配到Func1,其参数就是this - Class aClass = LambdaUtil.getImplClass(MyTeacher::getAge); - Assert.assertEquals(MyTeacher.class, aClass); - - MyTeacher myTeacher = new MyTeacher(); - - // 对象方法引用,因为已经有了对象,因此此处引用相当于获取:myTeacher.getAge() - aClass = LambdaUtil.getImplClass(myTeacher::getAge); - Assert.assertEquals(MyTeacher.class, aClass); - - // 静态方法引用,相当于获取:MyTeader.takeAge - aClass = LambdaUtil.getImplClass(MyTeacher::takeAge); - Assert.assertEquals(MyTeacher.class, aClass); + public void resolveTest() { + // 引用构造函数 + Assert.assertEquals(LambdaUtil.LambdaKindEnum.REF_newInvokeSpecial.ordinal(), + LambdaUtil.resolve(MyTeacher::new).getImplMethodKind()); + // 数组构造函数引用 + Assert.assertEquals(LambdaUtil.LambdaKindEnum.REF_invokeStatic.ordinal(), + LambdaUtil.resolve(MyTeacher[]::new).getImplMethodKind()); + // 引用静态方法 + Assert.assertEquals(LambdaUtil.LambdaKindEnum.REF_invokeStatic.ordinal(), + LambdaUtil.resolve(MyTeacher::takeAge).getImplMethodKind()); + // 引用特定对象的实例方法 + Assert.assertEquals(LambdaUtil.LambdaKindEnum.REF_invokeVirtual.ordinal(), + LambdaUtil.resolve(new MyTeacher()::getAge).getImplMethodKind()); + // 引用特定类型的任意对象的实例方法 + Assert.assertEquals(LambdaUtil.LambdaKindEnum.REF_invokeVirtual.ordinal(), + LambdaUtil.resolve(MyTeacher::getAge).getImplMethodKind()); } + @Test - public void getInstantiatedClassTest() { - // 类方法引用,相当于获取的方法引用是:MyTeacher.getAge(this) - // 因此此处会匹配到Func1,其参数就是this - Class aClass = LambdaUtil.getInstantiatedClass(MyTeacher::getAge); - Assert.assertEquals(MyTeacher.class, aClass); + public void getRealClassTest() { + // 引用特定类型的任意对象的实例方法 + Class functionClass = LambdaUtil.getRealClass(MyTeacher::getAge); + Assert.assertEquals(MyTeacher.class, functionClass); + // 枚举测试,不会导致类型擦除 + Class enumFunctionClass = LambdaUtil.getRealClass(LambdaUtil.LambdaKindEnum::ordinal); + Assert.assertEquals(LambdaUtil.LambdaKindEnum.class, enumFunctionClass); + // 调用父类方法,能获取到正确的子类类型 + Class superFunctionClass = LambdaUtil.getRealClass(MyTeacher::getId); + Assert.assertEquals(MyTeacher.class, superFunctionClass); + + MyTeacher myTeacher = new MyTeacher(); + // 引用特定对象的实例方法 + Class supplierClass = LambdaUtil.getRealClass(myTeacher::getAge); + Assert.assertEquals(MyTeacher.class, supplierClass); + // 枚举测试,只能获取到枚举类型 + Class> enumSupplierClass = LambdaUtil.getRealClass(LambdaUtil.LambdaKindEnum.REF_NONE::ordinal); + Assert.assertEquals(Enum.class, enumSupplierClass); + // 调用父类方法,只能获取到父类类型 + Class> superSupplierClass = LambdaUtil.getRealClass(myTeacher::getId); + Assert.assertEquals(Entity.class, superSupplierClass); + + // 引用静态带参方法,能够获取到正确的参数类型 + Class staticFunctionClass = LambdaUtil.getRealClass(MyTeacher::takeAgeBy); + Assert.assertEquals(MyTeacher.class, staticFunctionClass); + // 引用父类静态带参方法,只能获取到父类类型 + Class> staticSuperFunctionClass = LambdaUtil.getRealClass(MyTeacher::takeId); + Assert.assertEquals(Entity.class, staticSuperFunctionClass); + + // 引用静态无参方法,能够获取到正确的类型 + Class staticSupplierClass = LambdaUtil.getRealClass(MyTeacher::takeAge); + Assert.assertEquals(MyTeacher.class, staticSupplierClass); + // 引用父类静态无参方法,能够获取到正确的参数类型 + Class staticSuperSupplierClass = LambdaUtil.getRealClass(MyTeacher::takeIdBy); + Assert.assertEquals(MyTeacher.class, staticSuperSupplierClass); } @Data - static class MyTeacher { + @AllArgsConstructor + static class MyStudent { - public static String takeAge(){ + private String name; + } + + @Data + public static class Entity { + + private T id; + + public static T takeId() { + return new Entity().getId(); + } + + public static T takeIdBy(Entity entity) { + return entity.getId(); + } + + + } + + @Data + @EqualsAndHashCode(callSuper = true) + static class MyTeacher extends Entity { + + public static String takeAge() { return new MyTeacher().getAge(); } + public static String takeAgeBy(MyTeacher myTeacher) { + return myTeacher.getAge(); + } + public String age; } }