修复LambdaUtil对多参数且参数为包含数组的lambda的支持;优化clazz获取逻辑,直接从executable获取而不是反射

This commit is contained in:
achao 2022-08-18 15:54:49 +08:00 committed by VampireAchao
parent f536f4f47a
commit 6bccf59079
2 changed files with 30 additions and 18 deletions

View File

@ -1,7 +1,7 @@
package cn.hutool.core.lang.func;
import cn.hutool.core.classloader.ClassLoaderUtil;
import cn.hutool.core.reflect.FieldUtil;
import cn.hutool.core.text.StrPool;
import java.lang.invoke.SerializedLambda;
import java.lang.reflect.Constructor;
@ -30,34 +30,42 @@ public class LambdaInfo {
this.parameterTypes = method.getGenericParameterTypes();
this.returnType = method.getGenericReturnType();
this.name = method.getName();
this.clazz = method.getDeclaringClass();
} else if (executable instanceof Constructor) {
final Constructor<?> constructor = (Constructor<?>) executable;
this.parameterTypes = constructor.getGenericParameterTypes();
this.returnType = constructor.getDeclaringClass();
this.name = constructor.getName();
this.clazz = constructor.getDeclaringClass();
} else {
throw new IllegalArgumentException("Unsupported executable type: " + executable.getClass());
}
final int index = lambda.getInstantiatedMethodType().indexOf(";)");
if (index > -1) {
final boolean isArray = lambda.getInstantiatedMethodType().startsWith("([");
if (isArray) {
try {
this.instantiatedTypes = new Type[]{Class.forName(lambda.getInstantiatedMethodType().replace("/", ".").substring(0, index).substring(1) + ";")};
} catch (final ClassNotFoundException e) {
throw new IllegalStateException(e);
}
} else {
final 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]);
final String className = lambda.getInstantiatedMethodType().substring(1, index + 1);
final String[] instantiatedTypeNames = className.split(";");
final Type[] types = new Type[instantiatedTypeNames.length];
for (int i = 0; i < instantiatedTypeNames.length; i++) {
final boolean isArray = instantiatedTypeNames[i].startsWith(StrPool.BRACKET_START);
if (isArray && !instantiatedTypeNames[i].endsWith(";")) {
// 如果是数组需要以 ";" 结尾才能加载
instantiatedTypeNames[i] += ";";
} else {
if (instantiatedTypeNames[i].startsWith("L")) {
// 如果以 "L" 开头删除 L
instantiatedTypeNames[i] = instantiatedTypeNames[i].substring(1);
}
if (instantiatedTypeNames[i].endsWith(";")) {
// 如果以 ";" 结尾删除 ";"
instantiatedTypeNames[i] = instantiatedTypeNames[i].substring(0, instantiatedTypeNames[i].length() - 1);
}
}
types[i] = ClassLoaderUtil.loadClass(instantiatedTypeNames[i]);
}
this.instantiatedTypes = types;
} else {
this.instantiatedTypes = new Type[0];
}
this.clazz = (Class<?>) FieldUtil.getFieldValue(executable, "clazz");
this.executable = executable;
this.lambda = lambda;
}

View File

@ -7,11 +7,9 @@ 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")
public class LambdaUtilTest {
@Test
@ -29,7 +27,7 @@ public class LambdaUtilTest {
}
@Test
public void resolveTest() {
public <T> void resolveTest() {
Stream.<Runnable>of(() -> {
// 引用构造函数
final Func0<MyTeacher> lambda = MyTeacher::new;
@ -41,7 +39,7 @@ public class LambdaUtilTest {
final Func1<Integer, MyTeacher[]> lambda = MyTeacher[]::new;
final LambdaInfo lambdaInfo = LambdaUtil.resolve(lambda);
Assert.assertEquals(int.class, lambdaInfo.getParameterTypes()[0]);
Assert.assertEquals(MyTeacher.class, ((Class<Array>) lambdaInfo.getReturnType()).getComponentType());
Assert.assertEquals(MyTeacher[].class, lambdaInfo.getReturnType());
}, () -> {
// 引用静态方法
final Func0<String> lambda = MyTeacher::takeAge;
@ -80,6 +78,12 @@ public class LambdaUtilTest {
Assert.assertEquals(String.class, lambdaInfo.getParameterTypes()[4]);
Assert.assertEquals(void.class, lambdaInfo.getReturnType());
}, () -> {
// 一些特殊的lambda
Assert.assertEquals("T[]", LambdaUtil.<Func<Object, Stream<?>>>resolve(Stream::of).getParameterTypes()[0].getTypeName());
Assert.assertEquals(MyTeacher[][].class, LambdaUtil.<Func1<Integer, MyTeacher[][]>>resolve(MyTeacher[][]::new).getReturnType());
Assert.assertEquals(Integer[][][].class, LambdaUtil.<VoidFunc1<Integer[][][]>>resolve(a -> {}).getParameterTypes()[0]);
Assert.assertEquals(Integer[][][].class, LambdaUtil.resolve((Serializable & Consumer3<Integer[][][], Integer[][], Integer>) (a, b, c) -> {}).getParameterTypes()[0]);
}).forEach(Runnable::run);
}