From c4a2be8120317c7095ccf021c62f1387c82595e9 Mon Sep 17 00:00:00 2001 From: achao Date: Sat, 4 Jun 2022 00:22:27 +0800 Subject: [PATCH 1/4] =?UTF-8?q?=E9=87=8D=E6=9E=84LambdaUtil=EF=BC=8C?= =?UTF-8?q?=E5=BC=BA=E5=8C=96=E5=8E=9F=E6=9C=89=E7=9A=84SerializedLambda?= =?UTF-8?q?=E7=BC=93=E5=AD=98=E4=B8=BA=E8=87=AA=E5=AE=9A=E4=B9=89LambdaInf?= =?UTF-8?q?o=EF=BC=8C=E8=AF=A5=E7=B1=BB=E9=A2=9D=E5=A4=96=E5=AD=98?= =?UTF-8?q?=E5=82=A8=E4=BA=86lambda=E5=8F=8D=E5=B0=84=E7=B1=BB=EF=BC=8C?= =?UTF-8?q?=E4=B8=94=E5=8F=AF=E4=BB=A5=E8=8E=B7=E5=8F=96=E5=88=B0lambda?= =?UTF-8?q?=E4=B8=AD=E4=BD=BF=E7=94=A8=E7=9A=84=E5=A4=96=E9=83=A8=E5=8F=98?= =?UTF-8?q?=E9=87=8F=E5=8F=82=E6=95=B0;=E9=A2=9D=E5=A4=96=E5=9C=A8ReflectU?= =?UTF-8?q?til=E6=96=B0=E5=A2=9E=E4=BA=86=E9=83=A8=E5=88=86=E5=87=BD?= =?UTF-8?q?=E6=95=B0=EF=BC=8C=E7=94=A8=E4=BA=8E=E8=8E=B7=E5=8F=96=E6=8F=8F?= =?UTF-8?q?=E8=BF=B0=E7=AC=A6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../cn/hutool/core/lang/func/LambdaInfo.java | 78 +++++++++++ .../cn/hutool/core/lang/func/LambdaUtil.java | 126 ++++++++--------- .../cn/hutool/core/reflect/ReflectUtil.java | 104 ++++++++++++++ .../hutool/core/lang/func/LambdaUtilTest.java | 127 ++++++++++-------- 4 files changed, 313 insertions(+), 122 deletions(-) create mode 100644 hutool-core/src/main/java/cn/hutool/core/lang/func/LambdaInfo.java diff --git a/hutool-core/src/main/java/cn/hutool/core/lang/func/LambdaInfo.java b/hutool-core/src/main/java/cn/hutool/core/lang/func/LambdaInfo.java new file mode 100644 index 000000000..3fcf9ffa5 --- /dev/null +++ b/hutool-core/src/main/java/cn/hutool/core/lang/func/LambdaInfo.java @@ -0,0 +1,78 @@ +package cn.hutool.core.lang.func; + +import cn.hutool.core.classloader.ClassLoaderUtil; +import cn.hutool.core.reflect.FieldUtil; +import cn.hutool.core.text.StrUtil; + +import java.lang.invoke.SerializedLambda; +import java.lang.reflect.Constructor; +import java.lang.reflect.Executable; +import java.lang.reflect.Method; +import java.lang.reflect.Type; + +/** + * 存放lambda信息 + * + * @author VampireAchao + */ +public class LambdaInfo { + + private Type instantiatedType; + private final Type[] parameterTypes; + private final Type returnType; + private final String name; + private final Executable executable; + private final Class clazz; + private final SerializedLambda lambda; + + public LambdaInfo(Executable executable, SerializedLambda lambda) { + if (executable instanceof Method) { + Method method = (Method) executable; + this.parameterTypes = method.getGenericParameterTypes(); + this.returnType = method.getGenericReturnType(); + this.name = method.getName(); + } else if (executable instanceof Constructor) { + Constructor constructor = (Constructor) executable; + this.parameterTypes = constructor.getGenericParameterTypes(); + this.returnType = constructor.getDeclaringClass(); + this.name = constructor.getName(); + } else { + throw new IllegalArgumentException("Unsupported executable type: " + executable.getClass()); + } + int index = lambda.getInstantiatedMethodType().indexOf(";)"); + if (index > -1) { + this.instantiatedType = ClassLoaderUtil.loadClass(StrUtil.sub(lambda.getInstantiatedMethodType(), 2, index)); + } + this.clazz = (Class) FieldUtil.getFieldValue(executable, "clazz"); + this.executable = executable; + this.lambda = lambda; + } + + public Type getInstantiatedType() { + return instantiatedType; + } + + public Type[] getParameterTypes() { + return parameterTypes; + } + + public Type getReturnType() { + return returnType; + } + + public String getName() { + return name; + } + + public Executable getExecutable() { + return executable; + } + + public Class getClazz() { + return clazz; + } + + public SerializedLambda getLambda() { + return lambda; + } +} 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 df348b6a6..d2c7ee38a 100755 --- 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 @@ -2,13 +2,18 @@ package cn.hutool.core.lang.func; import cn.hutool.core.bean.BeanUtil; import cn.hutool.core.classloader.ClassLoaderUtil; +import cn.hutool.core.exceptions.UtilException; +import cn.hutool.core.lang.Opt; import cn.hutool.core.map.WeakConcurrentMap; import cn.hutool.core.reflect.MethodUtil; -import cn.hutool.core.text.StrUtil; +import cn.hutool.core.reflect.ReflectUtil; import java.io.Serializable; -import java.lang.invoke.MethodHandleInfo; import java.lang.invoke.SerializedLambda; +import java.lang.reflect.Constructor; +import java.lang.reflect.Method; +import java.lang.reflect.Proxy; +import java.util.Objects; /** * Lambda相关工具类 @@ -18,7 +23,7 @@ import java.lang.invoke.SerializedLambda; */ public class LambdaUtil { - private static final WeakConcurrentMap cache = new WeakConcurrentMap<>(); + private static final WeakConcurrentMap CACHE = new WeakConcurrentMap<>(); /** * 通过对象的方法或类的静态方法引用,获取lambda实现类 @@ -50,39 +55,49 @@ public class LambdaUtil { * @param func lambda * @param 类型 * @return lambda实现类 - * @throws IllegalArgumentException 如果是不支持的方法引用,抛出该异常,见{@link LambdaUtil#checkLambdaTypeCanGetClass} - * @since 5.8.0 * @author VampireAchao */ - public static Class getRealClass(final Func0 func) { - final SerializedLambda lambda = resolve(func); - checkLambdaTypeCanGetClass(lambda.getImplMethodKind()); - return ClassLoaderUtil.loadClass(lambda.getImplClass()); + @SuppressWarnings("unchecked") + public static Class getRealClass(final Serializable func) { + LambdaInfo lambdaInfo = resolve(func); + return (Class) Opt.of(lambdaInfo).map(LambdaInfo::getInstantiatedType).orElseGet(lambdaInfo::getClazz); } /** * 解析lambda表达式,加了缓存。 * 该缓存可能会在任意不定的时间被清除 * - * @param Lambda类型 * @param func 需要解析的 lambda 对象(无参方法) * @return 返回解析后的结果 */ - public static SerializedLambda resolve(final Func1 func) { - return _resolve(func); - } - - /** - * 解析lambda表达式,加了缓存。 - * 该缓存可能会在任意不定的时间被清除 - * - * @param Lambda返回类型 - * @param func 需要解析的 lambda 对象(无参方法) - * @return 返回解析后的结果 - * @since 5.7.23 - */ - public static SerializedLambda resolve(final Func0 func) { - return _resolve(func); + public static LambdaInfo resolve(final Serializable func) { + return CACHE.computeIfAbsent(func.getClass().getName(), (key) -> { + final SerializedLambda serializedLambda = _resolve(func); + final String methodName = serializedLambda.getImplMethodName(); + final Class implClass; + ClassLoaderUtil.loadClass(serializedLambda.getImplClass().replace("/", "."), true); + try { + implClass = Class.forName(serializedLambda.getImplClass().replace("/", "."), true, Thread.currentThread().getContextClassLoader()); + } catch (ClassNotFoundException e) { + throw new UtilException(e); + } + if ("".equals(methodName)) { + for (Constructor constructor : implClass.getDeclaredConstructors()) { + if (ReflectUtil.getDescriptor(constructor).equals(serializedLambda.getImplMethodSignature())) { + return new LambdaInfo(constructor, serializedLambda); + } + } + } else { + Method[] methods = MethodUtil.getMethods(implClass); + for (Method method : methods) { + if (method.getName().equals(methodName) + && ReflectUtil.getDescriptor(method).equals(serializedLambda.getImplMethodSignature())) { + return new LambdaInfo(method, serializedLambda); + } + } + } + throw new IllegalStateException("No lambda method found."); + }); } /** @@ -93,7 +108,7 @@ public class LambdaUtil { * @return 函数名称 */ public static

String getMethodName(final Func1 func) { - return resolve(func).getImplMethodName(); + return resolve(func).getName(); } /** @@ -105,36 +120,7 @@ public class LambdaUtil { * @since 5.7.23 */ public static String getMethodName(final Func0 func) { - return resolve(func).getImplMethodName(); - } - - /** - * 通过对象的方法或类的静态方法引用,然后根据{@link SerializedLambda#getInstantiatedMethodType()}获取lambda实现类
- * 传入lambda有参数且含有返回值的情况能够匹配到此方法: - *

    - *
  • 引用特定类型的任意对象的实例方法:
    {@code
    -	 * Class functionClass = LambdaUtil.getRealClass(MyTeacher::getAge);
    -	 * Assert.assertEquals(MyTeacher.class, functionClass);
    -	 * }
  • - *
  • 引用静态带参方法:
    {@code
    -	 * Class staticFunctionClass = LambdaUtil.getRealClass(MyTeacher::takeAgeBy);
    -	 * Assert.assertEquals(MyTeacher.class, staticFunctionClass);
    -	 * }
  • - *
- * - * @param func lambda - * @param

方法调用方类型 - * @param 返回值类型 - * @return lambda实现类 - * @throws IllegalArgumentException 如果是不支持的方法引用,抛出该异常,见{@link LambdaUtil#checkLambdaTypeCanGetClass} - * @since 5.8.0 - * @author VampireAchao - */ - public static Class

getRealClass(final Func1 func) { - final SerializedLambda lambda = resolve(func); - checkLambdaTypeCanGetClass(lambda.getImplMethodKind()); - final String instantiatedMethodType = lambda.getInstantiatedMethodType(); - return ClassLoaderUtil.loadClass(StrUtil.sub(instantiatedMethodType, 2, StrUtil.indexOf(instantiatedMethodType, ';'))); + return resolve(func).getName(); } /** @@ -176,18 +162,6 @@ public class LambdaUtil { } //region Private methods - /** - * 检查是否为支持的类型 - * - * @param implMethodKind 支持的lambda类型 - * @throws IllegalArgumentException 如果是不支持的方法引用,抛出该异常 - */ - private static void checkLambdaTypeCanGetClass(final int implMethodKind) { - if (implMethodKind != MethodHandleInfo.REF_invokeVirtual && - implMethodKind != MethodHandleInfo.REF_invokeStatic) { - throw new IllegalArgumentException("该lambda不是合适的方法引用"); - } - } /** * 解析lambda表达式,加了缓存。 @@ -202,7 +176,21 @@ public class LambdaUtil { * @return 返回解析后的结果 */ private static SerializedLambda _resolve(final Serializable func) { - return cache.computeIfAbsent(func.getClass().getName(), (key) -> MethodUtil.invoke(func, "writeReplace")); + if (func instanceof SerializedLambda) { + return (SerializedLambda) func; + } + if (func instanceof Proxy) { + throw new UtilException("not support proxy, just for now"); + } + final Class clazz = func.getClass(); + if (!clazz.isSynthetic()) { + throw new UtilException("Not a lambda expression: " + clazz.getName()); + } + final Object serLambda = MethodUtil.invoke(func, "writeReplace"); + if (Objects.nonNull(serLambda) && serLambda instanceof SerializedLambda) { + return (SerializedLambda) serLambda; + } + throw new UtilException("writeReplace result value is not java.lang.invoke.SerializedLambda"); } //endregion } diff --git a/hutool-core/src/main/java/cn/hutool/core/reflect/ReflectUtil.java b/hutool-core/src/main/java/cn/hutool/core/reflect/ReflectUtil.java index 238730465..047529562 100644 --- a/hutool-core/src/main/java/cn/hutool/core/reflect/ReflectUtil.java +++ b/hutool-core/src/main/java/cn/hutool/core/reflect/ReflectUtil.java @@ -1,6 +1,9 @@ package cn.hutool.core.reflect; import java.lang.reflect.AccessibleObject; +import java.lang.reflect.Constructor; +import java.lang.reflect.Executable; +import java.lang.reflect.Method; /** * 反射工具类 @@ -24,4 +27,105 @@ public class ReflectUtil { } return accessibleObject; } + + /** + * 获取描述符 + * + * @param executable 可执行的反射对象 + * @return 描述符 + */ + public static String getDescriptor(Executable executable) { + StringBuilder stringBuilder = new StringBuilder(); + stringBuilder.append('('); + Class[] parameters = executable.getParameterTypes(); + for (Class parameter : parameters) { + appendDescriptor(parameter, stringBuilder); + } + if (executable instanceof Method) { + Method method = (Method) executable; + stringBuilder.append(')'); + appendDescriptor(method.getReturnType(), stringBuilder); + return stringBuilder.toString(); + } else if (executable instanceof Constructor) { + return stringBuilder.append(")V").toString(); + } + throw new IllegalArgumentException("Unknown Executable: " + executable); + } + + private static void appendDescriptor(Class clazz, StringBuilder stringBuilder) { + Class currentClass; + for (currentClass = clazz; + currentClass.isArray(); + currentClass = currentClass.getComponentType()) { + stringBuilder.append('['); + } + if (currentClass.isPrimitive()) { + final char descriptor; + // see sun.invoke.util.Wrapper + // These must be in the order defined for widening primitive conversions in JLS 5.1.2 + if (currentClass == boolean.class) { + descriptor = 'Z'; + } else if (currentClass == byte.class) { + descriptor = 'B'; + } else if (currentClass == short.class) { + descriptor = 'S'; + } else if (currentClass == char.class) { + descriptor = 'C'; + } else if (currentClass == int.class) { + descriptor = 'I'; + } else if (currentClass == long.class) { + descriptor = 'J'; + } else if (currentClass == float.class) { + descriptor = 'F'; + } else if (currentClass == double.class) { + descriptor = 'D'; + } else if (currentClass == Object.class) { + descriptor = 'L'; + } else if (currentClass == void.class) { + // VOID must be the last type, since it is "assignable" from any other type: + descriptor = 'V'; + } else { + throw new AssertionError(); + } + stringBuilder.append(descriptor); + } else { + stringBuilder.append('L').append(currentClass.getName().replace('.', '/')).append(';'); + } + + } + + private static char getDescriptorChar(Class currentClass) { + if (currentClass == Boolean.class || currentClass == boolean.class) { + return 'Z'; + } + if (currentClass == Byte.class || currentClass == byte.class) { + return 'B'; + } + if (currentClass == Short.class || currentClass == short.class) { + return 'S'; + } + if (currentClass == Character.class || currentClass == char.class) { + return 'C'; + } + if (currentClass == Integer.class || currentClass == int.class) { + return 'I'; + } + if (currentClass == Long.class || currentClass == long.class) { + return 'J'; + } + if (currentClass == Float.class || currentClass == float.class) { + return 'F'; + } + if (currentClass == Double.class || currentClass == double.class) { + return 'D'; + } + if (currentClass == Object.class) { + return 'L'; + } + if (currentClass == Void.class || currentClass == void.class) { + return 'V'; + } + throw new AssertionError(); + } + } 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 33925e651..b3cee7aac 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,14 +1,15 @@ package cn.hutool.core.lang.func; -import org.junit.Assert; -import org.junit.Test; - import lombok.AllArgsConstructor; import lombok.Data; import lombok.EqualsAndHashCode; +import org.junit.Assert; +import org.junit.Test; -import java.lang.invoke.MethodHandleInfo; +import java.lang.reflect.Array; +import java.util.stream.Stream; +@SuppressWarnings("unchecked") public class LambdaUtilTest { @Test @@ -25,60 +26,80 @@ public class LambdaUtilTest { @Test public void resolveTest() { - // 引用构造函数 - Assert.assertEquals(MethodHandleInfo.REF_newInvokeSpecial, - LambdaUtil.resolve(MyTeacher::new).getImplMethodKind()); - // 数组构造函数引用 - Assert.assertEquals(MethodHandleInfo.REF_invokeStatic, - LambdaUtil.resolve(MyTeacher[]::new).getImplMethodKind()); - // 引用静态方法 - Assert.assertEquals(MethodHandleInfo.REF_invokeStatic, - LambdaUtil.resolve(MyTeacher::takeAge).getImplMethodKind()); - // 引用特定对象的实例方法 - Assert.assertEquals(MethodHandleInfo.REF_invokeVirtual, - LambdaUtil.resolve(new MyTeacher()::getAge).getImplMethodKind()); - // 引用特定类型的任意对象的实例方法 - Assert.assertEquals(MethodHandleInfo.REF_invokeVirtual, - LambdaUtil.resolve(MyTeacher::getAge).getImplMethodKind()); + Stream.of(() -> { + // 引用构造函数 + Func0 lambda = MyTeacher::new; + LambdaInfo lambdaInfo = LambdaUtil.resolve(lambda); + Assert.assertEquals(0, lambdaInfo.getParameterTypes().length); + Assert.assertEquals(MyTeacher.class, lambdaInfo.getReturnType()); + }, () -> { + // 数组构造函数引用(此处数组构造参数) + Func1 lambda = MyTeacher[]::new; + LambdaInfo lambdaInfo = LambdaUtil.resolve(lambda); + Assert.assertEquals(int.class, lambdaInfo.getParameterTypes()[0]); + Assert.assertEquals(MyTeacher.class, ((Class) lambdaInfo.getReturnType()).getComponentType()); + }, () -> { + // 引用静态方法 + Func0 noArgsStaticMethod = MyTeacher::takeAge; + LambdaInfo lambdaInfo = LambdaUtil.resolve(noArgsStaticMethod); + Assert.assertEquals(String.class, lambdaInfo.getReturnType()); + }, () -> { + // 引用特定对象的实例方法 + Func0 instantiated = new MyTeacher()::getAge; + LambdaInfo lambdaInfo = LambdaUtil.resolve(instantiated); + Assert.assertEquals(String.class, lambdaInfo.getReturnType()); + }, () -> { + // 引用特定类型的任意对象的实例方法 + Func1 annoInstantiated = MyTeacher::getAge; + LambdaInfo lambdaInfo = LambdaUtil.resolve(annoInstantiated); + Assert.assertEquals(String.class, lambdaInfo.getReturnType()); + }).forEach(Runnable::run); } - @Test public void getRealClassTest() { - // 引用特定类型的任意对象的实例方法 - final Class functionClass = LambdaUtil.getRealClass(MyTeacher::getAge); - Assert.assertEquals(MyTeacher.class, functionClass); - // 枚举测试,不会导致类型擦除 - final Class enumFunctionClass = LambdaUtil.getRealClass(LambdaKindEnum::ordinal); - Assert.assertEquals(LambdaKindEnum.class, enumFunctionClass); - // 调用父类方法,能获取到正确的子类类型 - final Class superFunctionClass = LambdaUtil.getRealClass(MyTeacher::getId); - Assert.assertEquals(MyTeacher.class, superFunctionClass); - final MyTeacher myTeacher = new MyTeacher(); - // 引用特定对象的实例方法 - final Class supplierClass = LambdaUtil.getRealClass(myTeacher::getAge); - Assert.assertEquals(MyTeacher.class, supplierClass); - // 枚举测试,只能获取到枚举类型 - final Class> enumSupplierClass = LambdaUtil.getRealClass(LambdaKindEnum.REF_NONE::ordinal); - Assert.assertEquals(Enum.class, enumSupplierClass); - // 调用父类方法,只能获取到父类类型 - final Class> superSupplierClass = LambdaUtil.getRealClass(myTeacher::getId); - Assert.assertEquals(Entity.class, superSupplierClass); - - // 引用静态带参方法,能够获取到正确的参数类型 - final Class staticFunctionClass = LambdaUtil.getRealClass(MyTeacher::takeAgeBy); - Assert.assertEquals(MyTeacher.class, staticFunctionClass); - // 引用父类静态带参方法,只能获取到父类类型 - final Class> staticSuperFunctionClass = LambdaUtil.getRealClass(MyTeacher::takeId); - Assert.assertEquals(Entity.class, staticSuperFunctionClass); - - // 引用静态无参方法,能够获取到正确的类型 - final Class staticSupplierClass = LambdaUtil.getRealClass(MyTeacher::takeAge); - Assert.assertEquals(MyTeacher.class, staticSupplierClass); - // 引用父类静态无参方法,能够获取到正确的参数类型 - final Class staticSuperSupplierClass = LambdaUtil.getRealClass(MyTeacher::takeIdBy); - Assert.assertEquals(MyTeacher.class, staticSuperSupplierClass); + Stream.of(() -> { + // 引用特定类型的任意对象的实例方法 + final Func1 lambda = MyTeacher::getAge; + Assert.assertEquals(MyTeacher.class, LambdaUtil.getRealClass(lambda)); + }, () -> { + // 枚举测试,不会导致类型擦除 + final Func1 lambda = LambdaKindEnum::ordinal; + Assert.assertEquals(LambdaKindEnum.class, LambdaUtil.getRealClass(lambda)); + }, () -> { + // 调用父类方法,能获取到正确的子类类型 + final Func1 lambda = MyTeacher::getId; + Assert.assertEquals(MyTeacher.class, LambdaUtil.getRealClass(lambda)); + }, () -> { + // 引用特定对象的实例方法 + Func0 lambda = myTeacher::getAge; + Assert.assertEquals(MyTeacher.class, LambdaUtil.getRealClass(lambda)); + }, () -> { + // 枚举测试,只能获取到枚举类型 + Func0 lambda = LambdaKindEnum.REF_NONE::ordinal; + Assert.assertEquals(Enum.class, LambdaUtil.getRealClass(lambda)); + }, () -> { + // 调用父类方法,只能获取到父类类型 + VoidFunc0 lambda = myTeacher::getId; + Assert.assertEquals(Entity.class, LambdaUtil.getRealClass(lambda)); + }, () -> { + // 引用静态带参方法,能够获取到正确的参数类型 + Func1 lambda = MyTeacher::takeAgeBy; + Assert.assertEquals(MyTeacher.class, LambdaUtil.getRealClass(lambda)); + }, () -> { + // 引用父类静态带参方法,只能获取到父类类型 + Func0 lambda = MyTeacher::takeId; + Assert.assertEquals(Entity.class, LambdaUtil.getRealClass(lambda)); + }, () -> { + // 引用静态无参方法,能够获取到正确的类型 + Func0 lambda = MyTeacher::takeAge; + Assert.assertEquals(MyTeacher.class, LambdaUtil.getRealClass(lambda)); + }, () -> { + // 引用父类静态无参方法,能够获取到正确的参数类型 + Func1 lambda = MyTeacher::takeIdBy; + Assert.assertEquals(MyTeacher.class, LambdaUtil.getRealClass(lambda)); + }).forEach(Runnable::run); } @Data From ccc71cd61a74702f1563330c93881a2d594df64d Mon Sep 17 00:00:00 2001 From: achao Date: Sat, 4 Jun 2022 00:43:30 +0800 Subject: [PATCH 2/4] =?UTF-8?q?=E7=B2=BE=E7=AE=80LambdaUtil?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../cn/hutool/core/lang/func/LambdaUtil.java | 39 ++----------------- 1 file changed, 3 insertions(+), 36 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 d2c7ee38a..b6a7726f7 100755 --- 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 @@ -103,23 +103,10 @@ public class LambdaUtil { /** * 获取lambda表达式函数(方法)名称 * - * @param

Lambda参数类型 * @param func 函数(无参方法) * @return 函数名称 */ - public static

String getMethodName(final Func1 func) { - return resolve(func).getName(); - } - - /** - * 获取lambda表达式函数(方法)名称 - * - * @param Lambda返回类型 - * @param func 函数(无参方法) - * @return 函数名称 - * @since 5.7.23 - */ - public static String getMethodName(final Func0 func) { + public static String getMethodName(final Serializable func) { return resolve(func).getName(); } @@ -132,32 +119,12 @@ public class LambdaUtil { *

  • 其它不满足规则的方法名抛出{@link IllegalArgumentException}
  • * * - * @param Lambda类型 - * @param func 函数(无参方法) - * @return 方法名称 - * @throws IllegalArgumentException 非Getter或Setter方法 - * @since 5.7.10 - */ - public static String getFieldName(final Func1 func) throws IllegalArgumentException { - return BeanUtil.getFieldName(getMethodName(func)); - } - - /** - * 获取lambda表达式Getter或Setter函数(方法)对应的字段名称,规则如下: - *
      - *
    • getXxxx获取为xxxx,如getName得到name。
    • - *
    • setXxxx获取为xxxx,如setName得到name。
    • - *
    • isXxxx获取为xxxx,如isName得到name。
    • - *
    • 其它不满足规则的方法名抛出{@link IllegalArgumentException}
    • - *
    - * - * @param Lambda类型 - * @param func 函数(无参方法) + * @param func 函数 * @return 方法名称 * @throws IllegalArgumentException 非Getter或Setter方法 * @since 5.7.23 */ - public static String getFieldName(final Func0 func) throws IllegalArgumentException { + public static String getFieldName(final Serializable func) throws IllegalArgumentException { return BeanUtil.getFieldName(getMethodName(func)); } From d5174b6b64db1d4ca36fe850877c896388773fcd Mon Sep 17 00:00:00 2001 From: achao Date: Sat, 4 Jun 2022 01:18:41 +0800 Subject: [PATCH 3/4] =?UTF-8?q?=E5=AE=8C=E5=96=84LambdaUtil?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../cn/hutool/core/lang/func/LambdaInfo.java | 14 ++++-- .../cn/hutool/core/lang/func/LambdaUtil.java | 11 +++-- .../hutool/core/lang/func/LambdaUtilTest.java | 45 +++++++++++++++---- 3 files changed, 52 insertions(+), 18 deletions(-) diff --git a/hutool-core/src/main/java/cn/hutool/core/lang/func/LambdaInfo.java b/hutool-core/src/main/java/cn/hutool/core/lang/func/LambdaInfo.java index 3fcf9ffa5..7a5eca69d 100644 --- a/hutool-core/src/main/java/cn/hutool/core/lang/func/LambdaInfo.java +++ b/hutool-core/src/main/java/cn/hutool/core/lang/func/LambdaInfo.java @@ -17,7 +17,7 @@ import java.lang.reflect.Type; */ public class LambdaInfo { - private Type instantiatedType; + private final Type[] instantiatedTypes; private final Type[] parameterTypes; private final Type returnType; private final String name; @@ -41,15 +41,21 @@ public class LambdaInfo { } int index = lambda.getInstantiatedMethodType().indexOf(";)"); if (index > -1) { - this.instantiatedType = ClassLoaderUtil.loadClass(StrUtil.sub(lambda.getInstantiatedMethodType(), 2, index)); + String[] instantiatedTypeNames = StrUtil.sub(lambda.getInstantiatedMethodType(), 2, index).split(";L"); + this.instantiatedTypes = new Type[instantiatedTypeNames.length]; + for (int i = 0; i < instantiatedTypeNames.length; i++) { + this.instantiatedTypes[i] = ClassLoaderUtil.loadClass(instantiatedTypeNames[i]); + } + } else { + instantiatedTypes = new Type[0]; } this.clazz = (Class) FieldUtil.getFieldValue(executable, "clazz"); this.executable = executable; this.lambda = lambda; } - public Type getInstantiatedType() { - return instantiatedType; + public Type[] getInstantiatedTypes() { + return instantiatedTypes; } public Type[] getParameterTypes() { 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 b6a7726f7..fc28793f7 100755 --- 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 @@ -60,7 +60,7 @@ public class LambdaUtil { @SuppressWarnings("unchecked") public static Class getRealClass(final Serializable func) { LambdaInfo lambdaInfo = resolve(func); - return (Class) Opt.of(lambdaInfo).map(LambdaInfo::getInstantiatedType).orElseGet(lambdaInfo::getClazz); + return (Class) Opt.of(lambdaInfo).map(LambdaInfo::getInstantiatedTypes).filter(types -> types.length != 0).map(types -> types[types.length - 1]).orElseGet(lambdaInfo::getClazz); } /** @@ -131,12 +131,11 @@ public class LambdaUtil { //region Private methods /** - * 解析lambda表达式,加了缓存。 - * 该缓存可能会在任意不定的时间被清除。 + * 解析lambda表达式,没加缓存 * *

    * 通过反射调用实现序列化接口函数对象的writeReplace方法,从而拿到{@link SerializedLambda}
    - * 该对象中包含了lambda表达式的所有信息。 + * 该对象中包含了lambda表达式的大部分信息。 *

    * * @param func 需要解析的 lambda 对象 @@ -147,11 +146,11 @@ public class LambdaUtil { return (SerializedLambda) func; } if (func instanceof Proxy) { - throw new UtilException("not support proxy, just for now"); + throw new IllegalArgumentException("not support proxy, just for now"); } final Class clazz = func.getClass(); if (!clazz.isSynthetic()) { - throw new UtilException("Not a lambda expression: " + clazz.getName()); + throw new IllegalArgumentException("Not a lambda expression: " + clazz.getName()); } final Object serLambda = MethodUtil.invoke(func, "writeReplace"); if (Objects.nonNull(serLambda) && serLambda instanceof SerializedLambda) { 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 b3cee7aac..37fb1833e 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 @@ -6,7 +6,9 @@ import lombok.EqualsAndHashCode; import org.junit.Assert; import org.junit.Test; +import java.io.Serializable; import java.lang.reflect.Array; +import java.util.Objects; import java.util.stream.Stream; @SuppressWarnings("unchecked") @@ -14,13 +16,15 @@ public class LambdaUtilTest { @Test public void getMethodNameTest() { - final String methodName = LambdaUtil.getMethodName(MyTeacher::getAge); + Func1 lambda = MyTeacher::getAge; + final String methodName = LambdaUtil.getMethodName(lambda); Assert.assertEquals("getAge", methodName); } @Test public void getFieldNameTest() { - final String fieldName = LambdaUtil.getFieldName(MyTeacher::getAge); + Func1 lambda = MyTeacher::getAge; + final String fieldName = LambdaUtil.getFieldName(lambda); Assert.assertEquals("age", fieldName); } @@ -40,20 +44,45 @@ public class LambdaUtilTest { Assert.assertEquals(MyTeacher.class, ((Class) lambdaInfo.getReturnType()).getComponentType()); }, () -> { // 引用静态方法 - Func0 noArgsStaticMethod = MyTeacher::takeAge; - LambdaInfo lambdaInfo = LambdaUtil.resolve(noArgsStaticMethod); + Func0 lambda = MyTeacher::takeAge; + LambdaInfo lambdaInfo = LambdaUtil.resolve(lambda); + Assert.assertEquals(0, lambdaInfo.getParameterTypes().length); Assert.assertEquals(String.class, lambdaInfo.getReturnType()); }, () -> { // 引用特定对象的实例方法 - Func0 instantiated = new MyTeacher()::getAge; - LambdaInfo lambdaInfo = LambdaUtil.resolve(instantiated); + Func0 lambda = new MyTeacher()::getAge; + LambdaInfo lambdaInfo = LambdaUtil.resolve(lambda); + Assert.assertEquals(0, lambdaInfo.getParameterTypes().length); Assert.assertEquals(String.class, lambdaInfo.getReturnType()); }, () -> { // 引用特定类型的任意对象的实例方法 - Func1 annoInstantiated = MyTeacher::getAge; - LambdaInfo lambdaInfo = LambdaUtil.resolve(annoInstantiated); + Func1 lambda = MyTeacher::getAge; + LambdaInfo lambdaInfo = LambdaUtil.resolve(lambda); + Assert.assertEquals(0, lambdaInfo.getParameterTypes().length); Assert.assertEquals(String.class, lambdaInfo.getReturnType()); + }, () -> { + // 最最重要的!!! + Character character = '0'; + Integer integer = 0; + SerThiCons lambda = (obj, bool, str) -> { + Objects.nonNull(character); + Objects.nonNull(integer); + }; + LambdaInfo lambdaInfo = LambdaUtil.resolve(lambda); + // 获取闭包使用的参数类型 + Assert.assertEquals(Character.class, lambdaInfo.getParameterTypes()[0]); + Assert.assertEquals(Integer.class, lambdaInfo.getParameterTypes()[1]); + // 最后几个是原有lambda的参数类型 + Assert.assertEquals(Object.class, lambdaInfo.getParameterTypes()[2]); + Assert.assertEquals(Boolean.class, lambdaInfo.getParameterTypes()[3]); + Assert.assertEquals(String.class, lambdaInfo.getParameterTypes()[4]); + + Assert.assertEquals(void.class, lambdaInfo.getReturnType()); }).forEach(Runnable::run); + + } + + interface SerThiCons extends Consumer3, Serializable { } @Test From 08ecda4cd21bedd0e615bc2e2ea3380dcedccb3c Mon Sep 17 00:00:00 2001 From: achao Date: Sat, 4 Jun 2022 13:50:28 +0800 Subject: [PATCH 4/4] =?UTF-8?q?=E6=96=B0=E5=A2=9E=E5=AF=B9instantiatedType?= =?UTF-8?q?=E6=95=B0=E7=BB=84=E7=9A=84=E6=94=AF=E6=8C=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../cn/hutool/core/lang/func/LambdaInfo.java | 20 +++++++++++++------ .../hutool/core/lang/func/LambdaUtilTest.java | 4 ++++ 2 files changed, 18 insertions(+), 6 deletions(-) diff --git a/hutool-core/src/main/java/cn/hutool/core/lang/func/LambdaInfo.java b/hutool-core/src/main/java/cn/hutool/core/lang/func/LambdaInfo.java index 7a5eca69d..6012b6c0f 100644 --- a/hutool-core/src/main/java/cn/hutool/core/lang/func/LambdaInfo.java +++ b/hutool-core/src/main/java/cn/hutool/core/lang/func/LambdaInfo.java @@ -2,7 +2,6 @@ package cn.hutool.core.lang.func; import cn.hutool.core.classloader.ClassLoaderUtil; import cn.hutool.core.reflect.FieldUtil; -import cn.hutool.core.text.StrUtil; import java.lang.invoke.SerializedLambda; import java.lang.reflect.Constructor; @@ -41,13 +40,22 @@ public class LambdaInfo { } int index = lambda.getInstantiatedMethodType().indexOf(";)"); if (index > -1) { - String[] instantiatedTypeNames = StrUtil.sub(lambda.getInstantiatedMethodType(), 2, index).split(";L"); - this.instantiatedTypes = new Type[instantiatedTypeNames.length]; - for (int i = 0; i < instantiatedTypeNames.length; i++) { - this.instantiatedTypes[i] = ClassLoaderUtil.loadClass(instantiatedTypeNames[i]); + boolean isArray = lambda.getInstantiatedMethodType().startsWith("(["); + if (isArray) { + try { + this.instantiatedTypes = new Type[]{Class.forName(lambda.getInstantiatedMethodType().replace("/", ".").substring(0, index).substring(1) + ";")}; + } catch (ClassNotFoundException e) { + throw new IllegalStateException(e); + } + } else { + String[] instantiatedTypeNames = lambda.getInstantiatedMethodType().substring(2, index).split(";L"); + this.instantiatedTypes = new Type[instantiatedTypeNames.length]; + for (int i = 0; i < instantiatedTypeNames.length; i++) { + this.instantiatedTypes[i] = ClassLoaderUtil.loadClass(instantiatedTypeNames[i]); + } } } else { - instantiatedTypes = new Type[0]; + this.instantiatedTypes = new Type[0]; } this.clazz = (Class) FieldUtil.getFieldValue(executable, "clazz"); this.executable = executable; 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 37fb1833e..16660af6f 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 @@ -128,6 +128,10 @@ public class LambdaUtilTest { // 引用父类静态无参方法,能够获取到正确的参数类型 Func1 lambda = MyTeacher::takeIdBy; Assert.assertEquals(MyTeacher.class, LambdaUtil.getRealClass(lambda)); + }, () -> { + // 数组测试 + VoidFunc1 lambda = (String[] stringList) -> {}; + Assert.assertEquals(String[].class, LambdaUtil.getRealClass(lambda)); }).forEach(Runnable::run); }