!960 [enhancement] 使LambdaFactory.build支持构造器

Merge pull request !960 from 阿超/v6-dev
This commit is contained in:
Looly 2023-03-15 07:56:29 +00:00 committed by Gitee
commit d9cd597256
No known key found for this signature in database
GPG Key ID: 173E9B9CA92EEF8F
2 changed files with 36 additions and 19 deletions

View File

@ -7,9 +7,12 @@ import cn.hutool.core.lang.mutable.MutableEntry;
import cn.hutool.core.map.WeakConcurrentMap; import cn.hutool.core.map.WeakConcurrentMap;
import cn.hutool.core.reflect.LookupFactory; import cn.hutool.core.reflect.LookupFactory;
import cn.hutool.core.reflect.MethodUtil; import cn.hutool.core.reflect.MethodUtil;
import cn.hutool.core.reflect.ReflectUtil;
import java.io.Serializable; import java.io.Serializable;
import java.lang.invoke.*; import java.lang.invoke.*;
import java.lang.reflect.Constructor;
import java.lang.reflect.Executable;
import java.lang.reflect.Method; import java.lang.reflect.Method;
import java.lang.reflect.Modifier; import java.lang.reflect.Modifier;
import java.util.Arrays; import java.util.Arrays;
@ -31,7 +34,7 @@ public class LambdaFactory {
throw new IllegalAccessException(); throw new IllegalAccessException();
} }
private static final Map<MutableEntry<Class<?>, Method>, Object> CACHE = new WeakConcurrentMap<>(); private static final Map<MutableEntry<Class<?>, Executable>, Object> CACHE = new WeakConcurrentMap<>();
/** /**
* 构建Lambda * 构建Lambda
@ -61,30 +64,37 @@ public class LambdaFactory {
* 构建Lambda * 构建Lambda
* *
* @param functionInterfaceType 接受Lambda的函数式接口类型 * @param functionInterfaceType 接受Lambda的函数式接口类型
* @param method 方法对象 * @param executable 方法对象支持构造器
* @param <F> Function类型
* @return 接受Lambda的函数式接口对象 * @return 接受Lambda的函数式接口对象
* @param <F> Function类型
*/ */
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
public static <F> F build(final Class<F> functionInterfaceType, final Method method) { public static <F> F build(final Class<F> functionInterfaceType, final Executable executable) {
Assert.notNull(functionInterfaceType); Assert.notNull(functionInterfaceType);
Assert.notNull(method); Assert.notNull(executable);
final MutableEntry<Class<?>, Method> cacheKey = new MutableEntry<>(functionInterfaceType, method); final MutableEntry<Class<?>, Executable> cacheKey = new MutableEntry<>(functionInterfaceType, executable);
return (F) CACHE.computeIfAbsent(cacheKey, key -> { return (F) CACHE.computeIfAbsent(cacheKey, key -> {
final List<Method> abstractMethods = Arrays.stream(functionInterfaceType.getMethods()) final List<Method> abstractMethods = Arrays.stream(functionInterfaceType.getMethods())
.filter(m -> Modifier.isAbstract(m.getModifiers())) .filter(m -> Modifier.isAbstract(m.getModifiers()))
.collect(Collectors.toList()); .collect(Collectors.toList());
Assert.equals(abstractMethods.size(), 1, "不支持非函数式接口"); Assert.equals(abstractMethods.size(), 1, "不支持非函数式接口");
if (!method.isAccessible()) { ReflectUtil.setAccessible(executable);
method.setAccessible(true);
}
final Method invokeMethod = abstractMethods.get(0); final Method invokeMethod = abstractMethods.get(0);
final MethodHandles.Lookup caller = LookupFactory.lookup(method.getDeclaringClass()); final MethodHandles.Lookup caller = LookupFactory.lookup(executable.getDeclaringClass());
final String invokeName = invokeMethod.getName(); final String invokeName = invokeMethod.getName();
final MethodType invokedType = methodType(functionInterfaceType); final MethodType invokedType = methodType(functionInterfaceType);
final MethodType samMethodType = methodType(invokeMethod.getReturnType(), invokeMethod.getParameterTypes()); final MethodType samMethodType = methodType(invokeMethod.getReturnType(), invokeMethod.getParameterTypes());
final MethodHandle implMethod = Opt.ofTry(() -> caller.unreflect(method)).get(); final MethodHandle implMethod;
final MethodType insMethodType = methodType(method.getReturnType(), method.getDeclaringClass(), method.getParameterTypes()); final MethodType instantiatedMethodType;
if (executable instanceof Method) {
final Method method = (Method) executable;
implMethod = ((SerSupplier<MethodHandle>) () -> MethodHandles.lookup().unreflect(method)).get();
instantiatedMethodType = MethodType.methodType(method.getReturnType(), method.getDeclaringClass(), method.getParameterTypes());
} else {
final Constructor<?> constructor = (Constructor<?>) executable;
implMethod = ((SerSupplier<MethodHandle>) () -> MethodHandles.lookup().unreflectConstructor(constructor)).get();
instantiatedMethodType = MethodType.methodType(constructor.getDeclaringClass(), constructor.getParameterTypes());
}
final boolean isSerializable = Serializable.class.isAssignableFrom(functionInterfaceType); final boolean isSerializable = Serializable.class.isAssignableFrom(functionInterfaceType);
try { try {
final CallSite callSite = isSerializable ? final CallSite callSite = isSerializable ?
@ -94,7 +104,7 @@ public class LambdaFactory {
invokedType, invokedType,
samMethodType, samMethodType,
implMethod, implMethod,
insMethodType, instantiatedMethodType,
FLAG_SERIALIZABLE FLAG_SERIALIZABLE
) : ) :
LambdaMetafactory.metafactory( LambdaMetafactory.metafactory(
@ -103,7 +113,7 @@ public class LambdaFactory {
invokedType, invokedType,
samMethodType, samMethodType,
implMethod, implMethod,
insMethodType instantiatedMethodType
); );
//noinspection unchecked //noinspection unchecked
return (F) callSite.getTarget().invoke(); return (F) callSite.getTarget().invoke();

View File

@ -12,11 +12,8 @@ import org.junit.Test;
import org.junit.runner.RunWith; import org.junit.runner.RunWith;
import org.junit.runners.Parameterized; import org.junit.runners.Parameterized;
import java.lang.invoke.LambdaConversionException; import java.lang.invoke.*;
import java.lang.invoke.MethodHandle; import java.lang.reflect.Constructor;
import java.lang.invoke.MethodHandleProxies;
import java.lang.invoke.MethodHandles;
import java.lang.invoke.MethodType;
import java.lang.reflect.Method; import java.lang.reflect.Method;
import java.util.Arrays; import java.util.Arrays;
import java.util.Collection; import java.util.Collection;
@ -24,6 +21,7 @@ import java.util.Comparator;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
import java.util.function.BiConsumer; import java.util.function.BiConsumer;
import java.util.function.Function; import java.util.function.Function;
import java.util.function.Supplier;
/** /**
* @author nasodaengineer * @author nasodaengineer
@ -160,6 +158,15 @@ public class LambdaFactoryTest {
loop(count, tasks); loop(count, tasks);
} }
@Test
public void testConstructor() {
Constructor<Something> constructor = ((SerSupplier<Constructor<Something>>) Something.class::getConstructor).get();
Supplier<Something> constructorLambda = LambdaFactory.build(Supplier.class, constructor);
// constructorLambda can be cache or transfer
Something something = constructorLambda.get();
Assert.assertEquals(Something.class, something.getClass());
}
/** /**
* <p>hardCode 运行1次耗时 7600 NANOSECONDS * <p>hardCode 运行1次耗时 7600 NANOSECONDS
* <p>lambda 运行1次耗时 12400 NANOSECONDS * <p>lambda 运行1次耗时 12400 NANOSECONDS