RhythmNDev255
RhythmNDev255

Reputation: 21

Custom reflection method for setting field

I'm fairly new to using reflection and am currently trying to implement a custom reflection util for setting fields. Looking at springboots ReflectionsTestUtils, I've got a mild understanding of how the setFields is done. I'm currently trying to implement that by taking an Object and an ImmutableMap as parameters.

The thought is to match the Field/Key value from the hashmap with a field that exists in the object. I understand the overall idea but the method i’ve tried implementing doesn’t seem work for retrieving the field from the map and comparing the one in the object.

My question is: Is there a more effiecient way to achieve this than what I have tried implementing below?

Not sure if i'm heading the right way here and would appreciate all the tips given:

    public static void setField(Object object, ImmutableMap<String, Object> map)
  throws IllegalArgumentException, IllegalAccessException, NoSuchFieldException, SecurityException {
      if (object instanceof Object) {
        Field[] fields = object.getClass().getDeclaredFields();

        for (Field aField : fields) {
         for (Map.Entry<String, Object> entry : map.entrySet()) {
          if (entry.getKey() instanceof Object) {
           String value = entry.getKey();
           checkPrimitiveValue(aField, object, value);
         }
        }
       }
    }

}

   private static void checkPrimitiveValue(Field field, Object object, String value) throws IllegalArgumentException, IllegalAccessException {
// Find out Primitive and parse the string to correct value
if (field.getType() == boolean.class) {
  field.setAccessible(true);
  field.set(object, Boolean.parseBoolean(value));
} else if (field.getType() == long.class) {
  field.setAccessible(true);
  field.set(object, Long.parseLong(value));
} else if (field.getType() == int.class) {
  field.setAccessible(true);
  field.set(object, Integer.parseInt(value));
} else if (field.getType() == double.class) {
  field.setAccessible(true);
  field.set(object, Double.parseDouble(value));
} else { // String is used
  field.setAccessible(true);
  field.set(object, value);
}

}

Upvotes: 0

Views: 350

Answers (1)

Maurice Perry
Maurice Perry

Reputation: 9650

The test:

 if (object instanceof Object)

is useless, except in the case where object is null, so here is a better way to write it:

 if (object != null)

You're assigning every key in the map to every field of the object. I guess you wanted to do something like:

    public static void setField(Object object, ImmutableMap<String, Object> map)
  throws IllegalArgumentException, IllegalAccessException, NoSuchFieldException, SecurityException {
        if (object != null) {
            Field[] fields = object.getClass().getDeclaredFields();

            for (Field aField: fields) {
                Object value = map.get(aField.getName());
                assignValue(aField, object, value);
            }
        }
    }

Now I don't get why you're passing assuming that the values in the map are Strings (the static type is Object).

private static void assignValue(Field field, Object object, Object value)
    throws IllegalArgumentException, IllegalAccessException,
            NoSuchFieldException, SecurityException {
    field.setAccessible(true);
    if (field.getType().isPrimitive())
        if (value == null) {
            value = defaultValue(field.getType());
        } else if (value instanceof String) {
            value = parseValue(field.getType(), (String)value);
        }
    }
    field.set(object, value);
}

Default value of a primitive type:

private static Object defaultValue(Class<?> clazz) {
    if (clazz == boolean.class) {
        return Boolean.FALSE;
    } else if (clazz == char.class) {
        return (char)0;
    } else if (clazz == byte.class) {
        return (byte)0;
    } else if (clazz == short.class) {
        return (short)0;
    } else if (clazz == int.class) {
        return 0;
    } else if (clazz == long.class) {
        return 0L;
    } else if (clazz == float.class) {
        return 0F;
    } else if (clazz == double.class) {
        return 0.0;
    } else {
        throw new IllegalArgumentException("Not a primitive type");
    }
}

Conversion from a string to a primitive type (I don't get why you're doing it, but here it is):

private static Object parseValue(Class<?> type, String s) {
    if (type == boolean.class) {
        return Boolean.valueOf(s);
    } else if (type == char.class) {
        return (char)Integer.parseInt(s);
    } else if (type == byte.class) {
        return Byte.valueOf(s);
    } else if (type == short.class) {
        return Short.valueOf(s);
    } else if (type == int.class) {
        return Integer.valueOf(s);
    } else if (type == long.class) {
        return Long.valueOf(s);
    } else if (type == float.class) {
        return Float.valueOf(s);
    } else if (type == double.class) {
        return Double.valueOf(s);
    } else {
        throw new IllegalArgumentException("Not a primitive type");
    }
}

Upvotes: 1

Related Questions