cn.hutool.core.lang.func.LambdaUtil Maven / Gradle / Ivy
Show all versions of hutool-all Show documentation
package cn.hutool.core.lang.func;
import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.map.WeakConcurrentMap;
import cn.hutool.core.util.ClassUtil;
import cn.hutool.core.util.ReflectUtil;
import cn.hutool.core.util.StrUtil;
import java.io.Serializable;
import java.lang.invoke.MethodHandleInfo;
import java.lang.invoke.SerializedLambda;
/**
* Lambda相关工具类
*
* @author looly, Scen
* @since 5.6.3
*/
public class LambdaUtil {
private static final WeakConcurrentMap cache = new WeakConcurrentMap<>();
/**
* 通过对象的方法或类的静态方法引用,获取lambda实现类
* 传入lambda无参数但含有返回值的情况能够匹配到此方法:
*
* - 引用特定对象的实例方法:
{@code
* MyTeacher myTeacher = new MyTeacher();
* Class supplierClass = LambdaUtil.getRealClass(myTeacher::getAge);
* Assert.assertEquals(MyTeacher.class, supplierClass);
* }
* - 引用静态无参方法:
{@code
* Class staticSupplierClass = LambdaUtil.getRealClass(MyTeacher::takeAge);
* Assert.assertEquals(MyTeacher.class, staticSupplierClass);
* }
*
* 在以下场景无法获取到正确类型
* {@code
* // 枚举测试,只能获取到枚举类型
* Class> enumSupplierClass = LambdaUtil.getRealClass(LambdaUtil.LambdaKindEnum.REF_NONE::ordinal);
* Assert.assertEquals(Enum.class, enumSupplierClass);
* // 调用父类方法,只能获取到父类类型
* Class> superSupplierClass = LambdaUtil.getRealClass(myTeacher::getId);
* Assert.assertEquals(Entity.class, superSupplierClass);
* // 引用父类静态带参方法,只能获取到父类类型
* Class> staticSuperFunctionClass = LambdaUtil.getRealClass(MyTeacher::takeId);
* Assert.assertEquals(Entity.class, staticSuperFunctionClass);
* }
*
* @param func lambda
* @param 类型
* @return lambda实现类
* @throws IllegalArgumentException 如果是不支持的方法引用,抛出该异常,见{@link LambdaUtil#checkLambdaTypeCanGetClass}
* @since 5.8.0
* @author VampireAchao
*/
public static Class getRealClass(Func0> func) {
final SerializedLambda lambda = resolve(func);
checkLambdaTypeCanGetClass(lambda.getImplMethodKind());
return ClassUtil.loadClass(lambda.getImplClass());
}
/**
* 解析lambda表达式,加了缓存。
* 该缓存可能会在任意不定的时间被清除
*
* @param Lambda类型
* @param func 需要解析的 lambda 对象(无参方法)
* @return 返回解析后的结果
*/
public static SerializedLambda resolve(Func1 func) {
return _resolve(func);
}
/**
* 解析lambda表达式,加了缓存。
* 该缓存可能会在任意不定的时间被清除
*
* @param Lambda返回类型
* @param func 需要解析的 lambda 对象(无参方法)
* @return 返回解析后的结果
* @since 5.7.23
*/
public static SerializedLambda resolve(Func0 func) {
return _resolve(func);
}
/**
* 获取lambda表达式函数(方法)名称
*
* @param Lambda参数类型
* @param func 函数(无参方法)
* @return 函数名称
*/
public static
String getMethodName(Func1
func) {
return resolve(func).getImplMethodName();
}
/**
* 获取lambda表达式函数(方法)名称
*
* @param Lambda返回类型
* @param func 函数(无参方法)
* @return 函数名称
* @since 5.7.23
*/
public static String getMethodName(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(Func1
func) {
final SerializedLambda lambda = resolve(func);
checkLambdaTypeCanGetClass(lambda.getImplMethodKind());
final String instantiatedMethodType = lambda.getInstantiatedMethodType();
return ClassUtil.loadClass(StrUtil.sub(instantiatedMethodType, 2, StrUtil.indexOf(instantiatedMethodType, ';')));
}
/**
* 获取lambda表达式Getter或Setter函数(方法)对应的字段名称,规则如下:
*
* - getXxxx获取为xxxx,如getName得到name。
* - setXxxx获取为xxxx,如setName得到name。
* - isXxxx获取为xxxx,如isName得到name。
* - 其它不满足规则的方法名抛出{@link IllegalArgumentException}
*
*
* @param Lambda类型
* @param func 函数(无参方法)
* @return 方法名称
* @throws IllegalArgumentException 非Getter或Setter方法
* @since 5.7.10
*/
public static String getFieldName(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 函数(无参方法)
* @return 方法名称
* @throws IllegalArgumentException 非Getter或Setter方法
* @since 5.7.23
*/
public static String getFieldName(Func0 func) throws IllegalArgumentException {
return BeanUtil.getFieldName(getMethodName(func));
}
//region Private methods
/**
* 检查是否为支持的类型
*
* @param implMethodKind 支持的lambda类型
* @throws IllegalArgumentException 如果是不支持的方法引用,抛出该异常
*/
private static void checkLambdaTypeCanGetClass(int implMethodKind) {
if (implMethodKind != MethodHandleInfo.REF_invokeVirtual &&
implMethodKind != MethodHandleInfo.REF_invokeStatic) {
throw new IllegalArgumentException("该lambda不是合适的方法引用");
}
}
/**
* 解析lambda表达式,加了缓存。
* 该缓存可能会在任意不定的时间被清除。
*
*
* 通过反射调用实现序列化接口函数对象的writeReplace方法,从而拿到{@link SerializedLambda}
* 该对象中包含了lambda表达式的所有信息。
*
*
* @param func 需要解析的 lambda 对象
* @return 返回解析后的结果
*/
private static SerializedLambda _resolve(Serializable func) {
return cache.computeIfAbsent(func.getClass().getName(), (key) -> ReflectUtil.invoke(func, "writeReplace"));
}
//endregion
}