首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> Java 程式設計 >> Java 相關知識

Java 反射動態設置對象屬性值通用工具類實現

     編輯:Java 相關知識

場景與需求

在框架開發、數據映射、表單綁定等場景中,常需根據字符串屬性名動態設置對象字段值。原文提供的代碼為 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 方法(符合規範、觸發邏輯),回退字段直接設置(突破封裝),並輔以基本類型/包裝類自動轉換與字符串通用解析。上述工具類可直接用於表單綁定、配置映射、測試數據構建等場景。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved