v0.0.3 #8

Merged
timi merged 1 commits from dev into master 2026-04-27 15:50:11 +00:00
38 changed files with 1058 additions and 562 deletions

41
pom.xml
View File

@@ -6,14 +6,14 @@
<groupId>com.imyeyu.java</groupId> <groupId>com.imyeyu.java</groupId>
<artifactId>timi-java</artifactId> <artifactId>timi-java</artifactId>
<version>0.0.2</version> <version>0.0.3</version>
<packaging>jar</packaging> <packaging>jar</packaging>
<properties> <properties>
<maven.test.skip>true</maven.test.skip>
<maven.compiler.source>21</maven.compiler.source> <maven.compiler.source>21</maven.compiler.source>
<maven.compiler.target>21</maven.compiler.target> <maven.compiler.target>21</maven.compiler.target>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<maven.test.skip>true</maven.test.skip>
</properties> </properties>
<build> <build>
@@ -28,10 +28,42 @@
<artifactId>maven-source-plugin</artifactId> <artifactId>maven-source-plugin</artifactId>
<version>3.3.1</version> <version>3.3.1</version>
</plugin> </plugin>
<plugin>
<groupId>org.projectlombok</groupId>
<artifactId>lombok-maven-plugin</artifactId>
<version>1.18.20.0</version>
<configuration>
<sourceDirectory>${project.basedir}/src/main/java</sourceDirectory>
<outputDirectory>${project.build.directory}/delombok</outputDirectory>
<addOutputDirectory>false</addOutputDirectory>
<encoding>UTF-8</encoding>
</configuration>
<executions>
<execution>
<phase>generate-sources</phase>
<goals>
<goal>delombok</goal>
</goals>
</execution>
</executions>
<dependencies>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.36</version>
</dependency>
</dependencies>
</plugin>
<plugin> <plugin>
<groupId>org.apache.maven.plugins</groupId> <groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-javadoc-plugin</artifactId> <artifactId>maven-javadoc-plugin</artifactId>
<version>3.11.2</version> <version>3.11.2</version>
<configuration>
<sourcepath>${project.build.directory}/delombok</sourcepath>
<encoding>UTF-8</encoding>
<charset>UTF-8</charset>
<docencoding>UTF-8</docencoding>
</configuration>
</plugin> </plugin>
</plugins> </plugins>
</build> </build>
@@ -57,6 +89,11 @@
</repositories> </repositories>
<dependencies> <dependencies>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.40</version>
</dependency>
<dependency> <dependency>
<groupId>org.junit.jupiter</groupId> <groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter</artifactId> <artifactId>junit-jupiter</artifactId>

View File

@@ -5,14 +5,17 @@ import java.io.StringWriter;
import java.util.Collection; import java.util.Collection;
import java.util.Collections; import java.util.Collections;
import java.util.Map; import java.util.Map;
import java.util.Objects;
/** /**
* 通用工具
* *
* @author 夜雨 * @author 夜雨
* @since 2021-02-13 11:39 * @since 2021-02-13 11:39
*/ */
public interface TimiJava { public interface TimiJava {
/** 全 0 UUID */
String ZERO_UUID = "00000000-0000-0000-0000-000000000000"; String ZERO_UUID = "00000000-0000-0000-0000-000000000000";
/** /**
@@ -69,41 +72,87 @@ public interface TimiJava {
return !isEmpty(object); return !isEmpty(object);
} }
/**
* 为空时取默认值
*
* @param obj 判空对象
* @param defaultObj 默认对象
* @return 最终值
* @param <T> 对象类型
*/
static <T> T defaultIfNull(T obj, T defaultObj) { static <T> T defaultIfNull(T obj, T defaultObj) {
return firstNotNull(obj, defaultObj); return Objects.requireNonNullElse(obj, defaultObj);
} }
/**
* 为空时取默认值
*
* @param obj 判空对象
* @param defaultObj 默认对象
* @return 最终值
* @param <T> 对象类型
*/
static <T> T defaultIfEmpty(T obj, T defaultObj) { static <T> T defaultIfEmpty(T obj, T defaultObj) {
return firstNotEmpty(obj, defaultObj); if (isEmpty(obj)) {
return defaultObj;
}
return obj;
} }
/**
* 取第一个非空对象
*
* @param objects 对象列表
* @return 最终值
* @param <T> 对象类型
*/
@SafeVarargs @SafeVarargs
static <T> T firstNotNull(T... objects) { static <T> T firstNotNull(T... objects) {
for (int i = 0; i < objects.length; i++) { for (T object : objects) {
if (objects[i] != null) { if (object != null) {
return objects[i]; return object;
} }
} }
return null; return null;
} }
/**
* 取第一个非空对象
*
* @param objects 对象列表
* @return 最终值
* @param <T> 对象类型
*/
@SafeVarargs @SafeVarargs
static <T> T firstNotEmpty(T... objects) { static <T> T firstNotEmpty(T... objects) {
for (int i = 0; i < objects.length; i++) { for (T object : objects) {
if (TimiJava.isNotEmpty(objects[i])) { if (TimiJava.isNotEmpty(object)) {
return objects[i]; return object;
} }
} }
return null; return null;
} }
static String toString(Exception e) { /**
* 打印异常
*
* @param e 异常
* @return 异常文本
*/
static String serializeThrowable(Throwable e) {
StringWriter sw = new StringWriter(); StringWriter sw = new StringWriter();
PrintWriter pw = new PrintWriter(sw); PrintWriter pw = new PrintWriter(sw);
e.printStackTrace(pw); e.printStackTrace(pw);
return sw.toString(); return sw.toString();
} }
/**
* 空安全迭代器,使 for 迭代可以入参为空
*
* @param iterable 可迭代对象
* @return 安全迭代对象
* @param <T> 迭代对象类型
*/
static <T> Iterable<T> safeIterable(Iterable<T> iterable) { static <T> Iterable<T> safeIterable(Iterable<T> iterable) {
return defaultIfNull(iterable, Collections::emptyIterator); return defaultIfNull(iterable, Collections::emptyIterator);
} }

View File

@@ -1,9 +1,18 @@
package com.imyeyu.java.bean; package com.imyeyu.java.bean;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
/** /**
* 分页
*
* @author 夜雨 * @author 夜雨
* @since 2025-07-25 11:04 * @since 2025-07-25 11:04
*/ */
@Data
@NoArgsConstructor
@AllArgsConstructor
public class BasePage { public class BasePage {
/** 下标 */ /** 下标 */
@@ -12,35 +21,13 @@ public class BasePage {
/** 数据量 */ /** 数据量 */
protected long size = 16; protected long size = 16;
public BasePage() { /** 上一页 */
}
public BasePage(int index, long size) {
this.index = index;
this.size = size;
}
public void prev() { public void prev() {
index--; index--;
} }
/** 下一页 */
public void next() { public void next() {
index++; index++;
} }
public int getIndex() {
return index;
}
public void setIndex(int index) {
this.index = index;
}
public long getSize() {
return size;
}
public void setSize(long size) {
this.size = size;
}
} }

View File

@@ -1,13 +1,17 @@
package com.imyeyu.java.bean; package com.imyeyu.java.bean;
import com.imyeyu.java.TimiJava; import com.imyeyu.java.TimiJava;
import lombok.Data;
import java.util.List; import java.util.List;
/** /**
* 分页结果
*
* @author 夜雨 * @author 夜雨
* @since 2025-07-25 11:04 * @since 2025-07-25 11:04
*/ */
@Data
public class BasePageResult<T> { public class BasePageResult<T> {
/** 总数据量 */ /** 总数据量 */
@@ -16,6 +20,7 @@ public class BasePageResult<T> {
/** 总页数 */ /** 总页数 */
protected int pages; protected int pages;
/** 分页数据列表 */
protected List<T> list; protected List<T> list;
/** /**
@@ -36,15 +41,6 @@ public class BasePageResult<T> {
return !isEmpty(); return !isEmpty();
} }
/**
* 获取总数据量
*
* @return 总数据量
*/
public long getTotal() {
return total;
}
/** /**
* 设置总数据量 * 设置总数据量
* *
@@ -57,18 +53,15 @@ public class BasePageResult<T> {
} }
} }
public List<T> getList() { /**
return list; * 设置分页数据列表
} *
* @param list 分页数据列表
*/
public void setList(List<T> list) { public void setList(List<T> list) {
this.list = list; this.list = list;
if (TimiJava.isNotEmpty(list)) { if (TimiJava.isNotEmpty(list)) {
pages = (int) Math.ceil(1D * total / list.size()); pages = (int) Math.ceil(1D * total / list.size());
} }
} }
public int getPages() {
return pages;
}
} }

View File

@@ -11,6 +11,11 @@ public interface Callback {
/** 执行程序 */ /** 执行程序 */
void handler() throws RuntimeException; void handler() throws RuntimeException;
/**
* 非空时执行回调
*
* @param callback 回调
*/
static void handle(Callback callback) { static void handle(Callback callback) {
if (callback == null) { if (callback == null) {
return; return;

View File

@@ -21,7 +21,9 @@ public interface CallbackArg<T> {
/** /**
* 非空时执行回调 * 非空时执行回调
* *
* @param t 参数
* @param callbackArg 回调 * @param callbackArg 回调
* @param <T> 入参类型
*/ */
static <T> void handle(T t, CallbackArg<T> callbackArg) { static <T> void handle(T t, CallbackArg<T> callbackArg) {
if (callbackArg == null) { if (callbackArg == null) {

View File

@@ -23,7 +23,10 @@ public interface CallbackArgReturn<T, R> {
/** /**
* 非空时执行回调 * 非空时执行回调
* *
* @param t 参数
* @param callbackArgReturn 回调 * @param callbackArgReturn 回调
* @param <T> 入参类型
* @param <R> 返回类型
*/ */
static <T, R> R handle(T t, CallbackArgReturn<T, R> callbackArgReturn) { static <T, R> R handle(T t, CallbackArgReturn<T, R> callbackArgReturn) {
if (callbackArgReturn == null) { if (callbackArgReturn == null) {

View File

@@ -22,6 +22,7 @@ public interface CallbackReturn<R> {
* 非空时执行回调 * 非空时执行回调
* *
* @param callbackReturn 回调 * @param callbackReturn 回调
* @param <R> 返回类型
*/ */
static <R> R handle(CallbackReturn<R> callbackReturn) { static <R> R handle(CallbackReturn<R> callbackReturn) {
if (callbackReturn == null) { if (callbackReturn == null) {

View File

@@ -1,6 +1,7 @@
package com.imyeyu.java.bean; package com.imyeyu.java.bean;
import com.imyeyu.java.ref.Ref; import com.imyeyu.java.ref.Ref;
import lombok.Data;
/** /**
* 多语言 * 多语言
@@ -8,9 +9,12 @@ import com.imyeyu.java.ref.Ref;
* @author 夜雨 * @author 夜雨
* @since 2022-02-23 11:25 * @since 2022-02-23 11:25
*/ */
@Data
public class Language { public class Language {
/** /**
* 支持语言枚举
*
* @author 夜雨 * @author 夜雨
* @since 2025-12-05 14:31 * @since 2025-12-05 14:31
*/ */
@@ -67,68 +71,4 @@ public class Language {
throw new RuntimeException(e); throw new RuntimeException(e);
} }
} }
public String getKey() {
return key;
}
public void setKey(String key) {
this.key = key;
}
public String getZhCN() {
return zhCN;
}
public void setZhCN(String zhCN) {
this.zhCN = zhCN;
}
public String getZhTW() {
return zhTW;
}
public void setZhTW(String zhTW) {
this.zhTW = zhTW;
}
public String getEnUS() {
return enUS;
}
public void setEnUS(String enUS) {
this.enUS = enUS;
}
public String getRuRU() {
return ruRU;
}
public void setRuRU(String ruRU) {
this.ruRU = ruRU;
}
public String getKoKR() {
return koKR;
}
public void setKoKR(String koKR) {
this.koKR = koKR;
}
public String getJaJP() {
return jaJP;
}
public void setJaJP(String jaJP) {
this.jaJP = jaJP;
}
public String getDeDE() {
return deDE;
}
public void setDeDE(String deDE) {
this.deDE = deDE;
}
} }

View File

@@ -3,18 +3,52 @@ package com.imyeyu.java.bean;
import java.util.Map; import java.util.Map;
/** /**
* 多语言映射接口
*
* @author 夜雨 * @author 夜雨
* @since 2024-04-03 11:27 * @since 2024-04-03 11:27
*/ */
public interface LanguageMapping { public interface LanguageMapping {
/**
* 添加映射
*
* @param key 键
* @param value 值
*/
void add(String key, String value); void add(String key, String value);
/**
* 是否存在映射
*
* @param key 键
* @return true 为存在
*/
boolean has(String key); boolean has(String key);
/**
* 获取映射值
*
* @param key 键
* @return 值
*/
String text(String key); String text(String key);
/**
* 获取映射值,不存在时使用默认值
*
* @param key 键
* @param def 默认值
* @return 值
*/
String text(String key, String def); String text(String key, String def);
/**
* 获取映射值并插入参数
*
* @param key 键
* @param argsMap 参数列表
* @return 值
*/
String textArgs(String key, Map<String, Object> argsMap); String textArgs(String key, Map<String, Object> argsMap);
} }

View File

@@ -3,26 +3,79 @@ package com.imyeyu.java.bean;
import java.util.Map; import java.util.Map;
/** /**
* 多语言消息接口
*
* @author 夜雨 * @author 夜雨
* @since 2024-04-01 10:28 * @since 2024-04-01 10:28
*/ */
public interface LanguageMsgMapping<T> { public interface LanguageMsgMapping<T> {
/**
* 设置消息
*
* @param msg 消息
* @return 原对象
*/
T msg(String msg); T msg(String msg);
/**
* 设置消息多语言键
*
* @param msgKey 键
* @return 原对象
*/
T msgKey(String msgKey); T msgKey(String msgKey);
/**
* 设置消息多语言键并附加插值参数
*
* @param msgKey 键
* @param msgArgs 插值参数
* @return 原对象
*/
T msgKey(String msgKey, Map<String, Object> msgArgs); T msgKey(String msgKey, Map<String, Object> msgArgs);
/**
* 设置消息
*
* @param msg 消息
*/
void setMsg(String msg); void setMsg(String msg);
/**
* 获取消息
*
* @return 消息
*/
String getMsg(); String getMsg();
/**
* 设置消息多语言键
*
* @param msgKey 键
*/
void setMsgKey(String msgKey); void setMsgKey(String msgKey);
/**
* 设置消息多语言键并附加插值参数列表
*
* @param msgKey 键
* @param msgArgs 插值参数
*/
void setMsgKey(String msgKey, Map<String, Object> msgArgs); void setMsgKey(String msgKey, Map<String, Object> msgArgs);
/**
* 获取消息多语言键
*
* @return 键
*/
String getMsgKey(); String getMsgKey();
/**
* 获取附加插值参数
*
* @return 附加插值参数列表
*/
Map<String, Object> getMsgArgs(); Map<String, Object> getMsgArgs();
} }

View File

@@ -1,11 +1,14 @@
package com.imyeyu.java.bean.timi; package com.imyeyu.java.bean.timi;
import lombok.Getter;
/** /**
* 通用代码(基于 HTTP 代码扩展) * 通用代码(基于 HTTP 代码扩展)
* *
* @author 夜雨 * @author 夜雨
* @since 2021-05-21 14:32 * @since 2021-05-21 14:32
*/ */
@Getter
public enum TimiCode { public enum TimiCode {
// ---------- 200 正常 ---------- // ---------- 200 正常 ----------
@@ -74,6 +77,7 @@ public enum TimiCode {
/** 服务繁忙 */ /** 服务繁忙 */
ERROR_SERVICE_BUSY(50300); ERROR_SERVICE_BUSY(50300);
/** 代码 */
final Integer value; final Integer value;
TimiCode(Integer value) { TimiCode(Integer value) {
@@ -108,15 +112,6 @@ public enum TimiCode {
return new TimiResponse<>(this).msg(toString()); return new TimiResponse<>(this).msg(toString());
} }
/**
* 获取代码
*
* @return 代码
*/
public Integer getValue() {
return value;
}
/** /**
* 根据代码返回对象 * 根据代码返回对象
* *
@@ -125,9 +120,9 @@ public enum TimiCode {
*/ */
public static TimiCode fromCode(int code) { public static TimiCode fromCode(int code) {
TimiCode[] codes = values(); TimiCode[] codes = values();
for (int i = 0; i < codes.length; i++) { for (TimiCode timiCode : codes) {
if (codes[i].getValue() == code) { if (timiCode.getValue() == code) {
return codes[i]; return timiCode;
} }
} }
return null; return null;

View File

@@ -1,6 +1,7 @@
package com.imyeyu.java.bean.timi; package com.imyeyu.java.bean.timi;
import com.imyeyu.java.bean.LanguageMsgMapping; import com.imyeyu.java.bean.LanguageMsgMapping;
import lombok.Getter;
import java.util.Map; import java.util.Map;
@@ -13,6 +14,7 @@ import java.util.Map;
public class TimiError extends AssertionError implements LanguageMsgMapping<TimiError> { public class TimiError extends AssertionError implements LanguageMsgMapping<TimiError> {
/** 代码 */ /** 代码 */
@Getter
protected final TimiCode code; protected final TimiCode code;
protected transient String msgKey; protected transient String msgKey;
@@ -35,15 +37,6 @@ public class TimiError extends AssertionError implements LanguageMsgMapping<Timi
this.code = code; this.code = code;
} }
/**
* 获取代码
*
* @return 代码
*/
public TimiCode getCode() {
return code;
}
public TimiResponse<?> toResponse() { public TimiResponse<?> toResponse() {
return new TimiResponse<>(code).msg(getMessage()).msgKey(msgKey, msgArgs); return new TimiResponse<>(code).msg(getMessage()).msgKey(msgKey, msgArgs);
} }

View File

@@ -3,6 +3,7 @@ package com.imyeyu.java.bean.timi;
import com.imyeyu.java.TimiJava; import com.imyeyu.java.TimiJava;
import com.imyeyu.java.bean.CallbackReturn; import com.imyeyu.java.bean.CallbackReturn;
import com.imyeyu.java.bean.LanguageMsgMapping; import com.imyeyu.java.bean.LanguageMsgMapping;
import lombok.Getter;
import java.util.Map; import java.util.Map;
@@ -15,6 +16,7 @@ import java.util.Map;
public class TimiException extends RuntimeException implements LanguageMsgMapping<TimiException> { public class TimiException extends RuntimeException implements LanguageMsgMapping<TimiException> {
/** 代码 */ /** 代码 */
@Getter
protected final TimiCode code; protected final TimiCode code;
protected transient String msgKey; protected transient String msgKey;
@@ -48,15 +50,6 @@ public class TimiException extends RuntimeException implements LanguageMsgMappin
this.code = code; this.code = code;
} }
/**
* 获取代码
*
* @return 代码
*/
public TimiCode getCode() {
return code;
}
public TimiResponse<?> toResponse() { public TimiResponse<?> toResponse() {
return new TimiResponse<>(code).msg(getMessage()).msgKey(msgKey, msgArgs); return new TimiResponse<>(code).msg(getMessage()).msgKey(msgKey, msgArgs);
} }

View File

@@ -1,6 +1,9 @@
package com.imyeyu.java.bean.timi; package com.imyeyu.java.bean.timi;
import com.imyeyu.java.bean.LanguageMsgMapping; import com.imyeyu.java.bean.LanguageMsgMapping;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
import java.io.Serializable; import java.io.Serializable;
import java.util.Map; import java.util.Map;
@@ -11,9 +14,12 @@ import java.util.Map;
* @author 夜雨 * @author 夜雨
* @since 2021-07-01 20:18 * @since 2021-07-01 20:18
*/ */
@NoArgsConstructor
public class TimiResponse<T> implements Serializable, LanguageMsgMapping<TimiResponse<T>> { public class TimiResponse<T> implements Serializable, LanguageMsgMapping<TimiResponse<T>> {
/** 代码 */ /** 响应代码 */
@Setter
@Getter
protected Integer code; protected Integer code;
/** 消息 */ /** 消息 */
@@ -24,12 +30,10 @@ public class TimiResponse<T> implements Serializable, LanguageMsgMapping<TimiRes
protected transient Map<String, Object> msgArgs; protected transient Map<String, Object> msgArgs;
/** 数据体 */ /** 数据体 */
@Setter
@Getter
protected T data; protected T data;
/** 默认构造器 */
public TimiResponse() {
}
/** /**
* 构造器 * 构造器
* *
@@ -85,42 +89,6 @@ public class TimiResponse<T> implements Serializable, LanguageMsgMapping<TimiRes
return code.toException(msg); return code.toException(msg);
} }
/**
* 获取代码
*
* @return 代码
*/
public Integer getCode() {
return code;
}
/**
* 设置代码
*
* @param code 代码
*/
public void setCode(Integer code) {
this.code = code;
}
/**
* 获取数据体
*
* @return 数据体
*/
public T getData() {
return data;
}
/**
* 设置数据体
*
* @param data 数据体
*/
public void setData(T data) {
this.data = data;
}
/** /**
* 追加消息(避免和泛型字符串冲突) * 追加消息(避免和泛型字符串冲突)
* *

View File

@@ -3,16 +3,33 @@ package com.imyeyu.java.obs;
import java.util.List; import java.util.List;
/** /**
* 值变更监听器
*
* @param <T> 监听值类型
* @author 夜雨 * @author 夜雨
* @since 2024-09-01 15:15 * @since 2024-09-01 15:15
*/ */
public interface ChangeListener<T> { public interface ChangeListener<T> {
/**
* 处理值变更事件
*
* @param from 变更前值
* @param to 变更后值
*/
void handler(T from, T to); void handler(T from, T to);
/**
* 通知全部监听器处理本次变更
*
* @param listenerList 监听器列表
* @param from 变更前值
* @param to 变更后值
* @param <T> 监听值类型
*/
static <T> void notifyListener(List<ChangeListener<T>> listenerList, T from, T to) { static <T> void notifyListener(List<ChangeListener<T>> listenerList, T from, T to) {
for (int i = 0; i < listenerList.size(); i++) { for (ChangeListener<T> listener : listenerList) {
listenerList.get(i).handler(from, to); listener.handler(from, to);
} }
} }
} }

View File

@@ -3,27 +3,49 @@ package com.imyeyu.java.obs;
import java.util.List; import java.util.List;
/** /**
* 集合元素变更监听器
*
* @param <E> 元素类型
* @author 夜雨 * @author 夜雨
* @since 2024-09-01 15:59 * @since 2024-09-01 15:59
*/ */
public interface CollectionChangeListener<E> { public interface CollectionChangeListener<E> {
/** /**
* 集合变更类型
*
* @author 夜雨 * @author 夜雨
* @since 2024-09-01 19:47 * @since 2024-09-01 19:47
*/ */
enum ChangeType { enum ChangeType {
/** 新增元素 */
ADD, ADD,
/** 删除元素 */
REMOVE REMOVE
} }
/**
* 处理集合元素变更
*
* @param type 变更类型
* @param e 本次变更元素
* @throws RuntimeException 监听器处理失败时抛出
*/
void handler(ChangeType type, E e) throws RuntimeException; void handler(ChangeType type, E e) throws RuntimeException;
/**
* 通知全部集合监听器处理本次变更
*
* @param listenerList 监听器列表
* @param type 变更类型
* @param element 本次变更元素
* @param <E> 元素类型
*/
static <E> void notifyListener(List<CollectionChangeListener<E>> listenerList, ChangeType type, E element) { static <E> void notifyListener(List<CollectionChangeListener<E>> listenerList, ChangeType type, E element) {
for (int i = 0; i < listenerList.size(); i++) { for (CollectionChangeListener<E> listener : listenerList) {
listenerList.get(i).handler(type, element); listener.handler(type, element);
} }
} }
} }

View File

@@ -3,32 +3,56 @@ package com.imyeyu.java.obs;
import java.util.List; import java.util.List;
/** /**
* 映射变更监听器
*
* @param <K> 键类型
* @param <V> 值类型
* @author 夜雨 * @author 夜雨
* @since 2024-09-01 18:01 * @since 2024-09-01 18:01
*/ */
public interface MapChangeListener<K, V> { public interface MapChangeListener<K, V> {
/** /**
* * 映射变更类型
* *
* @author 夜雨 * @author 夜雨
* @since 2024-09-01 23:51 * @since 2024-09-01 23:51
*/ */
enum ChangeType { enum ChangeType {
/** 新增键值对 */
ADD, ADD,
/** 更新既有键值对 */
UPDATE, UPDATE,
/** 删除键值对 */
REMOVE REMOVE
} }
/**
* 处理映射变更
*
* @param type 变更类型
* @param key 变更键
* @param value 变更值
*/
void handler(ChangeType type, K key, V value); void handler(ChangeType type, K key, V value);
/**
* 通知全部映射监听器处理本次变更
*
* @param listenerList 监听器列表
* @param type 变更类型
* @param key 变更键
* @param value 变更值
* @param <K> 键类型
* @param <V> 值类型
*/
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
static <K, V> void notifyListener(List<MapChangeListener<K, V>> listenerList, ChangeType type, Object key, V value) { static <K, V> void notifyListener(List<MapChangeListener<K, V>> listenerList, ChangeType type, Object key, V value) {
for (int i = 0; i < listenerList.size(); i++) { for (MapChangeListener<K, V> kvMapChangeListener : listenerList) {
listenerList.get(i).handler(type, (K) key, value); kvMapChangeListener.handler(type, (K) key, value);
} }
} }
} }

View File

@@ -1,16 +1,39 @@
package com.imyeyu.java.obs; package com.imyeyu.java.obs;
/** /**
* 可观察对象接口
*
* @param <T> 可观察值类型
* @author 夜雨 * @author 夜雨
* @since 2024-09-01 15:07 * @since 2024-09-01 15:07
*/ */
public interface Observable<T> { public interface Observable<T> {
/**
* 获取当前值
*
* @return 当前值
*/
T get(); T get();
/**
* 设置当前值
*
* @param value 新值
*/
void set(T value); void set(T value);
/**
* 添加值变更监听器
*
* @param changeListener 值变更监听器
*/
void addListener(ChangeListener<T> changeListener); void addListener(ChangeListener<T> changeListener);
/**
* 移除值变更监听器
*
* @param changeListener 值变更监听器
*/
void removeListener(ChangeListener<T> changeListener); void removeListener(ChangeListener<T> changeListener);
} }

View File

@@ -1,15 +1,21 @@
package com.imyeyu.java.obs; package com.imyeyu.java.obs;
import lombok.NoArgsConstructor;
/** /**
* 可观察布尔值
*
* @author 夜雨 * @author 夜雨
* @since 2024-09-01 18:43 * @since 2024-09-01 18:43
*/ */
@NoArgsConstructor
public class ObservableBoolean extends ObservableObject<Boolean> { public class ObservableBoolean extends ObservableObject<Boolean> {
public ObservableBoolean() { /**
super(); * 使用指定初始值创建可观察布尔值
} *
* @param value 初始值
*/
public ObservableBoolean(Boolean value) { public ObservableBoolean(Boolean value) {
super(value); super(value);
} }

View File

@@ -1,15 +1,21 @@
package com.imyeyu.java.obs; package com.imyeyu.java.obs;
import lombok.NoArgsConstructor;
/** /**
* 可观察双精度值
*
* @author 夜雨 * @author 夜雨
* @since 2024-09-01 18:43 * @since 2024-09-01 18:43
*/ */
@NoArgsConstructor
public class ObservableDouble extends ObservableObject<Double> { public class ObservableDouble extends ObservableObject<Double> {
public ObservableDouble() { /**
super(); * 使用指定数值创建可观察双精度值
} *
* @param value 初始值
*/
public ObservableDouble(Number value) { public ObservableDouble(Number value) {
super(value.doubleValue()); super(value.doubleValue());
} }

View File

@@ -1,15 +1,21 @@
package com.imyeyu.java.obs; package com.imyeyu.java.obs;
import lombok.NoArgsConstructor;
/** /**
* 可观察浮点值
*
* @author 夜雨 * @author 夜雨
* @since 2024-09-01 18:43 * @since 2024-09-01 18:43
*/ */
@NoArgsConstructor
public class ObservableFloat extends ObservableObject<Float> { public class ObservableFloat extends ObservableObject<Float> {
public ObservableFloat() { /**
super(); * 使用指定初始值创建可观察浮点值
} *
* @param value 初始值
*/
public ObservableFloat(Float value) { public ObservableFloat(Float value) {
super(value); super(value);
} }

View File

@@ -1,14 +1,21 @@
package com.imyeyu.java.obs; package com.imyeyu.java.obs;
import lombok.NoArgsConstructor;
/** /**
* 可观察整型值
*
* @author 夜雨 * @author 夜雨
* @since 2024-09-01 15:06 * @since 2024-09-01 15:06
*/ */
@NoArgsConstructor
public class ObservableInteger extends ObservableObject<Integer> { public class ObservableInteger extends ObservableObject<Integer> {
public ObservableInteger() { /**
} * 使用指定初始值创建可观察整型值
*
* @param value 初始值
*/
public ObservableInteger(Integer value) { public ObservableInteger(Integer value) {
super(value); super(value);
} }

View File

@@ -7,28 +7,56 @@ import java.util.List;
import java.util.function.Predicate; import java.util.function.Predicate;
/** /**
* 可观察列表,支持监听元素新增和删除事件
*
* @param <E> 元素类型
* @author 夜雨 * @author 夜雨
* @since 2024-09-01 17:51 * @since 2024-09-01 17:51
*/ */
public class ObservableList<E> extends ArrayList<E> { public class ObservableList<E> extends ArrayList<E> {
/** 集合变更监听器列表 */
private final List<CollectionChangeListener<E>> changeListenerList = new ArrayList<>(); private final List<CollectionChangeListener<E>> changeListenerList = new ArrayList<>();
/**
* 创建空的可观察列表
*/
public ObservableList() { public ObservableList() {
} }
/**
* 使用指定集合创建可观察列表
*
* @param c 初始化集合
*/
public ObservableList(Collection<? extends E> c) { public ObservableList(Collection<? extends E> c) {
super(c); super(c);
} }
/**
* 添加集合变更监听器
*
* @param listener 集合变更监听器
*/
public void addChangeListener(CollectionChangeListener<E> listener) { public void addChangeListener(CollectionChangeListener<E> listener) {
changeListenerList.add(listener); changeListenerList.add(listener);
} }
/**
* 移除集合变更监听器
*
* @param listener 集合变更监听器
*/
public void removeChangeListener(CollectionChangeListener<E> listener) { public void removeChangeListener(CollectionChangeListener<E> listener) {
changeListenerList.add(listener); changeListenerList.remove(listener);
} }
/**
* 新增元素并通知监听器
*
* @param e 待新增元素
* @return true 表示列表发生变化
*/
@Override @Override
public boolean add(E e) { public boolean add(E e) {
boolean result = super.add(e); boolean result = super.add(e);
@@ -38,12 +66,24 @@ public class ObservableList<E> extends ArrayList<E> {
return result; return result;
} }
/**
* 在指定下标新增元素并通知监听器
*
* @param index 插入下标
* @param element 待新增元素
*/
@Override @Override
public void add(int index, E element) { public void add(int index, E element) {
super.add(index, element); super.add(index, element);
CollectionChangeListener.notifyListener(changeListenerList, CollectionChangeListener.ChangeType.ADD, element); CollectionChangeListener.notifyListener(changeListenerList, CollectionChangeListener.ChangeType.ADD, element);
} }
/**
* 批量新增元素并逐个通知监听器
*
* @param c 待新增元素集合
* @return true 表示列表发生变化
*/
@Override @Override
public boolean addAll(Collection<? extends E> c) { public boolean addAll(Collection<? extends E> c) {
boolean result = super.addAll(c); boolean result = super.addAll(c);
@@ -55,6 +95,13 @@ public class ObservableList<E> extends ArrayList<E> {
return result; return result;
} }
/**
* 从指定下标开始批量新增元素并逐个通知监听器
*
* @param index 起始下标
* @param c 待新增元素集合
* @return true 表示列表发生变化
*/
@Override @Override
public boolean addAll(int index, Collection<? extends E> c) { public boolean addAll(int index, Collection<? extends E> c) {
boolean result = super.addAll(index, c); boolean result = super.addAll(index, c);
@@ -66,6 +113,12 @@ public class ObservableList<E> extends ArrayList<E> {
return result; return result;
} }
/**
* 删除指定下标元素并通知监听器
*
* @param index 待删除元素下标
* @return 被删除元素
*/
@Override @Override
public E remove(int index) { public E remove(int index) {
E removed = super.remove(index); E removed = super.remove(index);
@@ -75,11 +128,23 @@ public class ObservableList<E> extends ArrayList<E> {
return removed; return removed;
} }
/**
* 删除指定元素并通知监听器
*
* @param o 待删除元素
* @return true 表示删除成功
*/
@Override @Override
public boolean remove(Object o) { public boolean remove(Object o) {
return remove(indexOf(o)) != null; return remove(indexOf(o)) != null;
} }
/**
* 批量删除元素并逐个通知监听器
*
* @param c 待删除元素集合
* @return true 表示列表发生变化
*/
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
@Override @Override
public boolean removeAll(Collection<?> c) { public boolean removeAll(Collection<?> c) {
@@ -92,6 +157,12 @@ public class ObservableList<E> extends ArrayList<E> {
return result; return result;
} }
/**
* 按条件删除元素
*
* @param filter 删除条件
* @return true 表示至少删除一个元素
*/
@Override @Override
public boolean removeIf(Predicate<? super E> filter) { public boolean removeIf(Predicate<? super E> filter) {
boolean removed = false; boolean removed = false;
@@ -106,6 +177,13 @@ public class ObservableList<E> extends ArrayList<E> {
return removed; return removed;
} }
/**
* 获取子列表的可观察副本
*
* @param fromIndex 起始下标(含)
* @param toIndex 结束下标(不含)
* @return 子列表副本
*/
@Override @Override
public ObservableList<E> subList(int fromIndex, int toIndex) { public ObservableList<E> subList(int fromIndex, int toIndex) {
List<E> subList = super.subList(fromIndex, toIndex); List<E> subList = super.subList(fromIndex, toIndex);

View File

@@ -1,15 +1,21 @@
package com.imyeyu.java.obs; package com.imyeyu.java.obs;
import lombok.NoArgsConstructor;
/** /**
* 可观察长整型值
*
* @author 夜雨 * @author 夜雨
* @since 2024-09-01 18:43 * @since 2024-09-01 18:43
*/ */
@NoArgsConstructor
public class ObservableLong extends ObservableObject<Long> { public class ObservableLong extends ObservableObject<Long> {
public ObservableLong() { /**
super(); * 使用指定初始值创建可观察长整型值
} *
* @param value 初始值
*/
public ObservableLong(Long value) { public ObservableLong(Long value) {
super(value); super(value);
} }

View File

@@ -6,21 +6,43 @@ import java.util.List;
import java.util.Map; import java.util.Map;
/** /**
* 可观察映射,支持监听键值对新增、更新和删除事件
*
* @param <K> 键类型
* @param <V> 值类型
* @author 夜雨 * @author 夜雨
* @since 2024-09-01 18:32 * @since 2024-09-01 18:32
*/ */
public class ObservableMap<K, V> extends HashMap<K, V> { public class ObservableMap<K, V> extends HashMap<K, V> {
/** 映射变更监听器列表 */
private final List<MapChangeListener<K, V>> changeListenerList = new ArrayList<>(); private final List<MapChangeListener<K, V>> changeListenerList = new ArrayList<>();
/**
* 添加映射变更监听器
*
* @param listener 映射变更监听器
*/
public void addChangeListener(MapChangeListener<K, V> listener) { public void addChangeListener(MapChangeListener<K, V> listener) {
changeListenerList.add(listener); changeListenerList.add(listener);
} }
/**
* 移除映射变更监听器
*
* @param listener 映射变更监听器
*/
public void removeChangeListener(MapChangeListener<K, V> listener) { public void removeChangeListener(MapChangeListener<K, V> listener) {
changeListenerList.add(listener); changeListenerList.remove(listener);
} }
/**
* 写入键值对并在必要时通知监听器
*
* @param key 键
* @param value 值
* @return 旧值,不存在则为 null
*/
@Override @Override
public V put(K key, V value) { public V put(K key, V value) {
V v = super.put(key, value); V v = super.put(key, value);
@@ -32,6 +54,11 @@ public class ObservableMap<K, V> extends HashMap<K, V> {
return v; return v;
} }
/**
* 批量写入键值对
*
* @param m 待写入映射
*/
@Override @Override
public void putAll(Map<? extends K, ? extends V> m) { public void putAll(Map<? extends K, ? extends V> m) {
for (Map.Entry<? extends K, ? extends V> item : m.entrySet()) { for (Map.Entry<? extends K, ? extends V> item : m.entrySet()) {
@@ -39,6 +66,12 @@ public class ObservableMap<K, V> extends HashMap<K, V> {
} }
} }
/**
* 删除指定键并通知监听器
*
* @param key 键
* @return 被删除值,不存在则为 null
*/
@Override @Override
public V remove(Object key) { public V remove(Object key) {
V removed = super.remove(key); V removed = super.remove(key);
@@ -48,6 +81,13 @@ public class ObservableMap<K, V> extends HashMap<K, V> {
return removed; return removed;
} }
/**
* 仅当键值都匹配时删除并通知监听器
*
* @param key 键
* @param value 值
* @return true 表示删除成功
*/
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
@Override @Override
public boolean remove(Object key, Object value) { public boolean remove(Object key, Object value) {

View File

@@ -1,30 +1,42 @@
package com.imyeyu.java.obs; package com.imyeyu.java.obs;
import lombok.NoArgsConstructor;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
/** /**
* 可观察对象抽象基类
*
* @param <T> 可观察值类型
* @author 夜雨 * @author 夜雨
* @since 2024-09-01 15:22 * @since 2024-09-01 15:22
*/ */
@NoArgsConstructor
public abstract class ObservableObject<T> implements Observable<T> { public abstract class ObservableObject<T> implements Observable<T> {
/** 值变更监听器列表 */
private final List<ChangeListener<T>> changeListenerList = new ArrayList<>(); private final List<ChangeListener<T>> changeListenerList = new ArrayList<>();
/** 当前值 */
private T value; private T value;
public ObservableObject() { /**
} * 使用指定初始值创建可观察对象
*
* @param value 初始值
*/
public ObservableObject(T value) { public ObservableObject(T value) {
this.value = value; this.value = value;
} }
/** {@inheritDoc} */
@Override @Override
public final T get() { public final T get() {
return value; return value;
} }
/** {@inheritDoc} */
@Override @Override
public final void set(T toValue) { public final void set(T toValue) {
final T from = this.value; final T from = this.value;
@@ -35,11 +47,13 @@ public abstract class ObservableObject<T> implements Observable<T> {
} }
} }
/** {@inheritDoc} */
@Override @Override
public void addListener(ChangeListener<T> changeListener) { public void addListener(ChangeListener<T> changeListener) {
changeListenerList.add(changeListener); changeListenerList.add(changeListener);
} }
/** {@inheritDoc} */
@Override @Override
public void removeListener(ChangeListener<T> changeListener) { public void removeListener(ChangeListener<T> changeListener) {
changeListenerList.remove(changeListener); changeListenerList.remove(changeListener);

View File

@@ -8,21 +8,41 @@ import java.util.List;
import java.util.function.Predicate; import java.util.function.Predicate;
/** /**
* 可观察集合,支持监听元素新增和删除事件
*
* @param <E> 元素类型
* @author 夜雨 * @author 夜雨
* @since 2024-09-01 18:29 * @since 2024-09-01 18:29
*/ */
public class ObservableSet<E> extends HashSet<E> { public class ObservableSet<E> extends HashSet<E> {
/** 集合变更监听器列表 */
private final List<CollectionChangeListener<E>> changeListenerList = new ArrayList<>(); private final List<CollectionChangeListener<E>> changeListenerList = new ArrayList<>();
/**
* 添加集合变更监听器
*
* @param listener 集合变更监听器
*/
public void addChangeListener(CollectionChangeListener<E> listener) { public void addChangeListener(CollectionChangeListener<E> listener) {
changeListenerList.add(listener); changeListenerList.add(listener);
} }
/**
* 移除集合变更监听器
*
* @param listener 集合变更监听器
*/
public void removeChangeListener(CollectionChangeListener<E> listener) { public void removeChangeListener(CollectionChangeListener<E> listener) {
changeListenerList.add(listener); changeListenerList.remove(listener);
} }
/**
* 新增元素并通知监听器
*
* @param e 待新增元素
* @return true 表示集合发生变化
*/
@Override @Override
public boolean add(E e) { public boolean add(E e) {
boolean result = super.add(e); boolean result = super.add(e);
@@ -32,6 +52,12 @@ public class ObservableSet<E> extends HashSet<E> {
return result; return result;
} }
/**
* 批量新增元素并逐个通知监听器
*
* @param c 待新增元素集合
* @return true 表示集合发生变化
*/
@Override @Override
public boolean addAll(Collection<? extends E> c) { public boolean addAll(Collection<? extends E> c) {
boolean result = super.addAll(c); boolean result = super.addAll(c);
@@ -43,6 +69,12 @@ public class ObservableSet<E> extends HashSet<E> {
return result; return result;
} }
/**
* 删除指定元素并通知监听器
*
* @param o 待删除元素
* @return true 表示删除成功
*/
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
@Override @Override
public boolean remove(Object o) { public boolean remove(Object o) {
@@ -53,6 +85,12 @@ public class ObservableSet<E> extends HashSet<E> {
return removed; return removed;
} }
/**
* 批量删除元素并逐个通知监听器
*
* @param c 待删除元素集合
* @return true 表示集合发生变化
*/
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
@Override @Override
public boolean removeAll(Collection<?> c) { public boolean removeAll(Collection<?> c) {
@@ -65,6 +103,12 @@ public class ObservableSet<E> extends HashSet<E> {
return result; return result;
} }
/**
* 按条件删除元素
*
* @param filter 删除条件
* @return true 表示至少删除一个元素
*/
@Override @Override
public boolean removeIf(Predicate<? super E> filter) { public boolean removeIf(Predicate<? super E> filter) {
boolean removed = false; boolean removed = false;

View File

@@ -1,27 +1,48 @@
package com.imyeyu.java.obs; package com.imyeyu.java.obs;
import lombok.NoArgsConstructor;
/** /**
* 可观察字符串值
*
* @author 夜雨 * @author 夜雨
* @since 2024-09-01 18:43 * @since 2024-09-01 18:43
*/ */
@NoArgsConstructor
public class ObservableString extends ObservableObject<String> { public class ObservableString extends ObservableObject<String> {
public ObservableString() { /**
super(); * 使用指定初始值创建可观察字符串值
} *
* @param value 初始值
*/
public ObservableString(String value) { public ObservableString(String value) {
super(value); super(value);
} }
/**
* 判断当前字符串是否为空或全空白
*
* @return true 表示空串或全空白
*/
public boolean isEmpty() { public boolean isEmpty() {
return get().isEmpty() || get().trim().isEmpty(); return get().isEmpty() || get().trim().isEmpty();
} }
/**
* 判断当前字符串是否非空且非全空白
*
* @return true 表示非空且包含非空白字符
*/
public boolean isNotEmpty() { public boolean isNotEmpty() {
return !isEmpty(); return !isEmpty();
} }
/**
* 获取当前字符串值
*
* @return 当前字符串值
*/
@Override @Override
public String toString() { public String toString() {
return get(); return get();

View File

@@ -7,7 +7,7 @@ import java.util.Collections;
import java.util.List; import java.util.List;
/** /**
* 反射相关 * 反射工具类,提供字段与方法查找、字段读写、字符串转枚举等能力
* *
* @author 夜雨 * @author 夜雨
* @since 2023-05-04 15:05 * @since 2023-05-04 15:05
@@ -15,9 +15,9 @@ import java.util.List;
public class Ref { public class Ref {
/** /**
* 获取类字段列表 * 获取指定类声明的字段列表(不包含父类)
* *
* @param clazz 类 * @param clazz 类型对象
* @return 字段列表 * @return 字段列表
*/ */
public static List<Field> listFields(Class<?> clazz) { public static List<Field> listFields(Class<?> clazz) {
@@ -25,9 +25,9 @@ public class Ref {
} }
/** /**
* 获取类字段列表(包括父类 * 获取指定类及其父类声明的字段列表(直到 {@link Object} 之前
* *
* @param clazz 类 * @param clazz 类型对象
* @return 字段列表 * @return 字段列表
*/ */
public static List<Field> listAllFields(Class<?> clazz) { public static List<Field> listAllFields(Class<?> clazz) {
@@ -40,20 +40,21 @@ public class Ref {
} }
/** /**
* 将外部键名转换为 Java 字段名
* *
* @param keyName * @param keyName 外部键名,支持中划线、下划线和空格分隔
* @return * @return 驼峰命名字段名
*/ */
public static String getFieldName(String keyName) { public static String getFieldName(String keyName) {
String[] splits = {"-", "_", " "}; String[] splits = {"-", "_", " "};
StringBuilder full = new StringBuilder(keyName.substring(0, 1).toUpperCase() + keyName.substring(1)); StringBuilder full = new StringBuilder(keyName.substring(0, 1).toUpperCase() + keyName.substring(1));
for (int i = 0; i < splits.length; i++) { for (String split : splits) {
if (keyName.contains(splits[i])) { if (keyName.contains(split)) {
// 存在分隔符 // 存在分隔符时,先按分隔符拆词,再按驼峰拼接
full.setLength(0); full.setLength(0);
String[] word = keyName.split(splits[i]); String[] word = keyName.split(split);
for (int j = 0; j < word.length; j++) { for (String s : word) {
full.append(word[j].substring(0, 1).toUpperCase()).append(word[j].substring(1)); full.append(s.substring(0, 1).toUpperCase()).append(s.substring(1));
} }
break; break;
} }
@@ -62,11 +63,12 @@ public class Ref {
} }
/** /**
* 反射获取对象字段,包括父级类,直至 {@link Object},如果都不存在则返回 null * 反射获取对象字段,包括父级类,直至 {@link Object},如果都不存在则抛出异常
* *
* @param clazz 类 * @param clazz 类
* @param fieldName 字段名 * @param fieldName 字段名
* @return 字段 * @return 字段对象
* @throws NullPointerException 找不到字段时抛出异常
*/ */
public static Field getField(Class<?> clazz, String fieldName) { public static Field getField(Class<?> clazz, String fieldName) {
do { do {
@@ -78,7 +80,7 @@ public class Ref {
clazz = clazz.getSuperclass(); clazz = clazz.getSuperclass();
} }
} while (clazz != Object.class); } while (clazz != Object.class);
throw new NullPointerException(String.format("not found field: %s in %s", fieldName, clazz)); throw new NullPointerException("not found field: %s in %s".formatted(fieldName, clazz));
} }
/** /**
@@ -105,7 +107,7 @@ public class Ref {
* @param <T> 返回类型 * @param <T> 返回类型
* @return 字段值 * @return 字段值
* @throws IllegalAccessException 反射访问失败 * @throws IllegalAccessException 反射访问失败
* @throws NullPointerException 向上反射直至 {@link Object} 也找不到该字段 * @throws NullPointerException 字段对象为空
*/ */
public static <T> T getFieldValue(Object object, Field field, Class<? extends T> toClass) throws IllegalAccessException, NullPointerException { public static <T> T getFieldValue(Object object, Field field, Class<? extends T> toClass) throws IllegalAccessException, NullPointerException {
if (field == null) { if (field == null) {
@@ -120,7 +122,7 @@ public class Ref {
* *
* @param objectClass 类 * @param objectClass 类
* @param fieldName 字段名 * @param fieldName 字段名
* @return 字段 * @return 字段对象,不存在时返回 null
*/ */
public static Field getClassField(Class<?> objectClass, String fieldName) { public static Field getClassField(Class<?> objectClass, String fieldName) {
try { try {
@@ -160,16 +162,19 @@ public class Ref {
* @param fieldName 字段名 * @param fieldName 字段名
* @param value 字段值 * @param value 字段值
* @throws IllegalAccessException 反射访问失败 * @throws IllegalAccessException 反射访问失败
* @throws NoSuchFieldException 向上反射直至 {@link Object} 也找不到该字段
*/ */
public static void setFieldValue(Object object, String fieldName, Object value) throws IllegalAccessException, NoSuchFieldException { public static void setFieldValue(Object object, String fieldName, Object value) throws IllegalAccessException {
Field field = getField(object.getClass(), fieldName); setFieldValue(object, getField(object.getClass(), fieldName), value);
if (field == null) {
throw new NoSuchFieldException("not found " + fieldName + " field in " + object.getClass().getSimpleName());
}
setFieldValue(object, field, value);
} }
/**
* 反射设置对象字段值
*
* @param object 对象
* @param field 字段对象
* @param value 字段值
* @throws IllegalAccessException 反射访问失败
*/
public static void setFieldValue(Object object, Field field, Object value) throws IllegalAccessException { public static void setFieldValue(Object object, Field field, Object value) throws IllegalAccessException {
field.setAccessible(true); field.setAccessible(true);
field.set(object, value); field.set(object, value);
@@ -200,7 +205,8 @@ public class Ref {
* @param clazz 类 * @param clazz 类
* @param methodName 方法名 * @param methodName 方法名
* @param parameterTypes 可选参 * @param parameterTypes 可选参
* @return 方法对象 * @return 方法对象,不存在时返回 null
* @throws NullPointerException 类对象为空
*/ */
public static Method getMethod(Class<?> clazz, String methodName, Class<?>... parameterTypes) { public static Method getMethod(Class<?> clazz, String methodName, Class<?>... parameterTypes) {
if (clazz == null) { if (clazz == null) {
@@ -224,7 +230,8 @@ public class Ref {
* @param clazz 枚举类 * @param clazz 枚举类
* @param string 字符串 * @param string 字符串
* @param <T> 泛型 * @param <T> 泛型
* @return 泛型 * @return 匹配到的枚举值,找不到或输入为空时返回 null
* @throws IllegalArgumentException 传入类型不是枚举类型
*/ */
public static <T extends Enum<?>> T toType(Class<T> clazz, String string) { public static <T extends Enum<?>> T toType(Class<T> clazz, String string) {
if (string == null) { if (string == null) {
@@ -234,31 +241,32 @@ public class Ref {
if (ts == null) { if (ts == null) {
throw new IllegalArgumentException(clazz.getName() + " is not an enum type"); throw new IllegalArgumentException(clazz.getName() + " is not an enum type");
} }
for (int i = 0; i < ts.length; i++) { for (T t : ts) {
if (ts[i].name().equalsIgnoreCase(string)) { if (t.name().equalsIgnoreCase(string)) {
return ts[i]; return t;
} }
} }
return null; return null;
} }
/** /**
* 创建字段反射元信息对象
* *
* * @param owner 字段所属类型
* @param owner * @param keyName 字段键名
* @param keyName * @return 字段反射元信息
* @return
*/ */
public static RefField field(Class<?> owner, String keyName) { public static RefField field(Class<?> owner, String keyName) {
return new RefField(owner, keyName); return new RefField(owner, keyName);
} }
/** /**
* 通过无参构造器创建实例
* *
* @param type * @param type 类型对象
* @return * @param <T> 泛型类型
* @param <T> * @return 新实例
* @throws Exception * @throws Exception 反射创建实例异常
*/ */
public static <T> T newInstance(Class<T> type) throws Exception { public static <T> T newInstance(Class<T> type) throws Exception {
return type.getDeclaredConstructor().newInstance(); return type.getDeclaredConstructor().newInstance();

View File

@@ -1,21 +1,39 @@
package com.imyeyu.java.ref; package com.imyeyu.java.ref;
import lombok.Getter;
import lombok.Setter;
import java.lang.reflect.Field; import java.lang.reflect.Field;
import java.lang.reflect.Method; import java.lang.reflect.Method;
/** /**
* * 字段反射元信息,包含字段类型以及对应的 getter/setter 方法
* *
* @author 夜雨 * @author 夜雨
* @since 2024-04-26 15:46 * @since 2024-04-26 15:46
*/ */
@Getter
@Setter
public class RefField { public class RefField {
/** 字段类型 */
private Class<?> type; private Class<?> type;
/** 字段对象 */
private Field field; private Field field;
/** 字段 setter 方法 */
private Method setter; private Method setter;
/** 字段 getter 方法 */
private Method getter; private Method getter;
/**
* 根据拥有者类型和键名构建字段反射元信息
*
* @param owner 字段所属类型
* @param keyName 字段键名,支持中划线、下划线和空格形式
*/
RefField(Class<?> owner, String keyName) { RefField(Class<?> owner, String keyName) {
String fieldName = Ref.getFieldName(keyName); String fieldName = Ref.getFieldName(keyName);
String fieldNameUpper = String.valueOf(fieldName.charAt(0)).toUpperCase() + fieldName.substring(1); String fieldNameUpper = String.valueOf(fieldName.charAt(0)).toUpperCase() + fieldName.substring(1);
@@ -38,40 +56,7 @@ public class RefField {
setterName = "set" + fieldNameUpper; setterName = "set" + fieldNameUpper;
getterName = "get" + fieldNameUpper; getterName = "get" + fieldNameUpper;
} }
setter = Ref.getMethod(owner, setterName, field.getType()); setter = Ref.getMethod(owner, setterName, field.getType());
getter = Ref.getMethod(owner, getterName); getter = Ref.getMethod(owner, getterName);
} }
public Class<?> getType() {
return type;
}
public void setType(Class<?> type) {
this.type = type;
}
public Field getField() {
return field;
}
public void setField(Field field) {
this.field = field;
}
public Method getSetter() {
return setter;
}
public void setSetter(Method setter) {
this.setter = setter;
}
public Method getGetter() {
return getter;
}
public void setGetter(Method getter) {
this.getter = getter;
}
} }

View File

@@ -2,10 +2,15 @@ package com.imyeyu.java.thread;
import com.imyeyu.java.bean.Callback; import com.imyeyu.java.bean.Callback;
import com.imyeyu.java.bean.CallbackArg; import com.imyeyu.java.bean.CallbackArg;
import com.imyeyu.java.bean.timi.TimiException;
import lombok.Builder;
import lombok.Data;
import lombok.Getter;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
/** /**
* 异步可重试执行器
* *
* @author 夜雨 * @author 夜雨
* @since 2025-11-06 17:45 * @since 2025-11-06 17:45
@@ -24,16 +29,30 @@ public class AsyncRetryExecutor {
/** true 为守护进程 */ /** true 为守护进程 */
private static final boolean DEFAULT_DAEMON = false; private static final boolean DEFAULT_DAEMON = false;
/** 实际执行重试逻辑的线程 */
private final Thread thread; private final Thread thread;
/** true 为执行成功 */
@Getter
private volatile boolean isSuccess = false; private volatile boolean isSuccess = false;
/** true 为处于运行状态 */
@Getter
private volatile boolean isRunning = false; private volatile boolean isRunning = false;
private AsyncRetryExecutor(Builder builder) { /**
* 根据构建器参数创建异步重试执行器
*
* @param task 构建器对象
*/
private AsyncRetryExecutor(Task task) {
TimiException.required(task, "not found task");
TimiException.required(task.getCallback(), "not found task.callback");
thread = new Thread(() -> { thread = new Thread(() -> {
int retryCount = 0; int retryCount = 0;
while (isRunning && !isSuccess && (builder.maxRetry < 0 || retryCount <= builder.maxRetry)) { while (isRunning && !isSuccess && (task.maxRetry < 0 || retryCount <= task.maxRetry)) {
try { try {
builder.callback.handler(); task.callback.handler();
isSuccess = true; isSuccess = true;
} catch (Exception e) { } catch (Exception e) {
retryCount++; retryCount++;
@@ -41,22 +60,22 @@ public class AsyncRetryExecutor {
// 中断 // 中断
break; break;
} }
if (0 < builder.maxRetry && builder.maxRetry < retryCount) { if (0 < task.maxRetry && task.maxRetry < retryCount) {
// 超过重试次数 // 超过重试次数
builder.onRetryExhausted.handler(e); task.onRetryExhausted.handler(e);
break; break;
} }
// 重试 // 重试
try { try {
TimeUnit.MILLISECONDS.sleep(builder.retryInterval); TimeUnit.MILLISECONDS.sleep(task.retryInterval);
} catch (InterruptedException ex) { } catch (InterruptedException ex) {
Thread.currentThread().interrupt(); Thread.currentThread().interrupt();
break; break;
} }
} }
} }
}, builder.threadNamePrefix + Thread.currentThread().threadId()); }, task.threadNamePrefix + Thread.currentThread().threadId());
thread.setDaemon(builder.daemon); thread.setDaemon(task.daemon);
} }
/** /**
@@ -65,64 +84,64 @@ public class AsyncRetryExecutor {
* @author 夜雨 * @author 夜雨
* @since 2025-11-06 23:37 * @since 2025-11-06 23:37
*/ */
public static class Builder { @Data
@Builder
public static class Task {
// 必需参数 /** 必填回调方法 */
private final Callback callback; private Callback callback;
// 可选参数(带默认值) /** 最大重试次数,-1 为无限重试 */
private int maxRetry = DEFAULT_MAX_RETRY; private int maxRetry = DEFAULT_MAX_RETRY;
/** 重试间隔毫秒数 */
private long retryInterval = DEFAULT_RETRY_INTERVAL; private long retryInterval = DEFAULT_RETRY_INTERVAL;
/** 线程名前缀 */
private String threadNamePrefix = DEFAULT_THREAD_NAME_PREFIX; private String threadNamePrefix = DEFAULT_THREAD_NAME_PREFIX;
/** 是否设置为守护线程 */
private boolean daemon = DEFAULT_DAEMON; private boolean daemon = DEFAULT_DAEMON;
/** 重试耗尽时的异常回调 */
private CallbackArg<Exception> onRetryExhausted; private CallbackArg<Exception> onRetryExhausted;
public Builder(Callback callback) {
this.callback = callback;
}
public Builder maxRetry(int maxRetry) {
this.maxRetry = maxRetry;
return this;
}
public Builder retryInterval(long retryInterval) {
this.retryInterval = retryInterval;
return this;
}
public Builder threadNamePrefix(String prefix) {
this.threadNamePrefix = prefix;
return this;
}
public Builder daemon(boolean daemon) {
this.daemon = daemon;
return this;
}
public Builder onRetryExhausted(CallbackArg<Exception> onRetryExhausted) {
this.onRetryExhausted = onRetryExhausted;
return this;
}
public AsyncRetryExecutor build() {
return new AsyncRetryExecutor(this);
}
} }
/**
* 使用默认参数创建执行器
*
* @param callback 执行回调
* @return 执行器实例
*/
public static AsyncRetryExecutor create(Callback callback) { public static AsyncRetryExecutor create(Callback callback) {
return new Builder(callback).build(); return new AsyncRetryExecutor(Task.builder().callback(callback).build());
} }
/**
* 使用指定最大重试次数创建执行器
*
* @param callback 执行回调
* @param maxRetry 最大重试次数
* @return 执行器实例
*/
public static AsyncRetryExecutor create(Callback callback, int maxRetry) { public static AsyncRetryExecutor create(Callback callback, int maxRetry) {
return new Builder(callback).maxRetry(maxRetry).build(); return new AsyncRetryExecutor(Task.builder().callback(callback).maxRetry(maxRetry).build());
} }
/**
* 使用指定重试间隔创建执行器
*
* @param callback 执行回调
* @param interval 重试间隔毫秒数
* @return 执行器实例
*/
public static AsyncRetryExecutor create(Callback callback, long interval) { public static AsyncRetryExecutor create(Callback callback, long interval) {
return new Builder(callback).retryInterval(interval).build(); return new AsyncRetryExecutor(Task.builder().callback(callback).retryInterval(interval).build());
} }
/**
* 启动异步执行
*/
public void start() { public void start() {
if (!isRunning) { if (!isRunning) {
isRunning = true; isRunning = true;
@@ -131,18 +150,13 @@ public class AsyncRetryExecutor {
} }
} }
/**
* 中断执行并停止后续重试
*/
public void interrupt() { public void interrupt() {
isRunning = false; isRunning = false;
if (thread.isAlive() && !thread.isInterrupted()) { if (thread.isAlive() && !thread.isInterrupted()) {
thread.interrupt(); thread.interrupt();
} }
} }
public boolean isSuccess() {
return isSuccess;
}
public boolean isRunning() {
return isRunning;
}
} }

View File

@@ -1,14 +1,23 @@
package test; package com.imyeyu.java;
import com.imyeyu.java.thread.AsyncRetryExecutor; import com.imyeyu.java.thread.AsyncRetryExecutor;
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.assertTrue;
/** /**
* 线程工具相关测试
*
* @author 夜雨 * @author 夜雨
* @since 2025-11-06 23:59 * @since 2025-11-06 23:59
*/ */
public class TestThread { public class AsyncRetryExecutorTest {
/**
* 验证异步重试执行器在成功后状态为成功
*
* @throws InterruptedException 等待异步完成时中断异常
*/
@Test @Test
public void testAsyncRetryExecutor() throws InterruptedException { public void testAsyncRetryExecutor() throws InterruptedException {
Object lock = new Object(); Object lock = new Object();
@@ -25,11 +34,9 @@ public class TestThread {
} }
}); });
executor.start(); executor.start();
synchronized (lock) { synchronized (lock) {
lock.wait(); lock.wait();
} }
assertTrue(executor.isSuccess());
assert executor.isSuccess();
} }
} }

View File

@@ -1,4 +1,4 @@
package test; package com.imyeyu.java;
import com.imyeyu.java.obs.ObservableList; import com.imyeyu.java.obs.ObservableList;
import com.imyeyu.java.obs.ObservableMap; import com.imyeyu.java.obs.ObservableMap;
@@ -7,12 +7,18 @@ import org.junit.jupiter.api.Test;
import java.util.List; import java.util.List;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertFalse;
/** /**
* 可观察对象相关测试
*
* @author 夜雨 * @author 夜雨
* @since 2024-09-01 23:26 * @since 2024-09-01 23:26
*/ */
public class TestObs { public class ObservableTest {
/** 验证可观察字符串赋值后值会更新 */
@Test @Test
public void testString() { public void testString() {
ObservableString obsString = new ObservableString("test1"); ObservableString obsString = new ObservableString("test1");
@@ -20,9 +26,10 @@ public class TestObs {
obsString.addListener((from, to) -> System.out.printf("from %s to %s%n", from, to)); obsString.addListener((from, to) -> System.out.printf("from %s to %s%n", from, to));
obsString.set("test2"); obsString.set("test2");
System.out.println("now value = " + obsString); System.out.println("now value = " + obsString);
assert obsString.get().equals("test2"); assertEquals("test2", obsString.get());
} }
/** 验证可观察列表在多次增删后的最终状态 */
@Test @Test
public void testList() { public void testList() {
ObservableList<String> list = new ObservableList<>(); ObservableList<String> list = new ObservableList<>();
@@ -40,8 +47,10 @@ public class TestObs {
list.removeAll(List.of("test3", "test4")); list.removeAll(List.of("test3", "test4"));
list.removeIf(item -> item.equals("test1")); // 移除 0, 1, 2, 3, 4 list.removeIf(item -> item.equals("test1")); // 移除 0, 1, 2, 3, 4
System.out.println(list); System.out.println(list);
assertEquals(List.of("test5", "test7", "test8"), list);
} }
/** 验证可观察映射在新增、更新与删除后的最终状态 */
@Test @Test
public void testMap() { public void testMap() {
ObservableMap<String, String> map = new ObservableMap<>(); ObservableMap<String, String> map = new ObservableMap<>();
@@ -56,5 +65,11 @@ public class TestObs {
map.remove("t1"); map.remove("t1");
map.remove("t2", "test2"); map.remove("t2", "test2");
System.out.println(map); System.out.println(map);
assertEquals(3, map.size());
assertEquals("test3", map.get("t3"));
assertEquals("test4", map.get("t4"));
assertEquals("test5", map.get("t5"));
assertFalse(map.containsKey("t1"));
assertFalse(map.containsKey("t2"));
} }
} }

View File

@@ -0,0 +1,109 @@
package com.imyeyu.java;
import com.imyeyu.java.ref.Ref;
import com.imyeyu.java.ref.RefField;
import lombok.Data;
import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNotNull;
/**
* 反射工具相关测试
*
* @author 夜雨
* @since 2024-04-26 11:05
*/
@Data
public class RefTest {
/** 布尔字段,使用 is 前缀 */
private boolean isBoy;
/** 布尔字段,不使用 is 前缀 */
private boolean boy;
/** 普通字符串字段 */
private String fieldName;
/** 验证字段名标准化转换规则 */
@Test
public void test() {
String t1 = Ref.getFieldName("is-boy");
assertEquals("isBoy", t1);
String t2 = Ref.getFieldName("is_boy");
assertEquals("isBoy", t2);
String t3 = Ref.getFieldName("is boy");
assertEquals("isBoy", t3);
String t4 = Ref.getFieldName("isBoy");
assertEquals("isBoy", t4);
String t5 = Ref.getFieldName("boy");
assertEquals("boy", t5);
String t6 = Ref.getFieldName("fieldName");
assertEquals("fieldName", t6);
String t7 = Ref.getFieldName("field name");
assertEquals("fieldName", t7);
String t8 = Ref.getFieldName("field-Name");
assertEquals("fieldName", t8);
String t9 = Ref.getFieldName("field_Name");
assertEquals("fieldName", t9);
}
/** 验证布尔字段 is-boy 能映射到标准 getter/setter */
@Test
public void refBooleanField() {
RefField refField = Ref.field(RefTest.class, "is-boy");
assertNotNull(refField.getSetter());
assertEquals("setBoy", refField.getSetter().getName());
assertNotNull(refField.getGetter());
assertEquals("isBoy", refField.getGetter().getName());
}
/** 验证布尔字段 boy 能映射到标准 getter/setter */
@Test
public void refBoolean2Field() {
RefField refField = Ref.field(RefTest.class, "boy");
assertNotNull(refField.getSetter());
assertEquals("setBoy", refField.getSetter().getName());
assertNotNull(refField.getGetter());
assertEquals("isBoy", refField.getGetter().getName());
}
/** 验证字段名 fieldName 能映射到标准 getter/setter */
@Test
public void refStringField() {
RefField refField = Ref.field(RefTest.class, "fieldName");
assertNotNull(refField.getSetter());
assertEquals("setFieldName", refField.getSetter().getName());
assertNotNull(refField.getGetter());
assertEquals("getFieldName", refField.getGetter().getName());
}
/** 验证字段名 field-name 能映射到标准 getter/setter */
@Test
public void refString2Field() {
RefField refField = Ref.field(RefTest.class, "field-name");
assertNotNull(refField.getSetter());
assertEquals("setFieldName", refField.getSetter().getName());
assertNotNull(refField.getGetter());
assertEquals("getFieldName", refField.getGetter().getName());
}
/** 验证字段名 field_name 能映射到标准 getter/setter */
@Test
public void refString3Field() {
RefField refField = Ref.field(RefTest.class, "field_name");
assertNotNull(refField.getSetter());
assertEquals("setFieldName", refField.getSetter().getName());
assertNotNull(refField.getGetter());
assertEquals("getFieldName", refField.getGetter().getName());
}
/** 验证字段名 field name 能映射到标准 getter/setter */
@Test
public void refString4Field() {
RefField refField = Ref.field(RefTest.class, "field name");
assertNotNull(refField.getSetter());
assertEquals("setFieldName", refField.getSetter().getName());
assertNotNull(refField.getGetter());
assertEquals("getFieldName", refField.getGetter().getName());
}
}

View File

@@ -0,0 +1,87 @@
package com.imyeyu.java;
import org.junit.jupiter.api.Test;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertIterableEquals;
import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.junit.jupiter.api.Assertions.assertNull;
import static org.junit.jupiter.api.Assertions.assertTrue;
/**
* {@link TimiJava} 静态工具方法测试
*
* @author 夜雨
* @since 2026-04-27 10:00
*/
public class TimiJavaTest {
/** 验证 isEmpty 与 isNotEmpty 的主要判空分支 */
@Test
public void testEmptyCheck() {
assertTrue(TimiJava.isEmpty(null));
assertTrue(TimiJava.isEmpty(""));
assertTrue(TimiJava.isEmpty(" "));
assertTrue(TimiJava.isEmpty(Collections.emptyList()));
assertTrue(TimiJava.isEmpty(Collections.emptyMap()));
assertFalse(TimiJava.isEmpty("value"));
assertTrue(TimiJava.isNotEmpty("value"));
}
/** 验证 defaultIfNull 与 defaultIfEmpty 返回默认值逻辑 */
@Test
public void testDefaultValue() {
assertEquals("default", TimiJava.defaultIfNull(null, "default"));
assertEquals("value", TimiJava.defaultIfNull("value", "default"));
assertEquals("default", TimiJava.defaultIfEmpty(" ", "default"));
assertEquals("value", TimiJava.defaultIfEmpty("value", "default"));
}
/** 验证 firstNotNull 与 firstNotEmpty 的首个命中逻辑 */
@Test
public void testFirstValue() {
assertEquals("v1", TimiJava.firstNotNull(null, "v1", "v2"));
assertNull(TimiJava.firstNotNull(null, null));
assertEquals("v1", TimiJava.firstNotEmpty("", " ", "v1", "v2"));
assertNull(TimiJava.firstNotEmpty("", " ", null));
}
/** 验证异常序列化结果包含异常类型与消息 */
@Test
public void testSerializeThrowable() {
String text = TimiJava.serializeThrowable(new IllegalArgumentException("bad"));
assertTrue(text.contains(IllegalArgumentException.class.getName()));
assertTrue(text.contains("bad"));
}
/** 验证 safeIterable 在空值与非空场景下都可安全迭代 */
@Test
public void testSafeIterable() {
Iterable<String> empty = TimiJava.safeIterable(null);
assertNotNull(empty);
assertFalse(empty.iterator().hasNext());
Iterable<String> values = TimiJava.safeIterable(List.of("a", "b"));
assertIterableEquals(List.of("a", "b"), values);
}
/** 验证 ZERO_UUID 常量值 */
@Test
public void testZeroUuid() {
assertEquals("00000000-0000-0000-0000-000000000000", TimiJava.ZERO_UUID);
}
/** 验证数组和映射判空路径 */
@Test
public void testArrayAndMapEmptyCheck() {
assertTrue(TimiJava.isEmpty(new String[]{}));
assertFalse(TimiJava.isEmpty(new String[]{"v"}));
assertTrue(TimiJava.isEmpty(Map.of()));
assertFalse(TimiJava.isEmpty(Map.of("k", "v")));
}
}

View File

@@ -1,96 +0,0 @@
package test;
import com.imyeyu.java.ref.Ref;
import com.imyeyu.java.ref.RefField;
import org.junit.jupiter.api.Test;
/**
* @author 夜雨
* @since 2024-04-26 11:05
*/
public class TestRef {
private boolean isBoy;
private boolean boy;
private String fieldName;
@Test
public void test() {
String t1 = Ref.getFieldName("is-boy");
assert t1.equals("isBoy");
String t2 = Ref.getFieldName("is_boy");
assert t2.equals("isBoy");
String t3 = Ref.getFieldName("is boy");
assert t3.equals("isBoy");
String t4 = Ref.getFieldName("isBoy");
assert t4.equals("isBoy");
String t5 = Ref.getFieldName("boy");
assert t5.equals("boy");
String t6 = Ref.getFieldName("fieldName");
assert t6.equals("fieldName");
String t7 = Ref.getFieldName("field name");
assert t7.equals("fieldName");
String t8 = Ref.getFieldName("field-Name");
assert t8.equals("fieldName");
String t9 = Ref.getFieldName("field_Name");
assert t9.equals("fieldName");
}
@Test
public void refBooleanField() {
RefField refField = Ref.field(TestRef.class, "is-boy");
assert refField.getSetter() != null && refField.getSetter().getName().equals("setBoy");
assert refField.getGetter() != null && refField.getGetter().getName().equals("isBoy");
}
@Test
public void refBoolean2Field() {
RefField refField = Ref.field(TestRef.class, "boy");
assert refField.getSetter() != null && refField.getSetter().getName().equals("setBoy");
assert refField.getGetter() != null && refField.getGetter().getName().equals("isBoy");
}
@Test
public void refStringField() {
RefField refField = Ref.field(TestRef.class, "fieldName");
assert refField.getSetter() != null && refField.getSetter().getName().equals("setFieldName");
assert refField.getGetter() != null && refField.getGetter().getName().equals("getFieldName");
}
@Test
public void refString2Field() {
RefField refField = Ref.field(TestRef.class, "field-name");
assert refField.getSetter() != null && refField.getSetter().getName().equals("setFieldName");
assert refField.getGetter() != null && refField.getGetter().getName().equals("getFieldName");
}
@Test
public void refString3Field() {
RefField refField = Ref.field(TestRef.class, "field_name");
assert refField.getSetter() != null && refField.getSetter().getName().equals("setFieldName");
assert refField.getGetter() != null && refField.getGetter().getName().equals("getFieldName");
}
@Test
public void refString4Field() {
RefField refField = Ref.field(TestRef.class, "field name");
assert refField.getSetter() != null && refField.getSetter().getName().equals("setFieldName");
assert refField.getGetter() != null && refField.getGetter().getName().equals("getFieldName");
}
public boolean isBoy() {
return isBoy;
}
public void setBoy(boolean boy) {
isBoy = boy;
}
public String getFieldName() {
return fieldName;
}
public void setFieldName(String fieldName) {
this.fieldName = fieldName;
}
}