This commit is contained in:
Looly
2022-09-23 23:45:59 +08:00
parent 62c80a2184
commit 1f554b7759
22 changed files with 314 additions and 134 deletions

View File

@@ -1,5 +1,6 @@
package cn.hutool.core.lang.func;
import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.classloader.ClassLoaderUtil;
import cn.hutool.core.lang.Assert;
import cn.hutool.core.text.StrPool;
@@ -11,28 +12,42 @@ import java.lang.reflect.Method;
import java.lang.reflect.Type;
/**
* 存放lambda信息
* 存放lambda信息<br>
* 此类是{@link SerializedLambda}信息的扩充和补充类,包括:
* <ul>
* <li>实例化后的对象方法参数类型,一般用于方法引用</li>
* </ul>
*
* @author VampireAchao
*/
public class LambdaInfo {
private static final Type[] EMPTY_TYPE = new Type[0];
// 实例对象的方法参数类型
private final Type[] instantiatedMethodParameterTypes;
// 方法或构造的参数类型
private final Type[] parameterTypes;
private final Type returnType;
// 方法名或构造名称
private final String name;
private final Executable executable;
private final Class<?> clazz;
private final SerializedLambda lambda;
/**
* 构造
*
* @param executable 构造对象{@link Constructor}或方法对象{@link Method}
* @param lambda 实现了序列化接口的lambda表达式
*/
public LambdaInfo(final Executable executable, final SerializedLambda lambda) {
Assert.notNull(executable, "executable must be not null!");
// return type
final boolean isMethod = executable instanceof Method;
final boolean isConstructor = executable instanceof Constructor;
Assert.isTrue(isMethod || isConstructor, "Unsupported executable type: " + executable.getClass());
this.returnType = isMethod ?
((Method)executable).getGenericReturnType() : ((Constructor<?>)executable).getDeclaringClass();
((Method) executable).getGenericReturnType() : ((Constructor<?>) executable).getDeclaringClass();
// lambda info
this.parameterTypes = executable.getGenericParameterTypes();
@@ -42,15 +57,89 @@ public class LambdaInfo {
this.lambda = lambda;
// types
final int index = lambda.getInstantiatedMethodType().indexOf(";)");
this.instantiatedMethodParameterTypes = (index > -1) ? getInstantiatedMethodParamTypes(lambda, index) : EMPTY_TYPE;
final String instantiatedMethodType = lambda.getInstantiatedMethodType();
final int index = instantiatedMethodType.indexOf(";)");
this.instantiatedMethodParameterTypes = (index > -1) ?
getInstantiatedMethodParamTypes(instantiatedMethodType.substring(1, index + 1)) : EMPTY_TYPE;
}
/**
* 实例方法参数类型
*
* @return 实例方法参数类型
*/
public Type[] getInstantiatedMethodParameterTypes() {
return instantiatedMethodParameterTypes;
}
/**
* 获得构造或方法参数类型列表
*
* @return 参数类型列表
*/
public Type[] getParameterTypes() {
return parameterTypes;
}
/**
* 获取返回值类型(方法引用)
*
* @return 返回值类型
*/
public Type getReturnType() {
return returnType;
}
/**
* 方法或构造名称
*
* @return 方法或构造名称
*/
public String getName() {
return name;
}
/**
* 字段名称主要用于方法名称截取方法名称必须为getXXX、isXXX、setXXX
*
* @return getter或setter对应的字段名称
*/
public String getFieldName() {
return BeanUtil.getFieldName(getName());
}
/**
* 方法或构造对象
*
* @return 方法或构造对象
*/
public Executable getExecutable() {
return executable;
}
/**
* 方法或构造所在类
*
* @return 方法或构造所在类
*/
public Class<?> getClazz() {
return clazz;
}
/**
* 获得Lambda表达式对象
*
* @return 获得Lambda表达式对象
*/
public SerializedLambda getLambda() {
return lambda;
}
/**
* 根据lambda对象的方法签名信息解析获得实际的参数类型
*/
private Type[] getInstantiatedMethodParamTypes(SerializedLambda lambda, int index) {
final String className = lambda.getInstantiatedMethodType().substring(1, index + 1);
private static Type[] getInstantiatedMethodParamTypes(final String className) {
final String[] instantiatedTypeNames = className.split(";");
final Type[] types = new Type[instantiatedTypeNames.length];
for (int i = 0; i < instantiatedTypeNames.length; i++) {
@@ -72,32 +161,4 @@ public class LambdaInfo {
}
return types;
}
public Type[] getInstantiatedMethodParameterTypes() {
return instantiatedMethodParameterTypes;
}
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;
}
}

View File

@@ -28,27 +28,29 @@ public class LambdaUtil {
* 通过对象的方法或类的静态方法引用获取lambda实现类
* 传入lambda无参数但含有返回值的情况能够匹配到此方法
* <ul>
* <li>引用特定对象的实例方法:<pre>{@code
* MyTeacher myTeacher = new MyTeacher();
* Class<MyTeacher> supplierClass = LambdaUtil.getRealClass(myTeacher::getAge);
* Assert.assertEquals(MyTeacher.class, supplierClass);
* }</pre></li>
* <li>引用静态无参方法:<pre>{@code
* Class<MyTeacher> staticSupplierClass = LambdaUtil.getRealClass(MyTeacher::takeAge);
* Assert.assertEquals(MyTeacher.class, staticSupplierClass);
* }</pre></li>
* <li>引用特定对象的实例方法:<pre>{@code
* MyTeacher myTeacher = new MyTeacher();
* Class<MyTeacher> supplierClass = LambdaUtil.getRealClass(myTeacher::getAge);
* Assert.assertEquals(MyTeacher.class, supplierClass);
* }</pre>
* </li>
* <li>引用静态无参方法:<pre>{@code
* Class<MyTeacher> staticSupplierClass = LambdaUtil.getRealClass(MyTeacher::takeAge);
* Assert.assertEquals(MyTeacher.class, staticSupplierClass);
* }</pre>
* </li>
* </ul>
* 在以下场景无法获取到正确类型
* <pre>{@code
* // 枚举测试,只能获取到枚举类型
* Class<Enum<?>> enumSupplierClass = LambdaUtil.getRealClass(LambdaUtil.LambdaKindEnum.REF_NONE::ordinal);
* Assert.assertEquals(Enum.class, enumSupplierClass);
* // 调用父类方法,只能获取到父类类型
* Class<Entity<?>> superSupplierClass = LambdaUtil.getRealClass(myTeacher::getId);
* Assert.assertEquals(Entity.class, superSupplierClass);
* // 引用父类静态带参方法,只能获取到父类类型
* Class<Entity<?>> staticSuperFunctionClass = LambdaUtil.getRealClass(MyTeacher::takeId);
* Assert.assertEquals(Entity.class, staticSuperFunctionClass);
* // 枚举测试,只能获取到枚举类型
* Class<Enum<?>> enumSupplierClass = LambdaUtil.getRealClass(LambdaUtil.LambdaKindEnum.REF_NONE::ordinal);
* Assert.assertEquals(Enum.class, enumSupplierClass);
* // 调用父类方法,只能获取到父类类型
* Class<Entity<?>> superSupplierClass = LambdaUtil.getRealClass(myTeacher::getId);
* Assert.assertEquals(Entity.class, superSupplierClass);
* // 引用父类静态带参方法,只能获取到父类类型
* Class<Entity<?>> staticSuperFunctionClass = LambdaUtil.getRealClass(MyTeacher::takeId);
* Assert.assertEquals(Entity.class, staticSuperFunctionClass);
* }</pre>
*
* @param func lambda
@@ -60,7 +62,10 @@ public class LambdaUtil {
@SuppressWarnings("unchecked")
public static <R, T extends Serializable> Class<R> getRealClass(final T func) {
final LambdaInfo lambdaInfo = resolve(func);
return (Class<R>) Opt.of(lambdaInfo).map(LambdaInfo::getInstantiatedMethodParameterTypes).filter(types -> types.length != 0).map(types -> types[types.length - 1]).orElseGet(lambdaInfo::getClazz);
return (Class<R>) Opt.of(lambdaInfo)
.map(LambdaInfo::getInstantiatedMethodParameterTypes)
.filter(types -> types.length != 0).map(types -> types[types.length - 1])
.orElseGet(lambdaInfo::getClazz);
}
/**

View File

@@ -10,6 +10,8 @@ import java.util.stream.Stream;
/**
* SerBiConsumer
*
* @param <T> 第一个参数类型
* @param <U> 第二个参数类型
* @author VampireAchao
*/
@FunctionalInterface
@@ -24,7 +26,8 @@ public interface SerBiConsumer<T, U> extends BiConsumer<T, U>, Serializable {
*/
@SafeVarargs
static <T, U> SerBiConsumer<T, U> multi(final SerBiConsumer<T, U>... consumers) {
return Stream.of(consumers).reduce(SerBiConsumer::andThen).orElseGet(() -> (o, q) -> {});
return Stream.of(consumers).reduce(SerBiConsumer::andThen).orElseGet(() -> (o, q) -> {
});
}
/**
@@ -79,6 +82,7 @@ public interface SerBiConsumer<T, U> extends BiConsumer<T, U>, Serializable {
* @return 什么也不做
*/
static <T, U> SerBiConsumer<T, U> nothing() {
return (l, r) -> {};
return (l, r) -> {
};
}
}

View File

@@ -30,10 +30,10 @@ public interface SerFunction<T, R> extends Function<T, R>, Serializable {
* @return the function result
*/
@Override
default R apply(T t) {
default R apply(final T t) {
try {
return applying(t);
} catch (Exception e) {
} catch (final Exception e) {
throw new UtilException(e);
}
}

View File

@@ -11,9 +11,10 @@ import java.util.stream.Stream;
*
* @author VampireAchao
* @see Supplier
* @param <R> 返回值类型
*/
@FunctionalInterface
public interface SerSupplier<T> extends Supplier<T>, Serializable {
public interface SerSupplier<R> extends Supplier<R>, Serializable {
/**
* Gets a result.
@@ -21,7 +22,7 @@ public interface SerSupplier<T> extends Supplier<T>, Serializable {
* @return a result
* @throws Exception wrapped checked exceptions
*/
T getting() throws Exception;
R getting() throws Exception;
/**
* Gets a result.
@@ -29,7 +30,7 @@ public interface SerSupplier<T> extends Supplier<T>, Serializable {
* @return a result
*/
@Override
default T get() {
default R get() {
try {
return getting();
} catch (final Exception e) {

View File

@@ -7,7 +7,9 @@ import cn.hutool.core.collection.SetUtil;
import cn.hutool.core.convert.Convert;
import cn.hutool.core.exceptions.CloneRuntimeException;
import cn.hutool.core.lang.Assert;
import cn.hutool.core.lang.func.LambdaInfo;
import cn.hutool.core.lang.func.LambdaUtil;
import cn.hutool.core.lang.func.SerFunction;
import cn.hutool.core.lang.func.SerSupplier;
import cn.hutool.core.lang.getter.TypeGetter;
@@ -352,6 +354,19 @@ public class Dict extends CustomKeyMap<String, Object> implements TypeGetter<Str
return getOrDefault(key, defaultValue);
}
/**
* 根据lambda的方法引用获取
*
* @param func 方法引用
* @param <P> 参数类型
* @param <T> 返回值类型
* @return 获取表达式对应属性和返回的对象
*/
public <P, T> T get(final SerFunction<P, T> func) {
final LambdaInfo lambdaInfo = LambdaUtil.resolve(func);
return get(lambdaInfo.getFieldName(), lambdaInfo.getReturnType());
}
/**
* 获得特定类型值
*

View File

@@ -3,14 +3,15 @@ package cn.hutool.core.lang;
import cn.hutool.core.builder.GenericBuilder;
import cn.hutool.core.date.DateTime;
import cn.hutool.core.map.Dict;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import org.junit.Assert;
import org.junit.Test;
import java.util.HashMap;
import java.util.Map;
import static cn.hutool.core.lang.OptTest.User;
public class DictTest {
@Test
public void dictTest(){
@@ -70,5 +71,16 @@ public class DictTest {
dict.setFields(user::getNickname, user::getUsername);
Assert.assertEquals("hutool", dict.get("username"));
Assert.assertNull(dict.get("nickname"));
// get by lambda
Assert.assertEquals("hutool", dict.get(User::getUsername));
}
@Data
@NoArgsConstructor
@AllArgsConstructor
static class User {
private String username;
private String nickname;
}
}