在框架開發、數據映射、表單綁定等場景中,常需根據字符串屬性名動態設置對象字段值。原文提供的代碼為 iOS Objective-C 鍵盤處理邏輯,與標題不符。本文基於 Java 反射實現通用的 BeanSetter 工具類。
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.Arrays;
public final class BeanSetter {
private BeanSetter() {}
/**
* 根據屬性名設置值(支援私有字段、Setter 方法、基本類型自動轉換)
*/
public static void setProperty(Object target, String propertyName, Object value) {
if (target == null || propertyName == null) return;
Class<?> clazz = target.getClass();
try {
// 1. 嘗試調用 Setter 方法(標準 JavaBean 規範)
String setterName = "set" + Character.toUpperCase(propertyName.charAt(0)) + propertyName.substring(1);
Method setter = findSetter(clazz, setterName, value);
if (setter != null) {
setter.invoke(target, convertValue(value, setter.getParameterTypes()[0]));
return;
}
// 2. 回退:直接設置字段(暴力反射)
Field field = findField(clazz, propertyName);
if (field != null) {
field.setAccessible(true);
field.set(target, convertValue(value, field.getType()));
return;
}
throw new NoSuchFieldException("Property not found: " + propertyName);
} catch (Exception e) {
throw new RuntimeException("Failed to set property '" + propertyName + "' on " + clazz.getName(), e);
}
}
private static Method findSetter(Class<?> clazz, String name, Object value) {
return Arrays.stream(clazz.getMethods())
.filter(m -> m.getName().equals(name) && m.getParameterCount() == 1)
.filter(m -> m.getParameterTypes()[0].isAssignableFrom(value == null ? Object.class : value.getClass())
|| isPrimitiveWrapperMatch(m.getParameterTypes()[0], value))
.findFirst().orElse(null);
}
private static Field findField(Class<?> clazz, String name) {
while (clazz != null) {
try {
return clazz.getDeclaredField(name);
} catch (NoSuchFieldException e) {
clazz = clazz.getSuperclass();
}
}
return null;
}
private static boolean isPrimitiveWrapperMatch(Class<?> paramType, Object value) {
if (value == null) return false;
Class<?> valueType = value.getClass();
// 基本類型與包裝類互配
if (paramType == int.class && valueType == Integer.class) return true;
if (paramType == long.class && valueType == Long.class) return true;
if (paramType == double.class && valueType == Double.class) return true;
if (paramType == float.class && valueType == Float.class) return true;
if (paramType == boolean.class && valueType == Boolean.class) return true;
if (paramType == short.class && valueType == Short.class) return true;
if (paramType == byte.class && valueType == Byte.class) return true;
if (paramType == char.class && valueType == Character.class) return true;
return false;
}
private static Object convertValue(Object value, Class<?> targetType) {
if (value == null) return null;
if (targetType.isAssignableFrom(value.getClass())) return value;
// 字符串 → 基本類型/包裝類/日期 等常見轉換
if (value instanceof String) {
String str = (String) value;
if (targetType == int.class || targetType == Integer.class) return Integer.valueOf(str);
if (targetType == long.class || targetType == Long.class) return Long.valueOf(str);
if (targetType == double.class || targetType == Double.class) return Double.valueOf(str);
if (targetType == float.class || targetType == Float.class) return Float.valueOf(str);
if (targetType == boolean.class || targetType == Boolean.class) return Boolean.valueOf(str);
if (targetType == short.class || targetType == Short.class) return Short.valueOf(str);
if (targetType == byte.class || targetType == Byte.class) return Byte.valueOf(str);
if (targetType == char.class || targetType == Character.class) return str.charAt(0);
// 可擴展 LocalDate, BigDecimal 等
}
return value; // 最後嘗試直接傳入
}
}
public class User {
private String name;
private int age;
private boolean active;
// getter/setter
}
User user = new User();
BeanSetter.setProperty(user, "name", "張三");
BeanSetter.setProperty(user, "age", "25"); // 字符串自動轉 int
BeanSetter.setProperty(user, "active", true);
System.out.println(user.getName() + ", " + user.getAge() + ", " + user.isActive());
// 輸出:張三, 25, true
address.city 路徑表達式。list[0].name、map['key']。ConversionService(Spring)或自定義 Converter 體系。Method/Field 與 PropertyDescriptor,提升高頻調用性能。通過反射實現動態屬性設置,核心在於:優先使用 Setter 方法(符合規範、觸發邏輯),回退字段直接設置(突破封裝),並輔以基本類型/包裝類自動轉換與字符串通用解析。上述工具類可直接用於表單綁定、配置映射、測試數據構建等場景。