3

I'd like to perform a safety check for my getClass().getField(...).set(...) where the value I am setting should match the type of that field, (int x = 1 should only allow Integers to be set). The problem is, I'm having a hard time finding ways to compare the two. Currently this is the code:

int foo = 14;

Field field = getClass().getDeclaredField("foo");
Object source = this;

// make the field accessible...

public void safeSet(Object newValue) throws IllegalAccessException {
    // compare the field.getType() to the newValue type
    field.set(source, newValue);
}

I've tried a lot of things, and searched around the web for quite a bit, but cannot find an answer which solely focuses on this usage of it. I've tried things like field.getType().getClass().equals(newValue.getClass()), field.getType().equals(newValue), and more, and they do not work. How can I reasonably compare a primitive field.getType() to an Object value passed in, or, how would I, in this case, compare an int, to an Integer?

Frontear
  • 1,150
  • 12
  • 25
  • The association of the primitive types to their wrapper classes is indeed missing in java. One remark: either `getClass().getField("...")` or `Xxx.class.getDeclaredField("...")` to deal with child classes. – Joop Eggen Jan 02 '19 at 07:47
  • `if(field.getType().isPrimitive() && field.get(source).getClass() != newValue.getClass()) /* error */`. Works since primitive values can never be `null`, hence, there’s always an old value whose wrapped representation must have the same type as the wrapped new value. When you don’t want to read the field, check out [how to get the default value for a type](https://stackoverflow.com/a/52989209/2711488)… – Holger Jan 08 '19 at 09:19

2 Answers2

3

Your friend is Class.isAssignableFrom()

Because you want to assign a value to a field, this is the built-in solution to do that.

if (getClass().getDeclaredField("foo").getType().isAssignableFrom(newValue.getClass())) {
    ....
}

It works for primitive types, too.

gaborsch
  • 15,408
  • 6
  • 37
  • 48
  • Attempting this with my values results in an assert of false for the check. Same values, Object is '2', assert fails – Frontear Jan 02 '19 at 16:37
2

Step 1 : Check field.isPrimitive(). If it returns true then it's a primitive type. and proceed to step 3.

Step 2: If it's not primitive then you can directly check field.getType() == newValue.getClass() and then set the value

Step 3: if it's primitive then you need to have a static map

public final static Map<Class<?>, Class<?>> map = new HashMap<Class<?>, Class<?>>();
static {
    map.put(boolean.class, Boolean.class);
    map.put(byte.class, Byte.class);
    map.put(short.class, Short.class);
    map.put(char.class, Character.class);
    map.put(int.class, Integer.class);
    map.put(long.class, Long.class);
    map.put(float.class, Float.class);
    map.put(double.class, Double.class);
}

Class<?> clazz = map.get(field.getType());
then check clazz == newValue.getClass() and then set the variable.
Sagar Gandhi
  • 925
  • 6
  • 20
  • 1
    Fine solution but for non-primitive types inheritance means to check for `field.getType().isAssignableFrom(value.getClass())`. – Joop Eggen Jan 02 '19 at 07:44
  • In Step 2, `==` does not work if the assigned value is a subtype of the original, or the declared type is an interface. Use [Class.isAssignableFrom](https://docs.oracle.com/javase/8/docs/api/java/lang/Class.html#isAssignableFrom-java.lang.Class-) instead, it is a general solution. – gaborsch Jan 02 '19 at 08:05
  • @gaborsch I agree with you when you need subclasses to be included. For Strict class checking e.g. here he wants only primitive data types to be changed. == will work. – Sagar Gandhi Jan 02 '19 at 09:07
  • I wish this wasn't the solution I had to go with, but sadly, I can't find another generic way that works. Perhaps in the future Java can implement something which helps with this? – Frontear Jan 02 '19 at 17:12