mirror of
https://gitee.com/chinabugotech/hutool.git
synced 2025-05-09 23:51:34 +08:00
commit
97614d1276
@ -0,0 +1,92 @@
|
|||||||
|
package cn.hutool.core.lang.func;
|
||||||
|
|
||||||
|
import cn.hutool.core.classloader.ClassLoaderUtil;
|
||||||
|
import cn.hutool.core.reflect.FieldUtil;
|
||||||
|
|
||||||
|
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 final Type[] instantiatedTypes;
|
||||||
|
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) {
|
||||||
|
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 {
|
||||||
|
this.instantiatedTypes = new Type[0];
|
||||||
|
}
|
||||||
|
this.clazz = (Class<?>) FieldUtil.getFieldValue(executable, "clazz");
|
||||||
|
this.executable = executable;
|
||||||
|
this.lambda = lambda;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Type[] getInstantiatedTypes() {
|
||||||
|
return instantiatedTypes;
|
||||||
|
}
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
@ -2,13 +2,18 @@ package cn.hutool.core.lang.func;
|
|||||||
|
|
||||||
import cn.hutool.core.bean.BeanUtil;
|
import cn.hutool.core.bean.BeanUtil;
|
||||||
import cn.hutool.core.classloader.ClassLoaderUtil;
|
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.map.WeakConcurrentMap;
|
||||||
import cn.hutool.core.reflect.MethodUtil;
|
import cn.hutool.core.reflect.MethodUtil;
|
||||||
import cn.hutool.core.text.StrUtil;
|
import cn.hutool.core.reflect.ReflectUtil;
|
||||||
|
|
||||||
import java.io.Serializable;
|
import java.io.Serializable;
|
||||||
import java.lang.invoke.MethodHandleInfo;
|
|
||||||
import java.lang.invoke.SerializedLambda;
|
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相关工具类
|
* Lambda相关工具类
|
||||||
@ -18,7 +23,7 @@ import java.lang.invoke.SerializedLambda;
|
|||||||
*/
|
*/
|
||||||
public class LambdaUtil {
|
public class LambdaUtil {
|
||||||
|
|
||||||
private static final WeakConcurrentMap<String, SerializedLambda> cache = new WeakConcurrentMap<>();
|
private static final WeakConcurrentMap<String, LambdaInfo> CACHE = new WeakConcurrentMap<>();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 通过对象的方法或类的静态方法引用,获取lambda实现类
|
* 通过对象的方法或类的静态方法引用,获取lambda实现类
|
||||||
@ -50,91 +55,59 @@ public class LambdaUtil {
|
|||||||
* @param func lambda
|
* @param func lambda
|
||||||
* @param <R> 类型
|
* @param <R> 类型
|
||||||
* @return lambda实现类
|
* @return lambda实现类
|
||||||
* @throws IllegalArgumentException 如果是不支持的方法引用,抛出该异常,见{@link LambdaUtil#checkLambdaTypeCanGetClass}
|
|
||||||
* @since 5.8.0
|
|
||||||
* @author VampireAchao
|
* @author VampireAchao
|
||||||
*/
|
*/
|
||||||
public static <R> Class<R> getRealClass(final Func0<?> func) {
|
@SuppressWarnings("unchecked")
|
||||||
final SerializedLambda lambda = resolve(func);
|
public static <R> Class<R> getRealClass(final Serializable func) {
|
||||||
checkLambdaTypeCanGetClass(lambda.getImplMethodKind());
|
LambdaInfo lambdaInfo = resolve(func);
|
||||||
return ClassLoaderUtil.loadClass(lambda.getImplClass());
|
return (Class<R>) Opt.of(lambdaInfo).map(LambdaInfo::getInstantiatedTypes).filter(types -> types.length != 0).map(types -> types[types.length - 1]).orElseGet(lambdaInfo::getClazz);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 解析lambda表达式,加了缓存。
|
* 解析lambda表达式,加了缓存。
|
||||||
* 该缓存可能会在任意不定的时间被清除
|
* 该缓存可能会在任意不定的时间被清除
|
||||||
*
|
*
|
||||||
* @param <T> Lambda类型
|
|
||||||
* @param func 需要解析的 lambda 对象(无参方法)
|
* @param func 需要解析的 lambda 对象(无参方法)
|
||||||
* @return 返回解析后的结果
|
* @return 返回解析后的结果
|
||||||
*/
|
*/
|
||||||
public static <T> SerializedLambda resolve(final Func1<T, ?> func) {
|
public static LambdaInfo resolve(final Serializable func) {
|
||||||
return _resolve(func);
|
return CACHE.computeIfAbsent(func.getClass().getName(), (key) -> {
|
||||||
}
|
final SerializedLambda serializedLambda = _resolve(func);
|
||||||
|
final String methodName = serializedLambda.getImplMethodName();
|
||||||
/**
|
final Class<?> implClass;
|
||||||
* 解析lambda表达式,加了缓存。
|
ClassLoaderUtil.loadClass(serializedLambda.getImplClass().replace("/", "."), true);
|
||||||
* 该缓存可能会在任意不定的时间被清除
|
try {
|
||||||
*
|
implClass = Class.forName(serializedLambda.getImplClass().replace("/", "."), true, Thread.currentThread().getContextClassLoader());
|
||||||
* @param <R> Lambda返回类型
|
} catch (ClassNotFoundException e) {
|
||||||
* @param func 需要解析的 lambda 对象(无参方法)
|
throw new UtilException(e);
|
||||||
* @return 返回解析后的结果
|
}
|
||||||
* @since 5.7.23
|
if ("<init>".equals(methodName)) {
|
||||||
*/
|
for (Constructor<?> constructor : implClass.getDeclaredConstructors()) {
|
||||||
public static <R> SerializedLambda resolve(final Func0<R> func) {
|
if (ReflectUtil.getDescriptor(constructor).equals(serializedLambda.getImplMethodSignature())) {
|
||||||
return _resolve(func);
|
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.");
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 获取lambda表达式函数(方法)名称
|
* 获取lambda表达式函数(方法)名称
|
||||||
*
|
*
|
||||||
* @param <P> Lambda参数类型
|
|
||||||
* @param func 函数(无参方法)
|
* @param func 函数(无参方法)
|
||||||
* @return 函数名称
|
* @return 函数名称
|
||||||
*/
|
*/
|
||||||
public static <P> String getMethodName(final Func1<P, ?> func) {
|
public static String getMethodName(final Serializable func) {
|
||||||
return resolve(func).getImplMethodName();
|
return resolve(func).getName();
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 获取lambda表达式函数(方法)名称
|
|
||||||
*
|
|
||||||
* @param <R> Lambda返回类型
|
|
||||||
* @param func 函数(无参方法)
|
|
||||||
* @return 函数名称
|
|
||||||
* @since 5.7.23
|
|
||||||
*/
|
|
||||||
public static <R> String getMethodName(final Func0<R> func) {
|
|
||||||
return resolve(func).getImplMethodName();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 通过对象的方法或类的静态方法引用,然后根据{@link SerializedLambda#getInstantiatedMethodType()}获取lambda实现类<br>
|
|
||||||
* 传入lambda有参数且含有返回值的情况能够匹配到此方法:
|
|
||||||
* <ul>
|
|
||||||
* <li>引用特定类型的任意对象的实例方法:<pre>{@code
|
|
||||||
* Class<MyTeacher> functionClass = LambdaUtil.getRealClass(MyTeacher::getAge);
|
|
||||||
* Assert.assertEquals(MyTeacher.class, functionClass);
|
|
||||||
* }</pre></li>
|
|
||||||
* <li>引用静态带参方法:<pre>{@code
|
|
||||||
* Class<MyTeacher> staticFunctionClass = LambdaUtil.getRealClass(MyTeacher::takeAgeBy);
|
|
||||||
* Assert.assertEquals(MyTeacher.class, staticFunctionClass);
|
|
||||||
* }</pre></li>
|
|
||||||
* </ul>
|
|
||||||
*
|
|
||||||
* @param func lambda
|
|
||||||
* @param <P> 方法调用方类型
|
|
||||||
* @param <R> 返回值类型
|
|
||||||
* @return lambda实现类
|
|
||||||
* @throws IllegalArgumentException 如果是不支持的方法引用,抛出该异常,见{@link LambdaUtil#checkLambdaTypeCanGetClass}
|
|
||||||
* @since 5.8.0
|
|
||||||
* @author VampireAchao
|
|
||||||
*/
|
|
||||||
public static <P, R> Class<P> getRealClass(final Func1<P, R> func) {
|
|
||||||
final SerializedLambda lambda = resolve(func);
|
|
||||||
checkLambdaTypeCanGetClass(lambda.getImplMethodKind());
|
|
||||||
final String instantiatedMethodType = lambda.getInstantiatedMethodType();
|
|
||||||
return ClassLoaderUtil.loadClass(StrUtil.sub(instantiatedMethodType, 2, StrUtil.indexOf(instantiatedMethodType, ';')));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -146,63 +119,44 @@ public class LambdaUtil {
|
|||||||
* <li>其它不满足规则的方法名抛出{@link IllegalArgumentException}</li>
|
* <li>其它不满足规则的方法名抛出{@link IllegalArgumentException}</li>
|
||||||
* </ul>
|
* </ul>
|
||||||
*
|
*
|
||||||
* @param <T> Lambda类型
|
* @param func 函数
|
||||||
* @param func 函数(无参方法)
|
|
||||||
* @return 方法名称
|
|
||||||
* @throws IllegalArgumentException 非Getter或Setter方法
|
|
||||||
* @since 5.7.10
|
|
||||||
*/
|
|
||||||
public static <T> String getFieldName(final Func1<T, ?> func) throws IllegalArgumentException {
|
|
||||||
return BeanUtil.getFieldName(getMethodName(func));
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 获取lambda表达式Getter或Setter函数(方法)对应的字段名称,规则如下:
|
|
||||||
* <ul>
|
|
||||||
* <li>getXxxx获取为xxxx,如getName得到name。</li>
|
|
||||||
* <li>setXxxx获取为xxxx,如setName得到name。</li>
|
|
||||||
* <li>isXxxx获取为xxxx,如isName得到name。</li>
|
|
||||||
* <li>其它不满足规则的方法名抛出{@link IllegalArgumentException}</li>
|
|
||||||
* </ul>
|
|
||||||
*
|
|
||||||
* @param <T> Lambda类型
|
|
||||||
* @param func 函数(无参方法)
|
|
||||||
* @return 方法名称
|
* @return 方法名称
|
||||||
* @throws IllegalArgumentException 非Getter或Setter方法
|
* @throws IllegalArgumentException 非Getter或Setter方法
|
||||||
* @since 5.7.23
|
* @since 5.7.23
|
||||||
*/
|
*/
|
||||||
public static <T> String getFieldName(final Func0<T> func) throws IllegalArgumentException {
|
public static String getFieldName(final Serializable func) throws IllegalArgumentException {
|
||||||
return BeanUtil.getFieldName(getMethodName(func));
|
return BeanUtil.getFieldName(getMethodName(func));
|
||||||
}
|
}
|
||||||
|
|
||||||
//region Private methods
|
//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表达式,加了缓存。
|
* 解析lambda表达式,没加缓存
|
||||||
* 该缓存可能会在任意不定的时间被清除。
|
|
||||||
*
|
*
|
||||||
* <p>
|
* <p>
|
||||||
* 通过反射调用实现序列化接口函数对象的writeReplace方法,从而拿到{@link SerializedLambda}<br>
|
* 通过反射调用实现序列化接口函数对象的writeReplace方法,从而拿到{@link SerializedLambda}<br>
|
||||||
* 该对象中包含了lambda表达式的所有信息。
|
* 该对象中包含了lambda表达式的大部分信息。
|
||||||
* </p>
|
* </p>
|
||||||
*
|
*
|
||||||
* @param func 需要解析的 lambda 对象
|
* @param func 需要解析的 lambda 对象
|
||||||
* @return 返回解析后的结果
|
* @return 返回解析后的结果
|
||||||
*/
|
*/
|
||||||
private static SerializedLambda _resolve(final Serializable func) {
|
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 IllegalArgumentException("not support proxy, just for now");
|
||||||
|
}
|
||||||
|
final Class<? extends Serializable> clazz = func.getClass();
|
||||||
|
if (!clazz.isSynthetic()) {
|
||||||
|
throw new IllegalArgumentException("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
|
//endregion
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,9 @@
|
|||||||
package cn.hutool.core.reflect;
|
package cn.hutool.core.reflect;
|
||||||
|
|
||||||
import java.lang.reflect.AccessibleObject;
|
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;
|
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();
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -1,84 +1,138 @@
|
|||||||
package cn.hutool.core.lang.func;
|
package cn.hutool.core.lang.func;
|
||||||
|
|
||||||
import org.junit.Assert;
|
|
||||||
import org.junit.Test;
|
|
||||||
|
|
||||||
import lombok.AllArgsConstructor;
|
import lombok.AllArgsConstructor;
|
||||||
import lombok.Data;
|
import lombok.Data;
|
||||||
import lombok.EqualsAndHashCode;
|
import lombok.EqualsAndHashCode;
|
||||||
|
import org.junit.Assert;
|
||||||
|
import org.junit.Test;
|
||||||
|
|
||||||
import java.lang.invoke.MethodHandleInfo;
|
import java.io.Serializable;
|
||||||
|
import java.lang.reflect.Array;
|
||||||
|
import java.util.Objects;
|
||||||
|
import java.util.stream.Stream;
|
||||||
|
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
public class LambdaUtilTest {
|
public class LambdaUtilTest {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void getMethodNameTest() {
|
public void getMethodNameTest() {
|
||||||
final String methodName = LambdaUtil.getMethodName(MyTeacher::getAge);
|
Func1<MyTeacher, String> lambda = MyTeacher::getAge;
|
||||||
|
final String methodName = LambdaUtil.getMethodName(lambda);
|
||||||
Assert.assertEquals("getAge", methodName);
|
Assert.assertEquals("getAge", methodName);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void getFieldNameTest() {
|
public void getFieldNameTest() {
|
||||||
final String fieldName = LambdaUtil.getFieldName(MyTeacher::getAge);
|
Func1<MyTeacher, String> lambda = MyTeacher::getAge;
|
||||||
|
final String fieldName = LambdaUtil.getFieldName(lambda);
|
||||||
Assert.assertEquals("age", fieldName);
|
Assert.assertEquals("age", fieldName);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void resolveTest() {
|
public void resolveTest() {
|
||||||
// 引用构造函数
|
Stream.<Runnable>of(() -> {
|
||||||
Assert.assertEquals(MethodHandleInfo.REF_newInvokeSpecial,
|
// 引用构造函数
|
||||||
LambdaUtil.resolve(MyTeacher::new).getImplMethodKind());
|
Func0<MyTeacher> lambda = MyTeacher::new;
|
||||||
// 数组构造函数引用
|
LambdaInfo lambdaInfo = LambdaUtil.resolve(lambda);
|
||||||
Assert.assertEquals(MethodHandleInfo.REF_invokeStatic,
|
Assert.assertEquals(0, lambdaInfo.getParameterTypes().length);
|
||||||
LambdaUtil.resolve(MyTeacher[]::new).getImplMethodKind());
|
Assert.assertEquals(MyTeacher.class, lambdaInfo.getReturnType());
|
||||||
// 引用静态方法
|
}, () -> {
|
||||||
Assert.assertEquals(MethodHandleInfo.REF_invokeStatic,
|
// 数组构造函数引用(此处数组构造参数)
|
||||||
LambdaUtil.resolve(MyTeacher::takeAge).getImplMethodKind());
|
Func1<Integer, MyTeacher[]> lambda = MyTeacher[]::new;
|
||||||
// 引用特定对象的实例方法
|
LambdaInfo lambdaInfo = LambdaUtil.resolve(lambda);
|
||||||
Assert.assertEquals(MethodHandleInfo.REF_invokeVirtual,
|
Assert.assertEquals(int.class, lambdaInfo.getParameterTypes()[0]);
|
||||||
LambdaUtil.resolve(new MyTeacher()::getAge).getImplMethodKind());
|
Assert.assertEquals(MyTeacher.class, ((Class<Array>) lambdaInfo.getReturnType()).getComponentType());
|
||||||
// 引用特定类型的任意对象的实例方法
|
}, () -> {
|
||||||
Assert.assertEquals(MethodHandleInfo.REF_invokeVirtual,
|
// 引用静态方法
|
||||||
LambdaUtil.resolve(MyTeacher::getAge).getImplMethodKind());
|
Func0<String> lambda = MyTeacher::takeAge;
|
||||||
|
LambdaInfo lambdaInfo = LambdaUtil.resolve(lambda);
|
||||||
|
Assert.assertEquals(0, lambdaInfo.getParameterTypes().length);
|
||||||
|
Assert.assertEquals(String.class, lambdaInfo.getReturnType());
|
||||||
|
}, () -> {
|
||||||
|
// 引用特定对象的实例方法
|
||||||
|
Func0<String> lambda = new MyTeacher()::getAge;
|
||||||
|
LambdaInfo lambdaInfo = LambdaUtil.resolve(lambda);
|
||||||
|
Assert.assertEquals(0, lambdaInfo.getParameterTypes().length);
|
||||||
|
Assert.assertEquals(String.class, lambdaInfo.getReturnType());
|
||||||
|
}, () -> {
|
||||||
|
// 引用特定类型的任意对象的实例方法
|
||||||
|
Func1<MyTeacher, String> 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<Object, Boolean, String> 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<P1, P2, P3> extends Consumer3<P1, P2, P3>, Serializable {
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void getRealClassTest() {
|
public void getRealClassTest() {
|
||||||
// 引用特定类型的任意对象的实例方法
|
|
||||||
final Class<MyTeacher> functionClass = LambdaUtil.getRealClass(MyTeacher::getAge);
|
|
||||||
Assert.assertEquals(MyTeacher.class, functionClass);
|
|
||||||
// 枚举测试,不会导致类型擦除
|
|
||||||
final Class<LambdaKindEnum> enumFunctionClass = LambdaUtil.getRealClass(LambdaKindEnum::ordinal);
|
|
||||||
Assert.assertEquals(LambdaKindEnum.class, enumFunctionClass);
|
|
||||||
// 调用父类方法,能获取到正确的子类类型
|
|
||||||
final Class<MyTeacher> superFunctionClass = LambdaUtil.getRealClass(MyTeacher::getId);
|
|
||||||
Assert.assertEquals(MyTeacher.class, superFunctionClass);
|
|
||||||
|
|
||||||
final MyTeacher myTeacher = new MyTeacher();
|
final MyTeacher myTeacher = new MyTeacher();
|
||||||
// 引用特定对象的实例方法
|
Stream.<Runnable>of(() -> {
|
||||||
final Class<MyTeacher> supplierClass = LambdaUtil.getRealClass(myTeacher::getAge);
|
// 引用特定类型的任意对象的实例方法
|
||||||
Assert.assertEquals(MyTeacher.class, supplierClass);
|
final Func1<MyTeacher, String> lambda = MyTeacher::getAge;
|
||||||
// 枚举测试,只能获取到枚举类型
|
Assert.assertEquals(MyTeacher.class, LambdaUtil.getRealClass(lambda));
|
||||||
final Class<Enum<?>> enumSupplierClass = LambdaUtil.getRealClass(LambdaKindEnum.REF_NONE::ordinal);
|
}, () -> {
|
||||||
Assert.assertEquals(Enum.class, enumSupplierClass);
|
// 枚举测试,不会导致类型擦除
|
||||||
// 调用父类方法,只能获取到父类类型
|
final Func1<LambdaKindEnum, Integer> lambda = LambdaKindEnum::ordinal;
|
||||||
final Class<Entity<?>> superSupplierClass = LambdaUtil.getRealClass(myTeacher::getId);
|
Assert.assertEquals(LambdaKindEnum.class, LambdaUtil.getRealClass(lambda));
|
||||||
Assert.assertEquals(Entity.class, superSupplierClass);
|
}, () -> {
|
||||||
|
// 调用父类方法,能获取到正确的子类类型
|
||||||
// 引用静态带参方法,能够获取到正确的参数类型
|
final Func1<MyTeacher, ?> lambda = MyTeacher::getId;
|
||||||
final Class<MyTeacher> staticFunctionClass = LambdaUtil.getRealClass(MyTeacher::takeAgeBy);
|
Assert.assertEquals(MyTeacher.class, LambdaUtil.getRealClass(lambda));
|
||||||
Assert.assertEquals(MyTeacher.class, staticFunctionClass);
|
}, () -> {
|
||||||
// 引用父类静态带参方法,只能获取到父类类型
|
// 引用特定对象的实例方法
|
||||||
final Class<Entity<?>> staticSuperFunctionClass = LambdaUtil.getRealClass(MyTeacher::takeId);
|
Func0<String> lambda = myTeacher::getAge;
|
||||||
Assert.assertEquals(Entity.class, staticSuperFunctionClass);
|
Assert.assertEquals(MyTeacher.class, LambdaUtil.getRealClass(lambda));
|
||||||
|
}, () -> {
|
||||||
// 引用静态无参方法,能够获取到正确的类型
|
// 枚举测试,只能获取到枚举类型
|
||||||
final Class<MyTeacher> staticSupplierClass = LambdaUtil.getRealClass(MyTeacher::takeAge);
|
Func0<Integer> lambda = LambdaKindEnum.REF_NONE::ordinal;
|
||||||
Assert.assertEquals(MyTeacher.class, staticSupplierClass);
|
Assert.assertEquals(Enum.class, LambdaUtil.getRealClass(lambda));
|
||||||
// 引用父类静态无参方法,能够获取到正确的参数类型
|
}, () -> {
|
||||||
final Class<MyTeacher> staticSuperSupplierClass = LambdaUtil.getRealClass(MyTeacher::takeIdBy);
|
// 调用父类方法,只能获取到父类类型
|
||||||
Assert.assertEquals(MyTeacher.class, staticSuperSupplierClass);
|
VoidFunc0 lambda = myTeacher::getId;
|
||||||
|
Assert.assertEquals(Entity.class, LambdaUtil.getRealClass(lambda));
|
||||||
|
}, () -> {
|
||||||
|
// 引用静态带参方法,能够获取到正确的参数类型
|
||||||
|
Func1<MyTeacher, String> lambda = MyTeacher::takeAgeBy;
|
||||||
|
Assert.assertEquals(MyTeacher.class, LambdaUtil.getRealClass(lambda));
|
||||||
|
}, () -> {
|
||||||
|
// 引用父类静态带参方法,只能获取到父类类型
|
||||||
|
Func0<?> lambda = MyTeacher::takeId;
|
||||||
|
Assert.assertEquals(Entity.class, LambdaUtil.getRealClass(lambda));
|
||||||
|
}, () -> {
|
||||||
|
// 引用静态无参方法,能够获取到正确的类型
|
||||||
|
Func0<String> lambda = MyTeacher::takeAge;
|
||||||
|
Assert.assertEquals(MyTeacher.class, LambdaUtil.getRealClass(lambda));
|
||||||
|
}, () -> {
|
||||||
|
// 引用父类静态无参方法,能够获取到正确的参数类型
|
||||||
|
Func1<MyTeacher, ?> lambda = MyTeacher::takeIdBy;
|
||||||
|
Assert.assertEquals(MyTeacher.class, LambdaUtil.getRealClass(lambda));
|
||||||
|
}, () -> {
|
||||||
|
// 数组测试
|
||||||
|
VoidFunc1<String[]> lambda = (String[] stringList) -> {};
|
||||||
|
Assert.assertEquals(String[].class, LambdaUtil.getRealClass(lambda));
|
||||||
|
}).forEach(Runnable::run);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Data
|
@Data
|
||||||
|
Loading…
x
Reference in New Issue
Block a user