【问题标题】:Java how can I with reflection check if a field is initialized or is default value?Java如何通过反射检查字段是否已初始化或默认值?
【发布时间】:2011-08-28 19:51:16
【问题描述】:

所以,应该是直截了当的问题。

假设我有一个包含很多字段的类,例如:

String thizz;
long that;
boolean bar;

如何通过反射查看thizzthatbar 字段是否已初始化或保留为它们的默认值null、0 和false?

【问题讨论】:

  • 为什么需要反思?你不能检查一下吗?
  • 因为不知道另外一个对象有多少个和什么字段,所以变化很大...
  • 请注意Boolean的默认值为null。也许你想到了boolean
  • 等等,你问的是如何动态查找对象的字段和值?这是一个不同的问题。

标签: java reflection


【解决方案1】:

您只有 7 种原始类型和一种要检查的引用类型。如果将所有 Number 类型分组在一起,则只有四个值需要检查。

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
}  

【讨论】:

  • @Rahul 这就是代码中间的检查的用途,是的。
  • char 怎么样? (Character 不是Number
  • @DodgyCodeException a char 是一个无符号的 16 位值。例如初始化之前可以写char ch = 48; or Character ch = 48; or char ch = '0'; ch /= 0.9;0 or '\0'
  • @PeterLawrey 是的,但即便如此,您仍然会在else if(t.isPrimitive() && ((Number) v).doubleValue() == 0) 中获得 ClassCastException,因为 v 属于 Character 类,不能转换为 Number。
  • @DodgyCodeException 感谢您的指正。我已经添加了这个案例。
【解决方案2】:

Peter Lawrey's answer 对我来说很好,除了原始数据类型 char 的字段,这会在我的代码中引发以下异常:

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

所以我添加了char 案例:

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'\u0000',根据Java Documentation on primitive data types,默认为char

【讨论】:

  • 这很有帮助。谢谢!
【解决方案3】:

你不需要反思……

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

但是,它们可能已被初始化,然后重置为其默认值。如果您真的想知道它们是否已被初始化,您可以执行以下操作:

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

/编辑 要从一个类中获取所有字段,请查看Class.getDeclaredFields()。这将提供每个字段,而不仅仅是公共字段。

从这里您可以检查字段的类型并获取其值:

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
    }
}

【讨论】:

  • 啊,让我重申一下我的问题,我实际上并不关心该值是否已初始化并稍后设置为 null 或 0,我只需要检查所有 Javas 可能类型的默认值,以及原语作为 java.lang 对象
  • @AntonioP 在这种情况下,这很容易——只有大约 7 个(我可能错了一两个)不同的“默认值”。每个基元一个,非基元一个。一个简单的重载方法可以轻松执行检查:bool IsDefault(bool b)bool IsDefault(long i) 等(某些类型可以加倍,如int/longfloat/double),根据需要修改反射。
  • @Jeffrey,是的,这就是我所做的,除了我曾经检查 Number、Boolean 和 String 的实例,其余的我认为无论如何都是假的......
  • @AntonioP 如果你已经这样做了,问题出在哪里?
  • 在我提出问题后我就这样做了,因为我认为我可以尝试它并且它有效,但仍然可能有一种更聪明的方法:) 我认为会有一种方法询问一个对象你是 null false 还是 0 而不对其不同类型的值执行 instanceof
【解决方案4】:

您可以通过反射创建一个新实例(使用默认值自动初始化)并将 yourObject 与此默认实例进行比较。

//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:这个解决方案还解决了 Christophe Weis 提到的 char 问题。

【讨论】:

  • 这个解决方案应该有更多的赞成票。如果您创建没有静态类型的默认对象,您甚至不必依赖Foo。这样做:Object defaultObject = yourObject.getClass().newInstance();(如果你得到所有的构造函数并创建一个参数最少的构造函数,这会更精细)。
  • 这将为没有默认构造函数的类抛出 java.lang.NoSuchMethodException,例如 Integer。
【解决方案5】:

这应该会让你朝着正确的方向前进。

    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");
      }
    }

【讨论】:

    【解决方案6】:

    它的要点是:

    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.
    }
    

    现在,这只允许您读取公共字段。

    或者,如果您的对象遵循 getX/setX 命名约定,您可以使用 getMethods(),并查找名为“getXxx”和“setXxx”的方法,以推断可设置字段的存在——并调用这些 getter 来查找为预期的默认值。

    【讨论】:

    • 请注意getFields() 只返回公共字段。
    • getDeclaredFields() 为您提供所有字段。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-06-01
    • 1970-01-01
    • 1970-01-01
    • 2014-08-19
    • 1970-01-01
    相关资源
    最近更新 更多