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;
}
/**
* 字段名称主要用于方法名称截取方法名称必须为getXXXisXXXsetXXX
*
* @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;
}
}

View File

@ -1,36 +0,0 @@
package cn.hutool.extra.template;
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.StringWriter;
import java.util.Map;
import cn.hutool.core.io.FileUtil;
import cn.hutool.core.io.IoUtil;
/**
* 抽象模板提供将模板融合后写出到文件返回字符串等方法
*
* @author looly
*
*/
public abstract class AbstractTemplate implements Template{
@Override
public void render(final Map<?, ?> bindingMap, final File file) {
BufferedOutputStream out = null;
try {
out = FileUtil.getOutputStream(file);
this.render(bindingMap, out);
} finally {
IoUtil.close(out);
}
}
@Override
public String render(final Map<?, ?> bindingMap) {
final StringWriter writer = new StringWriter();
render(bindingMap, writer);
return writer.toString();
}
}

View File

@ -1,7 +1,12 @@
package cn.hutool.extra.template;
import cn.hutool.core.io.FileUtil;
import cn.hutool.core.io.IoUtil;
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.OutputStream;
import java.io.StringWriter;
import java.io.Writer;
import java.util.Map;
@ -9,7 +14,6 @@ import java.util.Map;
* 抽象模板接口
*
* @author looly
*
*/
public interface Template {
@ -17,7 +21,7 @@ public interface Template {
* 将模板与绑定参数融合后输出到Writer
*
* @param bindingMap 绑定的参数此Map中的参数会替换模板中的变量
* @param writer 输出
* @param writer 输出
*/
void render(Map<?, ?> bindingMap, Writer writer);
@ -25,16 +29,25 @@ public interface Template {
* 将模板与绑定参数融合后输出到流
*
* @param bindingMap 绑定的参数此Map中的参数会替换模板中的变量
* @param out 输出
* @param out 输出
*/
void render(Map<?, ?> bindingMap, OutputStream out);
/**
* 写出到文件
*
* @param bindingMap 绑定的参数此Map中的参数会替换模板中的变量
* @param file 输出到的文件
* @param file 输出到的文件
*/
void render(Map<?, ?> bindingMap, File file);
default void render(final Map<?, ?> bindingMap, final File file) {
BufferedOutputStream out = null;
try {
out = FileUtil.getOutputStream(file);
this.render(bindingMap, out);
} finally {
IoUtil.close(out);
}
}
/**
* 将模板与绑定参数融合后返回为字符串
@ -42,5 +55,9 @@ public interface Template {
* @param bindingMap 绑定的参数此Map中的参数会替换模板中的变量
* @return 融合后的内容
*/
String render(Map<?, ?> bindingMap);
default String render(final Map<?, ?> bindingMap) {
final StringWriter writer = new StringWriter();
render(bindingMap, writer);
return writer.toString();
}
}

View File

@ -1,6 +1,6 @@
package cn.hutool.extra.template.engine.beetl;
import cn.hutool.extra.template.AbstractTemplate;
import cn.hutool.extra.template.Template;
import java.io.OutputStream;
import java.io.Serializable;
@ -12,7 +12,7 @@ import java.util.Map;
*
* @author looly
*/
public class BeetlTemplate extends AbstractTemplate implements Serializable{
public class BeetlTemplate implements Template, Serializable{
private static final long serialVersionUID = -8157926902932567280L;
private final org.beetl.core.Template rawTemplate;

View File

@ -1,6 +1,6 @@
package cn.hutool.extra.template.engine.enjoy;
import cn.hutool.extra.template.AbstractTemplate;
import cn.hutool.extra.template.Template;
import java.io.OutputStream;
import java.io.Serializable;
@ -13,7 +13,7 @@ import java.util.Map;
* @author looly
* @since 4.1.9
*/
public class EnjoyTemplate extends AbstractTemplate implements Serializable {
public class EnjoyTemplate implements Template, Serializable {
private static final long serialVersionUID = 1L;
private final com.jfinal.template.Template rawTemplate;
@ -22,7 +22,7 @@ public class EnjoyTemplate extends AbstractTemplate implements Serializable {
* 包装Enjoy模板
*
* @param EnjoyTemplate Enjoy的模板对象 {@link com.jfinal.template.Template}
* @return {@link EnjoyTemplate}
* @return {@code EnjoyTemplate}
*/
public static EnjoyTemplate wrap(final com.jfinal.template.Template EnjoyTemplate) {
return (null == EnjoyTemplate) ? null : new EnjoyTemplate(EnjoyTemplate);

View File

@ -2,7 +2,7 @@ package cn.hutool.extra.template.engine.freemarker;
import cn.hutool.core.io.IORuntimeException;
import cn.hutool.core.io.IoUtil;
import cn.hutool.extra.template.AbstractTemplate;
import cn.hutool.extra.template.Template;
import cn.hutool.extra.template.TemplateException;
import java.io.IOException;
@ -17,7 +17,7 @@ import java.util.Map;
*
* @author looly
*/
public class FreemarkerTemplate extends AbstractTemplate implements Serializable{
public class FreemarkerTemplate implements Template, Serializable{
private static final long serialVersionUID = -8157926902932567280L;
freemarker.template.Template rawTemplate;

View File

@ -2,7 +2,7 @@ package cn.hutool.extra.template.engine.jetbrick;
import cn.hutool.core.convert.Convert;
import cn.hutool.core.reflect.TypeReference;
import cn.hutool.extra.template.AbstractTemplate;
import cn.hutool.extra.template.Template;
import jetbrick.template.JetTemplate;
import java.io.OutputStream;
@ -17,7 +17,7 @@ import java.util.Map;
* @author looly
* @since 5.7.21
*/
public class JetbrickTemplate extends AbstractTemplate implements Serializable{
public class JetbrickTemplate implements Template, Serializable{
private static final long serialVersionUID = 1L;
private final JetTemplate rawTemplate;

View File

@ -2,7 +2,7 @@ package cn.hutool.extra.template.engine.rythm;
import cn.hutool.core.convert.Convert;
import cn.hutool.core.reflect.TypeReference;
import cn.hutool.extra.template.AbstractTemplate;
import cn.hutool.extra.template.Template;
import java.io.OutputStream;
import java.io.Serializable;
@ -15,7 +15,7 @@ import java.util.Map;
* @author looly
*
*/
public class RythmTemplate extends AbstractTemplate implements Serializable {
public class RythmTemplate implements Template, Serializable {
private static final long serialVersionUID = -132774960373894911L;
private final org.rythmengine.template.ITemplate rawTemplate;
@ -24,7 +24,7 @@ public class RythmTemplate extends AbstractTemplate implements Serializable {
* 包装Rythm模板
*
* @param template Rythm的模板对象 {@link org.rythmengine.template.ITemplate}
* @return {@link RythmTemplate}
* @return {@code RythmTemplate}
*/
public static RythmTemplate wrap(final org.rythmengine.template.ITemplate template) {
return (null == template) ? null : new RythmTemplate(template);

View File

@ -5,7 +5,7 @@ import cn.hutool.core.io.IoUtil;
import cn.hutool.core.reflect.TypeReference;
import cn.hutool.core.util.CharsetUtil;
import cn.hutool.core.util.ObjUtil;
import cn.hutool.extra.template.AbstractTemplate;
import cn.hutool.extra.template.Template;
import org.thymeleaf.TemplateEngine;
import org.thymeleaf.context.Context;
@ -22,7 +22,7 @@ import java.util.Map;
* @author looly
* @since 4.1.11
*/
public class ThymeleafTemplate extends AbstractTemplate implements Serializable {
public class ThymeleafTemplate implements Template, Serializable {
private static final long serialVersionUID = 781284916568562509L;
private final TemplateEngine engine;
@ -35,7 +35,7 @@ public class ThymeleafTemplate extends AbstractTemplate implements Serializable
* @param engine Thymeleaf的模板引擎对象 {@link TemplateEngine}
* @param template 模板路径或模板内容
* @param charset 编码
* @return {@link ThymeleafTemplate}
* @return {@code ThymeleafTemplate}
*/
public static ThymeleafTemplate wrap(final TemplateEngine engine, final String template, final Charset charset) {
return (null == engine) ? null : new ThymeleafTemplate(engine, template, charset);

View File

@ -3,9 +3,9 @@ package cn.hutool.extra.template.engine.velocity;
import cn.hutool.core.convert.Convert;
import cn.hutool.core.io.IoUtil;
import cn.hutool.core.reflect.TypeReference;
import cn.hutool.core.util.CharsetUtil;
import cn.hutool.core.text.StrUtil;
import cn.hutool.extra.template.AbstractTemplate;
import cn.hutool.core.util.CharsetUtil;
import cn.hutool.extra.template.Template;
import org.apache.velocity.VelocityContext;
import org.apache.velocity.app.Velocity;
@ -20,7 +20,7 @@ import java.util.Map;
* @author looly
*
*/
public class VelocityTemplate extends AbstractTemplate implements Serializable {
public class VelocityTemplate implements Template, Serializable {
private static final long serialVersionUID = -132774960373894911L;
private final org.apache.velocity.Template rawTemplate;

View File

@ -2,7 +2,6 @@ package cn.hutool.extra.template.engine.wit;
import cn.hutool.core.convert.Convert;
import cn.hutool.core.reflect.TypeReference;
import cn.hutool.extra.template.AbstractTemplate;
import org.febit.wit.Template;
import java.io.OutputStream;
@ -15,7 +14,7 @@ import java.util.Map;
*
* @author looly
*/
public class WitTemplate extends AbstractTemplate implements Serializable{
public class WitTemplate implements cn.hutool.extra.template.Template, Serializable{
private static final long serialVersionUID = 1L;
private final Template rawTemplate;

View File

@ -1,6 +1,10 @@
package cn.hutool.json;
import cn.hutool.core.collection.CollUtil;
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.mutable.MutableEntry;
import cn.hutool.core.map.MapUtil;
import cn.hutool.core.map.MapWrapper;
@ -12,6 +16,7 @@ import java.io.StringWriter;
import java.io.Writer;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.util.Arrays;
import java.util.Collection;
import java.util.Map;
import java.util.function.Predicate;
@ -176,6 +181,18 @@ public class JSONObject extends MapWrapper<String, Object> implements JSON, JSON
return this.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());
}
/**
* PUT 键值对到JSONObject中在忽略null模式下如果值为{@code null}将此键移除
*
@ -202,6 +219,22 @@ public class JSONObject extends MapWrapper<String, Object> implements JSON, JSON
return this;
}
/**
* 通过lambda批量设置值<br>
* 实际使用时可以使用getXXX的方法引用来完成键值对的赋值
* <pre>
* User user = GenericBuilder.of(User::new).with(User::setUsername, "hutool").build();
* (new JSONObject()).setFields(user::getNickname, user::getUsername);
* </pre>
*
* @param fields lambda,不能为空
* @return this
*/
public JSONObject setFields(final SerSupplier<?>... fields) {
Arrays.stream(fields).forEach(f -> set(LambdaUtil.getFieldName(f), f.get()));
return this;
}
/**
* 一次性Put 键值对如果key已经存在抛出异常如果键值中有null值忽略
*

View File

@ -3,6 +3,9 @@ package cn.hutool.setting;
import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.bean.copier.CopyOptions;
import cn.hutool.core.bean.copier.ValueProvider;
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.getter.GroupedTypeGetter;
import cn.hutool.core.lang.getter.TypeGetter;
import cn.hutool.core.reflect.ConstructorUtil;
@ -35,6 +38,19 @@ public abstract class AbsSetting implements TypeGetter<CharSequence>,
return ObjUtil.defaultIfNull(getObjByGroup(key, DEFAULT_GROUP), 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());
}
/**
* 获得字符串类型值如果字符串为{@code null}或者""返回默认值
*

View File

@ -10,6 +10,8 @@ import cn.hutool.core.io.watch.SimpleWatcher;
import cn.hutool.core.io.watch.WatchMonitor;
import cn.hutool.core.io.watch.WatchUtil;
import cn.hutool.core.lang.Assert;
import cn.hutool.core.lang.func.LambdaUtil;
import cn.hutool.core.lang.func.SerSupplier;
import cn.hutool.core.text.StrUtil;
import cn.hutool.core.util.CharUtil;
import cn.hutool.core.util.CharsetUtil;
@ -21,6 +23,7 @@ import java.net.URL;
import java.nio.charset.Charset;
import java.nio.file.Path;
import java.nio.file.WatchEvent;
import java.util.Arrays;
import java.util.Collection;
import java.util.LinkedHashMap;
import java.util.List;
@ -577,6 +580,22 @@ public class Setting extends AbsSetting implements Map<String, String> {
return this;
}
/**
* 通过lambda批量设置值<br>
* 实际使用时可以使用getXXX的方法引用来完成键值对的赋值
* <pre>
* User user = GenericBuilder.of(User::new).with(User::setUsername, "hutool").build();
* Setting.of().setFields(user::getNickname, user::getUsername);
* </pre>
*
* @param fields lambda,不能为空
* @return this
*/
public Setting setFields(final SerSupplier<String>... fields) {
Arrays.stream(fields).forEach(f -> set(LambdaUtil.getFieldName(f), f.get()));
return this;
}
/**
* 将键值对加入到对应分组中<br>
* 此方法用于与getXXX统一参数顺序

View File

@ -11,6 +11,10 @@ import cn.hutool.core.io.watch.SimpleWatcher;
import cn.hutool.core.io.watch.WatchMonitor;
import cn.hutool.core.io.watch.WatchUtil;
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;
import cn.hutool.core.map.MapUtil;
import cn.hutool.core.reflect.ConstructorUtil;
@ -27,6 +31,7 @@ import java.net.URL;
import java.nio.charset.Charset;
import java.nio.file.Path;
import java.nio.file.WatchEvent;
import java.util.Arrays;
import java.util.Properties;
/**
@ -230,6 +235,19 @@ public final class Props extends Properties implements TypeGetter<CharSequence>
return ObjUtil.defaultIfNull(getProperty(StrUtil.str(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());
}
/**
* 获取并删除键值对当指定键对应值非空时返回并删除这个值后边的键对应的值不再查找
*
@ -353,10 +371,26 @@ public final class Props extends Properties implements TypeGetter<CharSequence>
* @param key 属性键
* @param value 属性值
*/
public void setProperty(final String key, final Object value) {
public void set(final String key, final Object value) {
super.setProperty(key, value.toString());
}
/**
* 通过lambda批量设置值<br>
* 实际使用时可以使用getXXX的方法引用来完成键值对的赋值
* <pre>
* User user = GenericBuilder.of(User::new).with(User::setUsername, "hutool").build();
* Setting.of().setFields(user::getNickname, user::getUsername);
* </pre>
*
* @param fields lambda,不能为空
* @return this
*/
public Props setFields(final SerSupplier<?>... fields) {
Arrays.stream(fields).forEach(f -> set(LambdaUtil.getFieldName(f), f.get()));
return this;
}
/**
* 持久化当前设置会覆盖掉之前的设置
*

View File

@ -67,11 +67,11 @@ public class PropsTest {
public void toBeanWithNullPrefixTest(){
final Props configProp = new Props();
configProp.setProperty("createTime", Objects.requireNonNull(DateUtil.parse("2020-01-01")));
configProp.setProperty("isInit", true);
configProp.setProperty("stairPlan", 1);
configProp.setProperty("stageNum", 2);
configProp.setProperty("version", 3);
configProp.set("createTime", Objects.requireNonNull(DateUtil.parse("2020-01-01")));
configProp.set("isInit", true);
configProp.set("stairPlan", 1);
configProp.set("stageNum", 2);
configProp.set("version", 3);
final SystemConfig systemConfig = configProp.toBean(SystemConfig.class);
Assert.assertEquals(DateUtil.parse("2020-01-01"), systemConfig.getCreateTime());