Invoking all setters within a class using reflection

后端 未结 4 1837
时光取名叫无心
时光取名叫无心 2020-12-11 10:03

I have a domain object, that for the purposes of this question I will call Person with the following private variables:

String name
int age

相关标签:
4条回答
  • 2020-12-11 10:25

    I think you could use a library, the Apache Commons BeanUtils. If you have a map that contains field and value pairs, the class PropertyUtils can help you:

    Person person = new Person();
    for(Map.Entry<String, Object> entry : map.entrySet())
        PropertyUtils.setProperty(person, entry.getKey(), entry.getValue());
    
    0 讨论(0)
  • 2020-12-11 10:37

    Have you tried BeanUtils.populate()) from Apache Commons BeanUtils?

    BeanUtils.populate(yourObject, propertiesMap);
    
    0 讨论(0)
  • 2020-12-11 10:39

    This is a full solution that verifies output class beforehand and consequently calls setters for all the properties that the map contains. It uses purely java.beans and java.lang.reflect.

    public Object mapToObject(Map<String, Object> input, Class<?> outputType) {
        Object outputObject = null;
        List<PropertyDescriptor> outputPropertyDescriptors = null;
        // Test if class is instantiable with default constructor
        if(isInstantiable(outputType) 
                && hasDefaultConstructor(outputType)
                && (outputPropertyDescriptors = getPropertyDescriptors(outputType)) != null) {
            try {
                outputObject = outputType.getConstructor().newInstance();
                for(PropertyDescriptor pd : outputPropertyDescriptors) {
                    Object value = input.get(pd.getName());
                    if(value != null) {
                        pd.getWriteMethod().invoke(outputObject, value);
                    }
                }
            } catch (InstantiationException|IllegalAccessException|InvocationTargetException|NoSuchMethodException e) {
                throw new IllegalStateException("Failed to instantiate verified class " + outputType, e);
            }
        } else {
            throw new IllegalArgumentException("Specified outputType class " + outputType + "cannot be instantiated with default constructor!");
        }
        return outputObject;
    }
    
    private List<PropertyDescriptor> getPropertyDescriptors(Class<?> outputType) {
        List<PropertyDescriptor> propertyDescriptors = null;
        try {
            propertyDescriptors = Arrays.asList(Introspector.getBeanInfo(outputType, Object.class).getPropertyDescriptors());
        } catch (IntrospectionException e) {
        }
        return propertyDescriptors;
    }
    
    private boolean isInstantiable(Class<?> clazz) {
        return ! clazz.isInterface() && ! Modifier.isAbstract(clazz.getModifiers());
    }
    
    private boolean hasDefaultConstructor(Class<?> clazz) {
        try {
            clazz.getConstructor();
            return true;
        } catch (NoSuchMethodException e) {
            return false;
        }
    }
    
    0 讨论(0)
  • 2020-12-11 10:42

    Sure it's possible! You can get all methods that start with "set" back by doing this:

    Class curClass = myclass.class;
    Method[] allMethods = curClass.getMethods();
    List<Method> setters = new ArrayList<Method>();
    for(Method method : allMethods) {
        if(method.getName().startsWith("set")) {
            setters.add(method);
        }
    }
    

    Now you've got the methods. Do you already know how to call them for your instance of the class?

    0 讨论(0)
提交回复
热议问题