13

So, should be straight forward question.

Lets say I have a class with a lot of fields like:

String thizz;
long that;
boolean bar;

How can I, with reflection, see if the fields thizz, that and bar have been initialized or left to their default values of null, 0 and false?

Enigmadan
  • 3,398
  • 2
  • 23
  • 35
rapadura
  • 5,242
  • 7
  • 39
  • 57

6 Answers6

22

You have only 7 primitive types and one reference type to check. If you group all Number types together, you only have four values to check for.

Object o =
for (Field field : o.getClass().getDeclaredFields()) {
 Class t = field.getType();
 Object v = field.get(o);
 if(t == boolean.class && Boolean.FALSE.equals(v)) 
   // found default value
 else if(t == char.class && ((Character) v).charValue() == 0)
   // found default value
 else if(t.isPrimitive() && ((Number) v).doubleValue() == 0)
   // found default value
 else if(v == null)
   // found default value
}  
Peter Lawrey
  • 525,659
  • 79
  • 751
  • 1,130
8

Peter Lawrey's answer works fine for me, except for fields of primitive data type char, which raises the following exception in my code :

java.lang.ClassCastException: java.lang.Character cannot be cast to java.lang.Number

So I added the char case :

Object o =
for (Field field : o.getClass().getDeclaredFields()) {
 Class t = field.getType();
 Object v = field.get(o);
 if (boolean.class.equals(t) && Boolean.FALSE.equals(v)) 
   // found default value
 else if (char.class.equals(t) && ((Character) v) != Character.MIN_VALUE)
   // found default value
 else if (t.isPrimitive() && ((Number) v).doubleValue() == 0)
   // found default value
 else if(!t.isPrimitive() && v == null)
   // found default value
}  

Character.MIN_VALUE is '\u0000', which is the default value of char according to the Java Documentation on primitive data types.

Community
  • 1
  • 1
Christophe Weis
  • 2,518
  • 4
  • 28
  • 32
6

You can create a new instance via reflection (automatically initialized with default value) and compare yourObject with this default instance.

//default Object filled with default values:
Object defaultObject = yourObject.getClass().newInstance();
for (final Field field : yourObject.getClass().getDeclaredFields()) {
  final Object value = field.get(yourObject);
  if (value == null || value.equals(field.get(defaultObject))) {
    // found default value
  }
}

PS: This solution also solved the issue with char mentioned by Christophe Weis.

JB Cha
  • 597
  • 5
  • 9
  • 1
    This solution should have more upvotes. If you create the default object without a static type you do not even have to depend on `Foo`. Do it this way: `Object defaultObject = yourObject.getClass().newInstance();` (this can be even more refined if you get all the constructors and create one that has the least parameters). – Hash Mar 17 '17 at 09:22
  • This will throw an java.lang.NoSuchMethodException for class that does not have a default constructor, like Integer for example. – abarazal Sep 21 '20 at 18:33
6

You don't need reflection...

if (thizz == null) {
    //it's not initialized
}
if (that == 0) {
   //it's not initialized
}
if(bar == false) {
    //it's not initialized
}

However, they could have been initialized then reset to their default values. If you truly want to know if they're been initialized you could do something like this:

private boolean isFooInitialized = false;
private Foo foo;
public void setFoo(Foo foo) {
    this.foo = foo;
    isFooInitialized = foo != null;
}

/edit To get all the fields from a class, check out Class.getDeclaredFields(). This will give every field, not just the public ones.

From here you can check the type of the field and get its value:

Foo foo = ...
Field[] fooFields = foo.getClass().getDeclaredFields();
for (Field fooField : fooFields) {
    Class<?> fooFieldClass = fooField.getClass();
    if (fooFieldClass.equals(int.class)) {
        if (fooField.getInt(foo) == 0) {
            // not initialized
        }
    } else if (fooFieldClass.equals(double.class)) {
        if (fooField.getDouble(foo) == 0) {
            // not initialized
        }
    } else if (fooFieldClass.equals(boolean.class)) {
        if (fooField.getBoolean(foo) == false) {
            // not initialized
        }
    } else if (fooFieldClass.equals(float.class)) {
        if (fooField.getFloat(foo) == 0) {
            // not initialized
        }
    } else if (fooFieldClass.equals(char.class)) {
        if (fooField.getChar(foo) == 0) {
            // not initialized
        }
    } else if (fooFieldClass.equals(byte.class)) {
        if (fooField.getByte(foo) == 0) {
            // not initialized
        }
    } else if (fooFieldClass.equals(long.class)) {
        if (fooField.getLong(foo) == 0) {
            // not initialized
        }
    } else if (fooField.get(foo) == null) {
        // not initialized
    }
}
thetaiko
  • 7,816
  • 2
  • 33
  • 49
Jeffrey
  • 44,417
  • 8
  • 90
  • 141
  • Ah, let me restate my question, I actually dont care if the value has been initialized and later set to null or 0, I just need to check the default for all Javas possible types, primitives as well as java.lang objects – rapadura Aug 28 '11 at 19:59
  • @AntonioP In that case, it's easy -- there are only ~7 (I could be wrong by one or two) different "default values". One for each primitive and one for non-primitives. A simple overloaded method could easily perform the check: `bool IsDefault(bool b)`, `bool IsDefault(long i)`, etc (some types can be doubled up, like `int/long` and `float/double`), modify for reflection as/if required. –  Aug 28 '11 at 20:05
  • @Jeffrey, Yeah that was kind of what I did, except I used to check instance of Number, Boolean and String, the rest I assume are false anyway... – rapadura Aug 28 '11 at 20:12
  • @AntonioP If you already did this, where's the problem with it? – Jeffrey Aug 28 '11 at 20:13
  • I did it like this after I asked my question since I figured I could try it and it worked, but still there might be a smarter way of doing it :) I thought there would be a way to ask an object are you null false or 0 without doing instanceof on its value for different types – rapadura Aug 28 '11 at 20:32
  • @AntionoP Ahh, I don't think there's a better way of doing this. Cheers. – Jeffrey Aug 28 '11 at 20:36
  • @Jeffrey, Most of the values can be compared with 0, you don't need so many checks. See my answer. – Peter Lawrey Aug 28 '11 at 22:16
  • @Peter Lawrey I guess I was just being thorough. That would be a much better way to go about this. – Jeffrey Aug 28 '11 at 23:32
1

This should get you going in the right direction.

    Class clazz = Class.forName("your.class");
    Field[] fields = clazz.getDeclaredFields();
    for (Field field : fields) {
      String dataType = field.getType().getName();
      if (dataType.equals("java.lang.String")) {
          System.out.println("found a string");
      }
    }
Preston
  • 3,273
  • 4
  • 26
  • 35
1

The gist of it is:

Field[] fields = yourObject.getClass().getFields();
for(Field f : fields)
{
  Class<?> k = f.getType();
  // depending on k, methods like f.getInt(yourObject),
  // f.getFloat(yourObject), 
  // f.getObject(hourObject) to get each member.
}

Now, this only lets you read the public fields.

Alternatively, IF your object follows getX/setX naming conventions, you can use getMethods(), and look for methods named "getXxx" and "setXxx", to infer the existence of settable fields -- and invoke those getters to look for the expected default values.

david van brink
  • 3,604
  • 1
  • 22
  • 17