From 58d28c0767e545e4ca808d21ea34ef03cb199fd5 Mon Sep 17 00:00:00 2001 From: wy Date: Sun, 11 Dec 2022 09:12:36 +0800 Subject: [PATCH 1/5] =?UTF-8?q?feat(core):=20=E6=96=B0=E5=A2=9ELambdaFacto?= =?UTF-8?q?ry?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../hutool/core/lang/func/LambdaFactory.java | 117 ++++++++ .../cn/hutool/core/lang/func/LambdaUtil.java | 64 +++++ .../core/lang/func/LambdaFactoryTest.java | 253 ++++++++++++++++++ .../hutool/core/lang/func/LambdaUtilTest.java | 89 +++++- 4 files changed, 520 insertions(+), 3 deletions(-) create mode 100644 hutool-core/src/main/java/cn/hutool/core/lang/func/LambdaFactory.java create mode 100644 hutool-core/src/test/java/cn/hutool/core/lang/func/LambdaFactoryTest.java diff --git a/hutool-core/src/main/java/cn/hutool/core/lang/func/LambdaFactory.java b/hutool-core/src/main/java/cn/hutool/core/lang/func/LambdaFactory.java new file mode 100644 index 000000000..f8c58d79f --- /dev/null +++ b/hutool-core/src/main/java/cn/hutool/core/lang/func/LambdaFactory.java @@ -0,0 +1,117 @@ +package cn.hutool.core.lang.func; + +import cn.hutool.core.lang.Assert; +import cn.hutool.core.lang.Opt; +import cn.hutool.core.lang.Tuple; +import cn.hutool.core.map.WeakConcurrentMap; +import cn.hutool.core.reflect.LookupFactory; +import cn.hutool.core.reflect.MethodUtil; + +import java.io.Serializable; +import java.lang.invoke.*; +import java.lang.reflect.Method; +import java.lang.reflect.Modifier; +import java.util.Arrays; +import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; + +import static java.lang.invoke.LambdaMetafactory.FLAG_SERIALIZABLE; +import static java.lang.invoke.MethodType.methodType; + +/** + * 以类似反射的方式动态创建Lambda,在性能上有一定优势,同时避免每次调用Lambda时创建匿名内部类 + * + * @author nasodaengineer + */ +public class LambdaFactory { + + private LambdaFactory() throws IllegalAccessException { + throw new IllegalAccessException(); + } + + private static final Map CACHE = new WeakConcurrentMap<>(); + + /** + * 构建Lambda + *
{@code
+	 * class Something {
+	 *     private Long id;
+	 *     private String name;
+	 *     // ... 省略GetterSetter方法
+	 * }
+	 * Function getIdFunction = LambdaFactory.buildLambda(Function.class, Something.class, "getId");
+	 * BiConsumer setNameConsumer = LambdaFactory.buildLambda(BiConsumer.class, Something.class, "setName", String.class);
+	 * }
+	 * 
+ * + * @param functionInterfaceType 接受Lambda的函数式接口类型 + * @param methodClass 声明方法的类的类型 + * @param methodName 方法名称 + * @param paramTypes 方法参数数组 + * @return 接受Lambda的函数式接口对象 + */ + public static F buildLambda(Class functionInterfaceType, Class methodClass, String methodName, Class... paramTypes) { + return buildLambda(functionInterfaceType, MethodUtil.getMethod(methodClass, methodName, paramTypes)); + } + + /** + * 构建Lambda + * + * @param functionInterfaceType 接受Lambda的函数式接口类型 + * @param method 方法对象 + * @return 接受Lambda的函数式接口对象 + */ + public static F buildLambda(Class functionInterfaceType, Method method) { + Assert.notNull(functionInterfaceType); + Assert.notNull(method); + Tuple cacheKey = new Tuple(functionInterfaceType, method); + Object cacheValue = CACHE.get(cacheKey); + if (null != cacheValue) { + //noinspection unchecked + return (F) cacheValue; + } + List abstractMethods = Arrays.stream(functionInterfaceType.getMethods()) + .filter(m -> Modifier.isAbstract(m.getModifiers())) + .collect(Collectors.toList()); + Assert.equals(abstractMethods.size(), 1, "不支持非函数式接口"); + if (!method.isAccessible()) { + method.setAccessible(true); + } + Method invokeMethod = abstractMethods.get(0); + MethodHandles.Lookup caller = LookupFactory.lookup(method.getDeclaringClass()); + String invokeName = invokeMethod.getName(); + MethodType invokedType = methodType(functionInterfaceType); + MethodType samMethodType = methodType(invokeMethod.getReturnType(), invokeMethod.getParameterTypes()); + MethodHandle implMethod = Opt.ofTry(() -> caller.unreflect(method)).get(); + MethodType insMethodType = methodType(method.getReturnType(), method.getDeclaringClass(), method.getParameterTypes()); + boolean isSerializable = Arrays.stream(functionInterfaceType.getInterfaces()).anyMatch(i -> i.isAssignableFrom(Serializable.class)); + CallSite callSite = Opt.ofTry(() -> isSerializable ? + LambdaMetafactory.altMetafactory( + caller, + invokeName, + invokedType, + samMethodType, + implMethod, + insMethodType, + FLAG_SERIALIZABLE + ) : + LambdaMetafactory.metafactory( + caller, + invokeName, + invokedType, + samMethodType, + implMethod, + insMethodType + )).get(); + + try { + //noinspection unchecked + F lambda = (F) callSite.getTarget().invoke(); + CACHE.put(cacheKey, lambda); + return lambda; + } catch (Throwable e) { + throw new RuntimeException(e); + } + } +} 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 239f845f0..b57824ac4 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 @@ -13,6 +13,8 @@ import java.lang.invoke.SerializedLambda; import java.lang.reflect.Constructor; import java.lang.reflect.Method; import java.lang.reflect.Proxy; +import java.util.function.BiConsumer; +import java.util.function.Function; /** * Lambda相关工具类 @@ -130,7 +132,69 @@ public class LambdaUtil { return BeanUtil.getFieldName(getMethodName(func)); } + /** + * 等效于 Obj::getXxx + * + * @param getMethod getter方法 + * @param 调用getter方法对象类型 + * @param getter方法返回值类型 + * @return Obj::getXxx + */ + public static Function getter(Method getMethod) { + return LambdaFactory.buildLambda(Function.class, getMethod); + } + /** + * 等效于 Obj::getXxx + * + * @param clazz 调用getter方法对象类 + * @param fieldName 字段名称 + * @param 调用getter方法对象类型 + * @param getter方法返回值类型 + * @return Obj::getXxx + */ + public static Function getter(Class clazz, String fieldName) { + return LambdaFactory.buildLambda(Function.class, BeanUtil.getBeanDesc(clazz).getGetter(fieldName)); + } + + /** + * 等效于 Obj::setXxx + * + * @param setMethod setter方法 + * @param 调用setter方法对象类型 + * @param

setter方法返回的值类型 + * @return Obj::setXxx + */ + public static BiConsumer setter(Method setMethod) { + return LambdaFactory.buildLambda(BiConsumer.class, setMethod); + } + + /** + * Obj::setXxx + * + * @param clazz 调用setter方法对象类 + * @param fieldName 字段名称 + * @param 调用setter方法对象类型 + * @param

setter方法返回的值类型 + * @return Obj::setXxx + */ + public static BiConsumer setter(Class clazz, String fieldName) { + return LambdaFactory.buildLambda(BiConsumer.class, BeanUtil.getBeanDesc(clazz).getSetter(fieldName)); + } + + /** + * 等效于 Obj::method + * + * @param lambdaType 接受lambda的函数式接口类型 + * @param clazz 调用类 + * @param methodName 方法名 + * @param paramsTypes 方法参数类型数组 + * @param 函数式接口类型 + * @return Obj::method + */ + public static F lambda(Class lambdaType, Class clazz, String methodName, Class... paramsTypes) { + return LambdaFactory.buildLambda(lambdaType, clazz, methodName, paramsTypes); + } //region Private methods diff --git a/hutool-core/src/test/java/cn/hutool/core/lang/func/LambdaFactoryTest.java b/hutool-core/src/test/java/cn/hutool/core/lang/func/LambdaFactoryTest.java new file mode 100644 index 000000000..4e1d3e6c8 --- /dev/null +++ b/hutool-core/src/test/java/cn/hutool/core/lang/func/LambdaFactoryTest.java @@ -0,0 +1,253 @@ +package cn.hutool.core.lang.func; + +import cn.hutool.core.collection.CollUtil; +import cn.hutool.core.collection.ListUtil; +import lombok.Data; +import lombok.Getter; +import lombok.Setter; +import lombok.SneakyThrows; +import org.junit.Assert; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.Parameterized; + +import java.lang.invoke.MethodHandleProxies; +import java.lang.invoke.MethodHandles; +import java.lang.reflect.Method; +import java.util.Arrays; +import java.util.Collection; +import java.util.Comparator; +import java.util.function.BiConsumer; +import java.util.function.Function; + +/** + * @author nasodaengineer + */ +public class LambdaFactoryTest { + + @Test(expected = RuntimeException.class) + public void testMethodNotMatch() { + LambdaFactory.buildLambda(Function.class, Something.class, "setId", Long.class); + } + + @Test + public void buildLambdaTest() { + Something something = new Something(); + something.setId(1L); + something.setName("name"); + + Function get11 = LambdaFactory.buildLambda(Function.class, Something.class, "getId"); + Function get12 = LambdaFactory.buildLambda(Function.class, Something.class, "getId"); + + Assert.assertEquals(get11, get12); + Assert.assertEquals(something.getId(), get11.apply(something)); + + String name = "sname"; + BiConsumer set = LambdaFactory.buildLambda(BiConsumer.class, Something.class, "setName", String.class); + set.accept(something, name); + + Assert.assertEquals(something.getName(), name); + } + + @Data + private static class Something { + private Long id; + private String name; + } + + /** + * 简单的性能测试,大多数情况下直接调用 快于 lambda 快于 反射 快于 代理 + * + * @author nasodaengineer + */ + @RunWith(Parameterized.class) + public static class PerformanceTest { + + @Parameterized.Parameter + public int count; + + @Parameterized.Parameters + public static Collection parameters() { + return ListUtil.of(1, 10, 100, 1000, 10000, 100000, 1000000, 10000000, 100000000); + } + + /** + *

hardCode 运行1次耗时 4600 ns + *

lambda 运行1次耗时 5400 ns + *

reflect 运行1次耗时 7100 ns + *

proxy 运行1次耗时 145400 ns + *

-------------------------------------------- + *

hardCode 运行10次耗时 1200 ns + *

lambda 运行10次耗时 1200 ns + *

proxy 运行10次耗时 10800 ns + *

reflect 运行10次耗时 20100 ns + *

-------------------------------------------- + *

lambda 运行100次耗时 6300 ns + *

hardCode 运行100次耗时 6400 ns + *

proxy 运行100次耗时 65100 ns + *

reflect 运行100次耗时 196800 ns + *

-------------------------------------------- + *

hardCode 运行1000次耗时 54100 ns + *

lambda 运行1000次耗时 82000 ns + *

reflect 运行1000次耗时 257300 ns + *

proxy 运行1000次耗时 822700 ns + *

-------------------------------------------- + *

hardCode 运行10000次耗时 84400 ns + *

lambda 运行10000次耗时 209200 ns + *

reflect 运行10000次耗时 1024300 ns + *

proxy 运行10000次耗时 1467300 ns + *

-------------------------------------------- + *

lambda 运行100000次耗时 618700 ns + *

hardCode 运行100000次耗时 675200 ns + *

reflect 运行100000次耗时 914100 ns + *

proxy 运行100000次耗时 2745800 ns + *

-------------------------------------------- + *

lambda 运行1000000次耗时 5342500 ns + *

hardCode 运行1000000次耗时 5616400 ns + *

reflect 运行1000000次耗时 9176700 ns + *

proxy 运行1000000次耗时 15801800 ns + *

-------------------------------------------- + *

lambda 运行10000000次耗时 53415200 ns + *

hardCode 运行10000000次耗时 63714500 ns + *

proxy 运行10000000次耗时 116420900 ns + *

reflect 运行10000000次耗时 120817900 ns + *

-------------------------------------------- + *

lambda 运行100000000次耗时 546706600 ns + *

hardCode 运行100000000次耗时 557174500 ns + *

reflect 运行100000000次耗时 924166200 ns + *

proxy 运行100000000次耗时 1862735900 ns + *

-------------------------------------------- + */ + @Test + @SneakyThrows + public void lambdaGetPerformanceTest() { + Something something = new Something(); + something.setId(1L); + something.setName("name"); + Method getByReflect = Something.class.getMethod("getId"); + Function getByProxy = MethodHandleProxies.asInterfaceInstance(Function.class, MethodHandles.lookup().unreflect(getByReflect)); + Function getByLambda = LambdaFactory.buildLambda(Function.class, getByReflect); + Task lambdaTask = new Task("lambda", () -> getByLambda.apply(something)); + Task proxyTask = new Task("proxy", () -> getByProxy.apply(something)); + Task reflectTask = new Task("reflect", () -> getByReflect.invoke(something)); + Task hardCodeTask = new Task("hardCode", () -> something.getId()); + Task[] tasks = {hardCodeTask, lambdaTask, proxyTask, reflectTask}; + loop(count, tasks); + } + + /** + *

hardCode 运行1次耗时 4800 ns + *

lambda 运行1次耗时 9100 ns + *

reflect 运行1次耗时 20600 ns + *

-------------------------------------------- + *

hardCode 运行10次耗时 1800 ns + *

lambda 运行10次耗时 2100 ns + *

reflect 运行10次耗时 24500 ns + *

-------------------------------------------- + *

hardCode 运行100次耗时 15700 ns + *

lambda 运行100次耗时 17500 ns + *

reflect 运行100次耗时 418200 ns + *

-------------------------------------------- + *

hardCode 运行1000次耗时 101700 ns + *

lambda 运行1000次耗时 157200 ns + *

reflect 运行1000次耗时 504900 ns + *

-------------------------------------------- + *

hardCode 运行10000次耗时 360800 ns + *

lambda 运行10000次耗时 371700 ns + *

reflect 运行10000次耗时 1887600 ns + *

-------------------------------------------- + *

lambda 运行100000次耗时 581500 ns + *

hardCode 运行100000次耗时 1629900 ns + *

reflect 运行100000次耗时 1781700 ns + *

-------------------------------------------- + *

lambda 运行1000000次耗时 175400 ns + *

hardCode 运行1000000次耗时 2045400 ns + *

reflect 运行1000000次耗时 14363200 ns + *

-------------------------------------------- + *

hardCode 运行10000000次耗时 60149000 ns + *

lambda 运行10000000次耗时 60502600 ns + *

reflect 运行10000000次耗时 187412800 ns + *

-------------------------------------------- + *

hardCode 运行100000000次耗时 562997300 ns + *

lambda 运行100000000次耗时 564359700 ns + *

reflect 运行100000000次耗时 1163617600 ns + * -------------------------------------------- + */ + @Test + @SneakyThrows + public void lambdaSetPerformanceTest() { + Something something = new Something(); + something.setId(1L); + something.setName("name"); + Method setByReflect = Something.class.getMethod("setName", String.class); + BiConsumer setByLambda = LambdaFactory.buildLambda(BiConsumer.class, setByReflect); + String name = "name1"; + Task lambdaTask = new Task("lambda", () -> { + setByLambda.accept(something, name); + return null; + }); + Task reflectTask = new Task("reflect", () -> { + setByReflect.invoke(something, name); + return null; + }); + Task hardCodeTask = new Task("hardCode", () -> { + something.setName(name); + return null; + }); + Task[] tasks = {hardCodeTask, lambdaTask, reflectTask}; + loop(count, tasks); + } + + @SneakyThrows + private void loop(int count, Task... tasks) { + Arrays.stream(tasks) + .peek(task -> { + LambdaFactoryTest.SupplierThrowable runnable = task.getRunnable(); + long cost = System.nanoTime(); + for (int i = 0; i < count; i++) { + runnable.get(); + } + cost = System.nanoTime() - cost; + task.setCost(cost); + task.setCount(count); + }) + .sorted(Comparator.comparing(Task::getCost)) + .map(Task::format) + .forEach(System.out::println); + System.out.println("--------------------------------------------"); + } + + @Getter + private class Task { + private String name; + private LambdaFactoryTest.SupplierThrowable runnable; + @Setter + private long cost; + @Setter + private Integer count; + + public Task(String name, LambdaFactoryTest.SupplierThrowable runnable) { + this.name = name; + this.runnable = runnable; + } + + public String format() { + return String.format("%-10s 运行%d次耗时 %d ns", name, count, cost); + } + } + + } + + @FunctionalInterface + interface SupplierThrowable { + T get0() throws Throwable; + + default T get() { + try { + return get0(); + } catch (Throwable e) { + throw new RuntimeException(e); + } + } + } +} 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 da6e3266e..08d0c144d 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,13 +1,19 @@ package cn.hutool.core.lang.func; +import cn.hutool.core.lang.Tuple; +import cn.hutool.core.reflect.MethodUtil; import lombok.AllArgsConstructor; import lombok.Data; import lombok.EqualsAndHashCode; +import lombok.experimental.FieldNameConstants; import org.junit.Assert; import org.junit.Test; import java.io.Serializable; import java.util.Objects; +import java.util.function.BiConsumer; +import java.util.function.BiFunction; +import java.util.function.Function; import java.util.stream.Stream; public class LambdaUtilTest { @@ -82,8 +88,10 @@ public class LambdaUtilTest { // 一些特殊的lambda Assert.assertEquals("T", LambdaUtil.>>resolve(Stream::of).getParameterTypes()[0].getTypeName()); Assert.assertEquals(MyTeacher[][].class, LambdaUtil.>resolve(MyTeacher[][]::new).getReturnType()); - Assert.assertEquals(Integer[][][].class, LambdaUtil.>resolve(a -> {}).getParameterTypes()[0]); - Assert.assertEquals(Integer[][][].class, LambdaUtil.resolve((Serializable & SerConsumer3) (a, b, c) -> {}).getParameterTypes()[0]); + Assert.assertEquals(Integer[][][].class, LambdaUtil.>resolve(a -> { + }).getParameterTypes()[0]); + Assert.assertEquals(Integer[][][].class, LambdaUtil.resolve((Serializable & SerConsumer3) (a, b, c) -> { + }).getParameterTypes()[0]); }).forEach(Runnable::run); } @@ -136,11 +144,86 @@ public class LambdaUtilTest { Assert.assertEquals(MyTeacher.class, LambdaUtil.getRealClass(lambda)); }, () -> { // 数组测试 - final SerConsumer lambda = (String[] stringList) -> {}; + final SerConsumer lambda = (String[] stringList) -> { + }; Assert.assertEquals(String[].class, LambdaUtil.getRealClass(lambda)); }).forEach(Runnable::run); } + @Test + public void getterTest() { + Bean bean = new Bean(); + bean.setId(2L); + + Function getId = cn.hutool.core.lang.func.LambdaUtil.getter(MethodUtil.getMethod(Bean.class, "getId")); + Function getId2 = cn.hutool.core.lang.func.LambdaUtil.getter(Bean.class, Bean.Fields.id); + + Assert.assertEquals(getId, getId2); + Assert.assertEquals(bean.getId(), getId.apply(bean)); + } + + @Test + public void setterTest() { + Bean bean = new Bean(); + bean.setId(2L); + bean.setFlag(false); + + BiConsumer setId = cn.hutool.core.lang.func.LambdaUtil.setter(MethodUtil.getMethod(Bean.class, "setId", Long.class)); + BiConsumer setId2 = cn.hutool.core.lang.func.LambdaUtil.setter(Bean.class, Bean.Fields.id); + BiConsumer setFlag = cn.hutool.core.lang.func.LambdaUtil.setter(Bean.class, Bean.Fields.flag); + Assert.assertEquals(setId, setId2); + + setId.accept(bean, 3L); + setFlag.accept(bean, true); + Assert.assertEquals(3L, (long) bean.getId()); + Assert.assertTrue(bean.isFlag()); + } + + @Test + public void lambdaTest() { + Bean bean = new Bean(); + bean.setId(1L); + bean.setPid(0L); + bean.setFlag(true); + BiFunction uniqueKeyFunction = LambdaUtil.lambda(BiFunction.class, Bean.class, "uniqueKey", String.class); + Function4 paramsFunction = LambdaUtil.lambda(Function4.class, Bean.class, "params", String.class, Integer.class, Double.class); + Assert.assertEquals(bean.uniqueKey("test"), uniqueKeyFunction.apply(bean, "test")); + Assert.assertEquals(bean.params("test", 1, 0.5), paramsFunction.apply(bean, "test", 1, 0.5)); + } + + @FunctionalInterface + interface Function4 { + R apply(P1 p1, P2 p2, P3 p3, P4 p4); + } + + @Data + @FieldNameConstants + private static class Bean { + Long id; + Long pid; + boolean flag; + + private Tuple uniqueKey(String name) { + return new Tuple(id, pid, flag, name); + } + + public Tuple params(String name, Integer length, Double score) { + return new Tuple(name, length, score); + } + + public static Function idGetter() { + return Bean::getId; + } + + public Function idGet() { + return bean -> bean.id; + } + + public Function idGetting() { + return Bean::getId; + } + } + @Data @AllArgsConstructor static class MyStudent { From 74db3733a229cc9e75f4c769e66cc3a8b44d5b4a Mon Sep 17 00:00:00 2001 From: wy Date: Sun, 11 Dec 2022 10:46:29 +0800 Subject: [PATCH 2/5] =?UTF-8?q?perf(core):=20=E6=94=B9=E7=94=A8=E7=BA=BF?= =?UTF-8?q?=E7=A8=8B=E5=AE=89=E5=85=A8=E7=9A=84=E6=96=B9=E5=BC=8F=E4=BB=8E?= =?UTF-8?q?=E7=BC=93=E5=AD=98=E4=B8=AD=E8=8E=B7=E5=8F=96lambda=E5=AF=B9?= =?UTF-8?q?=E8=B1=A1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../hutool/core/lang/func/LambdaFactory.java | 89 +++++++++---------- 1 file changed, 43 insertions(+), 46 deletions(-) diff --git a/hutool-core/src/main/java/cn/hutool/core/lang/func/LambdaFactory.java b/hutool-core/src/main/java/cn/hutool/core/lang/func/LambdaFactory.java index f8c58d79f..99e9e00bd 100644 --- a/hutool-core/src/main/java/cn/hutool/core/lang/func/LambdaFactory.java +++ b/hutool-core/src/main/java/cn/hutool/core/lang/func/LambdaFactory.java @@ -66,52 +66,49 @@ public class LambdaFactory { Assert.notNull(functionInterfaceType); Assert.notNull(method); Tuple cacheKey = new Tuple(functionInterfaceType, method); - Object cacheValue = CACHE.get(cacheKey); - if (null != cacheValue) { - //noinspection unchecked - return (F) cacheValue; - } - List abstractMethods = Arrays.stream(functionInterfaceType.getMethods()) - .filter(m -> Modifier.isAbstract(m.getModifiers())) - .collect(Collectors.toList()); - Assert.equals(abstractMethods.size(), 1, "不支持非函数式接口"); - if (!method.isAccessible()) { - method.setAccessible(true); - } - Method invokeMethod = abstractMethods.get(0); - MethodHandles.Lookup caller = LookupFactory.lookup(method.getDeclaringClass()); - String invokeName = invokeMethod.getName(); - MethodType invokedType = methodType(functionInterfaceType); - MethodType samMethodType = methodType(invokeMethod.getReturnType(), invokeMethod.getParameterTypes()); - MethodHandle implMethod = Opt.ofTry(() -> caller.unreflect(method)).get(); - MethodType insMethodType = methodType(method.getReturnType(), method.getDeclaringClass(), method.getParameterTypes()); - boolean isSerializable = Arrays.stream(functionInterfaceType.getInterfaces()).anyMatch(i -> i.isAssignableFrom(Serializable.class)); - CallSite callSite = Opt.ofTry(() -> isSerializable ? - LambdaMetafactory.altMetafactory( - caller, - invokeName, - invokedType, - samMethodType, - implMethod, - insMethodType, - FLAG_SERIALIZABLE - ) : - LambdaMetafactory.metafactory( - caller, - invokeName, - invokedType, - samMethodType, - implMethod, - insMethodType - )).get(); + //noinspection unchecked + return (F) CACHE.computeIfAbsent(cacheKey, key -> { + List abstractMethods = Arrays.stream(functionInterfaceType.getMethods()) + .filter(m -> Modifier.isAbstract(m.getModifiers())) + .collect(Collectors.toList()); + Assert.equals(abstractMethods.size(), 1, "不支持非函数式接口"); + if (!method.isAccessible()) { + method.setAccessible(true); + } + Method invokeMethod = abstractMethods.get(0); + MethodHandles.Lookup caller = LookupFactory.lookup(method.getDeclaringClass()); + String invokeName = invokeMethod.getName(); + MethodType invokedType = methodType(functionInterfaceType); + MethodType samMethodType = methodType(invokeMethod.getReturnType(), invokeMethod.getParameterTypes()); + MethodHandle implMethod = Opt.ofTry(() -> caller.unreflect(method)).get(); + MethodType insMethodType = methodType(method.getReturnType(), method.getDeclaringClass(), method.getParameterTypes()); + boolean isSerializable = Arrays.stream(functionInterfaceType.getInterfaces()).anyMatch(i -> i.isAssignableFrom(Serializable.class)); + CallSite callSite = Opt.ofTry(() -> isSerializable ? + LambdaMetafactory.altMetafactory( + caller, + invokeName, + invokedType, + samMethodType, + implMethod, + insMethodType, + FLAG_SERIALIZABLE + ) : + LambdaMetafactory.metafactory( + caller, + invokeName, + invokedType, + samMethodType, + implMethod, + insMethodType + )).get(); + + try { + //noinspection unchecked + return (F) callSite.getTarget().invoke(); + } catch (Throwable e) { + throw new RuntimeException(e); + } + }); - try { - //noinspection unchecked - F lambda = (F) callSite.getTarget().invoke(); - CACHE.put(cacheKey, lambda); - return lambda; - } catch (Throwable e) { - throw new RuntimeException(e); - } } } From 9daec07e5af94506881e0689b5c40ed678dc4b8e Mon Sep 17 00:00:00 2001 From: wy Date: Sun, 11 Dec 2022 12:17:39 +0800 Subject: [PATCH 3/5] =?UTF-8?q?perf(test):=20=E8=A1=A5=E5=85=85=E6=80=A7?= =?UTF-8?q?=E8=83=BD=E6=B5=8B=E8=AF=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../core/lang/func/LambdaFactoryTest.java | 24 +++++++++++++++---- 1 file changed, 20 insertions(+), 4 deletions(-) diff --git a/hutool-core/src/test/java/cn/hutool/core/lang/func/LambdaFactoryTest.java b/hutool-core/src/test/java/cn/hutool/core/lang/func/LambdaFactoryTest.java index 4e1d3e6c8..ee634b297 100644 --- a/hutool-core/src/test/java/cn/hutool/core/lang/func/LambdaFactoryTest.java +++ b/hutool-core/src/test/java/cn/hutool/core/lang/func/LambdaFactoryTest.java @@ -1,7 +1,7 @@ package cn.hutool.core.lang.func; -import cn.hutool.core.collection.CollUtil; import cn.hutool.core.collection.ListUtil; +import cn.hutool.core.reflect.MethodHandleUtil; import lombok.Data; import lombok.Getter; import lombok.Setter; @@ -11,12 +11,15 @@ import org.junit.Test; import org.junit.runner.RunWith; import org.junit.runners.Parameterized; +import java.lang.invoke.MethodHandle; import java.lang.invoke.MethodHandleProxies; import java.lang.invoke.MethodHandles; +import java.lang.invoke.MethodType; import java.lang.reflect.Method; import java.util.Arrays; import java.util.Collection; import java.util.Comparator; +import java.util.concurrent.TimeUnit; import java.util.function.BiConsumer; import java.util.function.Function; @@ -125,13 +128,15 @@ public class LambdaFactoryTest { something.setId(1L); something.setName("name"); Method getByReflect = Something.class.getMethod("getId"); + MethodHandle getByMh = MethodHandleUtil.findMethod(Something.class, "getId", MethodType.methodType(Long.class)); Function getByProxy = MethodHandleProxies.asInterfaceInstance(Function.class, MethodHandles.lookup().unreflect(getByReflect)); Function getByLambda = LambdaFactory.buildLambda(Function.class, getByReflect); Task lambdaTask = new Task("lambda", () -> getByLambda.apply(something)); + Task mhTask = new Task("mh", () -> getByMh.invoke(something)); Task proxyTask = new Task("proxy", () -> getByProxy.apply(something)); Task reflectTask = new Task("reflect", () -> getByReflect.invoke(something)); Task hardCodeTask = new Task("hardCode", () -> something.getId()); - Task[] tasks = {hardCodeTask, lambdaTask, proxyTask, reflectTask}; + Task[] tasks = {hardCodeTask, lambdaTask, mhTask, proxyTask, reflectTask}; loop(count, tasks); } @@ -180,12 +185,22 @@ public class LambdaFactoryTest { something.setId(1L); something.setName("name"); Method setByReflect = Something.class.getMethod("setName", String.class); + MethodHandle setByMh = MethodHandleUtil.findMethod(Something.class, "setName", MethodType.methodType(Void.TYPE, String.class)); + BiConsumer setByProxy = MethodHandleProxies.asInterfaceInstance(BiConsumer.class, setByMh); BiConsumer setByLambda = LambdaFactory.buildLambda(BiConsumer.class, setByReflect); String name = "name1"; Task lambdaTask = new Task("lambda", () -> { setByLambda.accept(something, name); return null; }); + Task proxyTask = new Task("proxy", () -> { + setByProxy.accept(something, name); + return null; + }); + Task mhTask = new Task("mh", () -> { + setByMh.invoke(something, name); + return null; + }); Task reflectTask = new Task("reflect", () -> { setByReflect.invoke(something, name); return null; @@ -194,7 +209,7 @@ public class LambdaFactoryTest { something.setName(name); return null; }); - Task[] tasks = {hardCodeTask, lambdaTask, reflectTask}; + Task[] tasks = {hardCodeTask, lambdaTask, proxyTask, mhTask, reflectTask}; loop(count, tasks); } @@ -232,7 +247,8 @@ public class LambdaFactoryTest { } public String format() { - return String.format("%-10s 运行%d次耗时 %d ns", name, count, cost); + TimeUnit timeUnit = TimeUnit.MILLISECONDS; + return String.format("%-10s 运行%d次耗时 %d %s", name, count, timeUnit.convert(cost, TimeUnit.NANOSECONDS), timeUnit.name()); } } From 344a292a3041d4dde6c23f5e8868182843cf447c Mon Sep 17 00:00:00 2001 From: wy Date: Sun, 11 Dec 2022 14:30:03 +0800 Subject: [PATCH 4/5] =?UTF-8?q?perf(core):=20=E6=9B=BF=E6=8D=A2=E7=BC=93?= =?UTF-8?q?=E5=AD=98=E9=94=AE=E7=B1=BB=E5=9E=8B=20Tuple=20->=20MutableEntr?= =?UTF-8?q?y?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../hutool/core/lang/func/LambdaFactory.java | 6 +- .../core/lang/func/LambdaFactoryTest.java | 157 ++++++++++-------- 2 files changed, 95 insertions(+), 68 deletions(-) diff --git a/hutool-core/src/main/java/cn/hutool/core/lang/func/LambdaFactory.java b/hutool-core/src/main/java/cn/hutool/core/lang/func/LambdaFactory.java index 99e9e00bd..aeb2df1c5 100644 --- a/hutool-core/src/main/java/cn/hutool/core/lang/func/LambdaFactory.java +++ b/hutool-core/src/main/java/cn/hutool/core/lang/func/LambdaFactory.java @@ -2,7 +2,7 @@ package cn.hutool.core.lang.func; import cn.hutool.core.lang.Assert; import cn.hutool.core.lang.Opt; -import cn.hutool.core.lang.Tuple; +import cn.hutool.core.lang.mutable.MutableEntry; import cn.hutool.core.map.WeakConcurrentMap; import cn.hutool.core.reflect.LookupFactory; import cn.hutool.core.reflect.MethodUtil; @@ -30,7 +30,7 @@ public class LambdaFactory { throw new IllegalAccessException(); } - private static final Map CACHE = new WeakConcurrentMap<>(); + private static final Map, Method>, Object> CACHE = new WeakConcurrentMap<>(); /** * 构建Lambda @@ -65,7 +65,7 @@ public class LambdaFactory { public static F buildLambda(Class functionInterfaceType, Method method) { Assert.notNull(functionInterfaceType); Assert.notNull(method); - Tuple cacheKey = new Tuple(functionInterfaceType, method); + MutableEntry, Method> cacheKey = new MutableEntry<>(functionInterfaceType, method); //noinspection unchecked return (F) CACHE.computeIfAbsent(cacheKey, key -> { List abstractMethods = Arrays.stream(functionInterfaceType.getMethods()) diff --git a/hutool-core/src/test/java/cn/hutool/core/lang/func/LambdaFactoryTest.java b/hutool-core/src/test/java/cn/hutool/core/lang/func/LambdaFactoryTest.java index ee634b297..5c8e99171 100644 --- a/hutool-core/src/test/java/cn/hutool/core/lang/func/LambdaFactoryTest.java +++ b/hutool-core/src/test/java/cn/hutool/core/lang/func/LambdaFactoryTest.java @@ -75,50 +75,59 @@ public class LambdaFactoryTest { } /** - *

hardCode 运行1次耗时 4600 ns - *

lambda 运行1次耗时 5400 ns - *

reflect 运行1次耗时 7100 ns - *

proxy 运行1次耗时 145400 ns + *

lambda 运行1次耗时 7000 NANOSECONDS + *

reflect 运行1次耗时 11300 NANOSECONDS + *

hardCode 运行1次耗时 12800 NANOSECONDS + *

proxy 运行1次耗时 160200 NANOSECONDS + *

mh 运行1次耗时 197900 NANOSECONDS *

-------------------------------------------- - *

hardCode 运行10次耗时 1200 ns - *

lambda 运行10次耗时 1200 ns - *

proxy 运行10次耗时 10800 ns - *

reflect 运行10次耗时 20100 ns + *

hardCode 运行10次耗时 1500 NANOSECONDS + *

lambda 运行10次耗时 2200 NANOSECONDS + *

mh 运行10次耗时 11700 NANOSECONDS + *

proxy 运行10次耗时 14400 NANOSECONDS + *

reflect 运行10次耗时 28600 NANOSECONDS *

-------------------------------------------- - *

lambda 运行100次耗时 6300 ns - *

hardCode 运行100次耗时 6400 ns - *

proxy 运行100次耗时 65100 ns - *

reflect 运行100次耗时 196800 ns + *

lambda 运行100次耗时 9300 NANOSECONDS + *

hardCode 运行100次耗时 14400 NANOSECONDS + *

mh 运行100次耗时 42900 NANOSECONDS + *

proxy 运行100次耗时 107900 NANOSECONDS + *

reflect 运行100次耗时 430800 NANOSECONDS *

-------------------------------------------- - *

hardCode 运行1000次耗时 54100 ns - *

lambda 运行1000次耗时 82000 ns - *

reflect 运行1000次耗时 257300 ns - *

proxy 运行1000次耗时 822700 ns + *

hardCode 运行1000次耗时 86300 NANOSECONDS + *

lambda 运行1000次耗时 101700 NANOSECONDS + *

reflect 运行1000次耗时 754700 NANOSECONDS + *

mh 运行1000次耗时 962200 NANOSECONDS + *

proxy 运行1000次耗时 1200500 NANOSECONDS *

-------------------------------------------- - *

hardCode 运行10000次耗时 84400 ns - *

lambda 运行10000次耗时 209200 ns - *

reflect 运行10000次耗时 1024300 ns - *

proxy 运行10000次耗时 1467300 ns + *

hardCode 运行10000次耗时 333000 NANOSECONDS + *

lambda 运行10000次耗时 367800 NANOSECONDS + *

mh 运行10000次耗时 999100 NANOSECONDS + *

proxy 运行10000次耗时 2766100 NANOSECONDS + *

reflect 运行10000次耗时 3157200 NANOSECONDS *

-------------------------------------------- - *

lambda 运行100000次耗时 618700 ns - *

hardCode 运行100000次耗时 675200 ns - *

reflect 运行100000次耗时 914100 ns - *

proxy 运行100000次耗时 2745800 ns + *

lambda 运行100000次耗时 571600 NANOSECONDS + *

hardCode 运行100000次耗时 1061700 NANOSECONDS + *

reflect 运行100000次耗时 1326800 NANOSECONDS + *

proxy 运行100000次耗时 3160900 NANOSECONDS + *

mh 运行100000次耗时 4137500 NANOSECONDS *

-------------------------------------------- - *

lambda 运行1000000次耗时 5342500 ns - *

hardCode 运行1000000次耗时 5616400 ns - *

reflect 运行1000000次耗时 9176700 ns - *

proxy 运行1000000次耗时 15801800 ns + *

hardCode 运行1000000次耗时 5066200 NANOSECONDS + *

lambda 运行1000000次耗时 5868700 NANOSECONDS + *

mh 运行1000000次耗时 8342700 NANOSECONDS + *

reflect 运行1000000次耗时 13009400 NANOSECONDS + *

proxy 运行1000000次耗时 21787800 NANOSECONDS *

-------------------------------------------- - *

lambda 运行10000000次耗时 53415200 ns - *

hardCode 运行10000000次耗时 63714500 ns - *

proxy 运行10000000次耗时 116420900 ns - *

reflect 运行10000000次耗时 120817900 ns + *

hardCode 运行10000000次耗时 51102700 NANOSECONDS + *

lambda 运行10000000次耗时 55007900 NANOSECONDS + *

mh 运行10000000次耗时 72751700 NANOSECONDS + *

reflect 运行10000000次耗时 92348800 NANOSECONDS + *

proxy 运行10000000次耗时 199705500 NANOSECONDS *

-------------------------------------------- - *

lambda 运行100000000次耗时 546706600 ns - *

hardCode 运行100000000次耗时 557174500 ns - *

reflect 运行100000000次耗时 924166200 ns - *

proxy 运行100000000次耗时 1862735900 ns + *

hardCode 运行100000000次耗时 456094400 NANOSECONDS + *

lambda 运行100000000次耗时 562348600 NANOSECONDS + *

reflect 运行100000000次耗时 630433200 NANOSECONDS + *

mh 运行100000000次耗时 671914300 NANOSECONDS + *

proxy 运行100000000次耗时 1117192600 NANOSECONDS *

-------------------------------------------- */ @Test @@ -141,42 +150,60 @@ public class LambdaFactoryTest { } /** - *

hardCode 运行1次耗时 4800 ns - *

lambda 运行1次耗时 9100 ns - *

reflect 运行1次耗时 20600 ns + *

hardCode 运行1次耗时 7600 NANOSECONDS + *

lambda 运行1次耗时 12400 NANOSECONDS + *

reflect 运行1次耗时 19900 NANOSECONDS + *

mh 运行1次耗时 139900 NANOSECONDS + *

proxy 运行1次耗时 261300 NANOSECONDS *

-------------------------------------------- - *

hardCode 运行10次耗时 1800 ns - *

lambda 运行10次耗时 2100 ns - *

reflect 运行10次耗时 24500 ns + *

hardCode 运行10次耗时 1700 NANOSECONDS + *

lambda 运行10次耗时 2600 NANOSECONDS + *

mh 运行10次耗时 3900 NANOSECONDS + *

proxy 运行10次耗时 20400 NANOSECONDS + *

reflect 运行10次耗时 26500 NANOSECONDS *

-------------------------------------------- - *

hardCode 运行100次耗时 15700 ns - *

lambda 运行100次耗时 17500 ns - *

reflect 运行100次耗时 418200 ns + *

hardCode 运行100次耗时 9000 NANOSECONDS + *

lambda 运行100次耗时 16900 NANOSECONDS + *

mh 运行100次耗时 32200 NANOSECONDS + *

proxy 运行100次耗时 315700 NANOSECONDS + *

reflect 运行100次耗时 604300 NANOSECONDS *

-------------------------------------------- - *

hardCode 运行1000次耗时 101700 ns - *

lambda 运行1000次耗时 157200 ns - *

reflect 运行1000次耗时 504900 ns + *

hardCode 运行1000次耗时 123500 NANOSECONDS + *

lambda 运行1000次耗时 253100 NANOSECONDS + *

mh 运行1000次耗时 644600 NANOSECONDS + *

reflect 运行1000次耗时 793100 NANOSECONDS + *

proxy 运行1000次耗时 1111100 NANOSECONDS *

-------------------------------------------- - *

hardCode 运行10000次耗时 360800 ns - *

lambda 运行10000次耗时 371700 ns - *

reflect 运行10000次耗时 1887600 ns + *

hardCode 运行10000次耗时 346800 NANOSECONDS + *

lambda 运行10000次耗时 524900 NANOSECONDS + *

mh 运行10000次耗时 931000 NANOSECONDS + *

reflect 运行10000次耗时 2046500 NANOSECONDS + *

proxy 运行10000次耗时 3108400 NANOSECONDS *

-------------------------------------------- - *

lambda 运行100000次耗时 581500 ns - *

hardCode 运行100000次耗时 1629900 ns - *

reflect 运行100000次耗时 1781700 ns + *

lambda 运行100000次耗时 608300 NANOSECONDS + *

hardCode 运行100000次耗时 1095600 NANOSECONDS + *

mh 运行100000次耗时 1430100 NANOSECONDS + *

reflect 运行100000次耗时 1558400 NANOSECONDS + *

proxy 运行100000次耗时 5566000 NANOSECONDS *

-------------------------------------------- - *

lambda 运行1000000次耗时 175400 ns - *

hardCode 运行1000000次耗时 2045400 ns - *

reflect 运行1000000次耗时 14363200 ns + *

lambda 运行1000000次耗时 6261000 NANOSECONDS + *

hardCode 运行1000000次耗时 6570200 NANOSECONDS + *

mh 运行1000000次耗时 8703300 NANOSECONDS + *

reflect 运行1000000次耗时 16437800 NANOSECONDS + *

proxy 运行1000000次耗时 22161100 NANOSECONDS *

-------------------------------------------- - *

hardCode 运行10000000次耗时 60149000 ns - *

lambda 运行10000000次耗时 60502600 ns - *

reflect 运行10000000次耗时 187412800 ns + *

lambda 运行10000000次耗时 60895800 NANOSECONDS + *

hardCode 运行10000000次耗时 61055300 NANOSECONDS + *

mh 运行10000000次耗时 69782400 NANOSECONDS + *

reflect 运行10000000次耗时 78078800 NANOSECONDS + *

proxy 运行10000000次耗时 193799800 NANOSECONDS + *

-------------------------------------------- + *

hardCode 运行100000000次耗时 499826200 NANOSECONDS + *

lambda 运行100000000次耗时 537454100 NANOSECONDS + *

reflect 运行100000000次耗时 673561400 NANOSECONDS + *

mh 运行100000000次耗时 700774100 NANOSECONDS + *

proxy 运行100000000次耗时 1169452400 NANOSECONDS *

-------------------------------------------- - *

hardCode 运行100000000次耗时 562997300 ns - *

lambda 运行100000000次耗时 564359700 ns - *

reflect 运行100000000次耗时 1163617600 ns - * -------------------------------------------- */ @Test @SneakyThrows @@ -247,7 +274,7 @@ public class LambdaFactoryTest { } public String format() { - TimeUnit timeUnit = TimeUnit.MILLISECONDS; + TimeUnit timeUnit = TimeUnit.NANOSECONDS; return String.format("%-10s 运行%d次耗时 %d %s", name, count, timeUnit.convert(cost, TimeUnit.NANOSECONDS), timeUnit.name()); } } From d6cf4d70e92b1eb7f286e188066cbfe0b1fbe103 Mon Sep 17 00:00:00 2001 From: wy Date: Sat, 17 Dec 2022 08:41:27 +0800 Subject: [PATCH 5/5] =?UTF-8?q?style(core):=20=E8=A7=84=E8=8C=83=E5=91=BD?= =?UTF-8?q?=E5=90=8D=EF=BC=8C=E6=9A=B4=E9=9C=B2=E5=BC=82=E5=B8=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../hutool/core/lang/func/LambdaFactory.java | 49 +++++++++---------- .../cn/hutool/core/lang/func/LambdaUtil.java | 20 ++++---- .../core/lang/func/LambdaFactoryTest.java | 25 +++++----- .../hutool/core/lang/func/LambdaUtilTest.java | 14 +++--- 4 files changed, 55 insertions(+), 53 deletions(-) diff --git a/hutool-core/src/main/java/cn/hutool/core/lang/func/LambdaFactory.java b/hutool-core/src/main/java/cn/hutool/core/lang/func/LambdaFactory.java index aeb2df1c5..d3b97c2b1 100644 --- a/hutool-core/src/main/java/cn/hutool/core/lang/func/LambdaFactory.java +++ b/hutool-core/src/main/java/cn/hutool/core/lang/func/LambdaFactory.java @@ -1,5 +1,6 @@ package cn.hutool.core.lang.func; +import cn.hutool.core.exceptions.UtilException; import cn.hutool.core.lang.Assert; import cn.hutool.core.lang.Opt; import cn.hutool.core.lang.mutable.MutableEntry; @@ -51,8 +52,8 @@ public class LambdaFactory { * @param paramTypes 方法参数数组 * @return 接受Lambda的函数式接口对象 */ - public static F buildLambda(Class functionInterfaceType, Class methodClass, String methodName, Class... paramTypes) { - return buildLambda(functionInterfaceType, MethodUtil.getMethod(methodClass, methodName, paramTypes)); + public static F build(Class functionInterfaceType, Class methodClass, String methodName, Class... paramTypes) { + return build(functionInterfaceType, MethodUtil.getMethod(methodClass, methodName, paramTypes)); } /** @@ -62,7 +63,7 @@ public class LambdaFactory { * @param method 方法对象 * @return 接受Lambda的函数式接口对象 */ - public static F buildLambda(Class functionInterfaceType, Method method) { + public static F build(Class functionInterfaceType, Method method) { Assert.notNull(functionInterfaceType); Assert.notNull(method); MutableEntry, Method> cacheKey = new MutableEntry<>(functionInterfaceType, method); @@ -82,31 +83,29 @@ public class LambdaFactory { MethodType samMethodType = methodType(invokeMethod.getReturnType(), invokeMethod.getParameterTypes()); MethodHandle implMethod = Opt.ofTry(() -> caller.unreflect(method)).get(); MethodType insMethodType = methodType(method.getReturnType(), method.getDeclaringClass(), method.getParameterTypes()); - boolean isSerializable = Arrays.stream(functionInterfaceType.getInterfaces()).anyMatch(i -> i.isAssignableFrom(Serializable.class)); - CallSite callSite = Opt.ofTry(() -> isSerializable ? - LambdaMetafactory.altMetafactory( - caller, - invokeName, - invokedType, - samMethodType, - implMethod, - insMethodType, - FLAG_SERIALIZABLE - ) : - LambdaMetafactory.metafactory( - caller, - invokeName, - invokedType, - samMethodType, - implMethod, - insMethodType - )).get(); - + boolean isSerializable = Serializable.class.isAssignableFrom(functionInterfaceType); try { - //noinspection unchecked + CallSite callSite = isSerializable ? + LambdaMetafactory.altMetafactory( + caller, + invokeName, + invokedType, + samMethodType, + implMethod, + insMethodType, + FLAG_SERIALIZABLE + ) : + LambdaMetafactory.metafactory( + caller, + invokeName, + invokedType, + samMethodType, + implMethod, + insMethodType + ); return (F) callSite.getTarget().invoke(); } catch (Throwable e) { - throw new RuntimeException(e); + throw new UtilException(e); } }); 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 b57824ac4..15f942687 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 @@ -140,8 +140,8 @@ public class LambdaUtil { * @param getter方法返回值类型 * @return Obj::getXxx */ - public static Function getter(Method getMethod) { - return LambdaFactory.buildLambda(Function.class, getMethod); + public static Function buildGetter(Method getMethod) { + return LambdaFactory.build(Function.class, getMethod); } /** @@ -153,8 +153,8 @@ public class LambdaUtil { * @param getter方法返回值类型 * @return Obj::getXxx */ - public static Function getter(Class clazz, String fieldName) { - return LambdaFactory.buildLambda(Function.class, BeanUtil.getBeanDesc(clazz).getGetter(fieldName)); + public static Function buildGetter(Class clazz, String fieldName) { + return LambdaFactory.build(Function.class, BeanUtil.getBeanDesc(clazz).getGetter(fieldName)); } /** @@ -165,8 +165,8 @@ public class LambdaUtil { * @param

setter方法返回的值类型 * @return Obj::setXxx */ - public static BiConsumer setter(Method setMethod) { - return LambdaFactory.buildLambda(BiConsumer.class, setMethod); + public static BiConsumer buildSetter(Method setMethod) { + return LambdaFactory.build(BiConsumer.class, setMethod); } /** @@ -178,8 +178,8 @@ public class LambdaUtil { * @param

setter方法返回的值类型 * @return Obj::setXxx */ - public static BiConsumer setter(Class clazz, String fieldName) { - return LambdaFactory.buildLambda(BiConsumer.class, BeanUtil.getBeanDesc(clazz).getSetter(fieldName)); + public static BiConsumer buildSetter(Class clazz, String fieldName) { + return LambdaFactory.build(BiConsumer.class, BeanUtil.getBeanDesc(clazz).getSetter(fieldName)); } /** @@ -192,8 +192,8 @@ public class LambdaUtil { * @param 函数式接口类型 * @return Obj::method */ - public static F lambda(Class lambdaType, Class clazz, String methodName, Class... paramsTypes) { - return LambdaFactory.buildLambda(lambdaType, clazz, methodName, paramsTypes); + public static F build(Class lambdaType, Class clazz, String methodName, Class... paramsTypes) { + return LambdaFactory.build(lambdaType, clazz, methodName, paramsTypes); } //region Private methods diff --git a/hutool-core/src/test/java/cn/hutool/core/lang/func/LambdaFactoryTest.java b/hutool-core/src/test/java/cn/hutool/core/lang/func/LambdaFactoryTest.java index 5c8e99171..a07ddbd19 100644 --- a/hutool-core/src/test/java/cn/hutool/core/lang/func/LambdaFactoryTest.java +++ b/hutool-core/src/test/java/cn/hutool/core/lang/func/LambdaFactoryTest.java @@ -1,6 +1,7 @@ package cn.hutool.core.lang.func; import cn.hutool.core.collection.ListUtil; +import cn.hutool.core.exceptions.UtilException; import cn.hutool.core.reflect.MethodHandleUtil; import lombok.Data; import lombok.Getter; @@ -11,10 +12,7 @@ import org.junit.Test; import org.junit.runner.RunWith; import org.junit.runners.Parameterized; -import java.lang.invoke.MethodHandle; -import java.lang.invoke.MethodHandleProxies; -import java.lang.invoke.MethodHandles; -import java.lang.invoke.MethodType; +import java.lang.invoke.*; import java.lang.reflect.Method; import java.util.Arrays; import java.util.Collection; @@ -28,9 +26,14 @@ import java.util.function.Function; */ public class LambdaFactoryTest { - @Test(expected = RuntimeException.class) + // @Test + @Test public void testMethodNotMatch() { - LambdaFactory.buildLambda(Function.class, Something.class, "setId", Long.class); + try { + LambdaFactory.build(Function.class, Something.class, "setId", Long.class); + } catch (Exception e) { + Assert.assertTrue(e.getCause() instanceof LambdaConversionException); + } } @Test @@ -39,14 +42,14 @@ public class LambdaFactoryTest { something.setId(1L); something.setName("name"); - Function get11 = LambdaFactory.buildLambda(Function.class, Something.class, "getId"); - Function get12 = LambdaFactory.buildLambda(Function.class, Something.class, "getId"); + Function get11 = LambdaFactory.build(Function.class, Something.class, "getId"); + Function get12 = LambdaFactory.build(Function.class, Something.class, "getId"); Assert.assertEquals(get11, get12); Assert.assertEquals(something.getId(), get11.apply(something)); String name = "sname"; - BiConsumer set = LambdaFactory.buildLambda(BiConsumer.class, Something.class, "setName", String.class); + BiConsumer set = LambdaFactory.build(BiConsumer.class, Something.class, "setName", String.class); set.accept(something, name); Assert.assertEquals(something.getName(), name); @@ -139,7 +142,7 @@ public class LambdaFactoryTest { Method getByReflect = Something.class.getMethod("getId"); MethodHandle getByMh = MethodHandleUtil.findMethod(Something.class, "getId", MethodType.methodType(Long.class)); Function getByProxy = MethodHandleProxies.asInterfaceInstance(Function.class, MethodHandles.lookup().unreflect(getByReflect)); - Function getByLambda = LambdaFactory.buildLambda(Function.class, getByReflect); + Function getByLambda = LambdaFactory.build(Function.class, getByReflect); Task lambdaTask = new Task("lambda", () -> getByLambda.apply(something)); Task mhTask = new Task("mh", () -> getByMh.invoke(something)); Task proxyTask = new Task("proxy", () -> getByProxy.apply(something)); @@ -214,7 +217,7 @@ public class LambdaFactoryTest { Method setByReflect = Something.class.getMethod("setName", String.class); MethodHandle setByMh = MethodHandleUtil.findMethod(Something.class, "setName", MethodType.methodType(Void.TYPE, String.class)); BiConsumer setByProxy = MethodHandleProxies.asInterfaceInstance(BiConsumer.class, setByMh); - BiConsumer setByLambda = LambdaFactory.buildLambda(BiConsumer.class, setByReflect); + BiConsumer setByLambda = LambdaFactory.build(BiConsumer.class, setByReflect); String name = "name1"; Task lambdaTask = new Task("lambda", () -> { setByLambda.accept(something, name); 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 08d0c144d..d410b92fc 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 @@ -155,8 +155,8 @@ public class LambdaUtilTest { Bean bean = new Bean(); bean.setId(2L); - Function getId = cn.hutool.core.lang.func.LambdaUtil.getter(MethodUtil.getMethod(Bean.class, "getId")); - Function getId2 = cn.hutool.core.lang.func.LambdaUtil.getter(Bean.class, Bean.Fields.id); + Function getId = LambdaUtil.buildGetter(MethodUtil.getMethod(Bean.class, "getId")); + Function getId2 = LambdaUtil.buildGetter(Bean.class, Bean.Fields.id); Assert.assertEquals(getId, getId2); Assert.assertEquals(bean.getId(), getId.apply(bean)); @@ -168,9 +168,9 @@ public class LambdaUtilTest { bean.setId(2L); bean.setFlag(false); - BiConsumer setId = cn.hutool.core.lang.func.LambdaUtil.setter(MethodUtil.getMethod(Bean.class, "setId", Long.class)); - BiConsumer setId2 = cn.hutool.core.lang.func.LambdaUtil.setter(Bean.class, Bean.Fields.id); - BiConsumer setFlag = cn.hutool.core.lang.func.LambdaUtil.setter(Bean.class, Bean.Fields.flag); + BiConsumer setId = LambdaUtil.buildSetter(MethodUtil.getMethod(Bean.class, "setId", Long.class)); + BiConsumer setId2 = LambdaUtil.buildSetter(Bean.class, Bean.Fields.id); + BiConsumer setFlag = LambdaUtil.buildSetter(Bean.class, Bean.Fields.flag); Assert.assertEquals(setId, setId2); setId.accept(bean, 3L); @@ -185,8 +185,8 @@ public class LambdaUtilTest { bean.setId(1L); bean.setPid(0L); bean.setFlag(true); - BiFunction uniqueKeyFunction = LambdaUtil.lambda(BiFunction.class, Bean.class, "uniqueKey", String.class); - Function4 paramsFunction = LambdaUtil.lambda(Function4.class, Bean.class, "params", String.class, Integer.class, Double.class); + BiFunction uniqueKeyFunction = LambdaUtil.build(BiFunction.class, Bean.class, "uniqueKey", String.class); + Function4 paramsFunction = LambdaUtil.build(Function4.class, Bean.class, "params", String.class, Integer.class, Double.class); Assert.assertEquals(bean.uniqueKey("test"), uniqueKeyFunction.apply(bean, "test")); Assert.assertEquals(bean.params("test", 1, 0.5), paramsFunction.apply(bean, "test", 1, 0.5)); }