2010-01-27 8 views
21

Necesito asegurarme de que ningún atributo de objeto sea nulo y agregue el valor predeterminado en caso de que sea nulo. ¿Hay alguna manera fácil de hacer esto, o tengo que hacerlo manualmente comprobando cada atributo por sus getters y setters?¿Es posible en Java comprobar si los campos de los objetos son nulos y luego agregar valor predeterminado a todos esos atributos?

+0

La respuesta breve es que tienes que hacerlo manualmente, aquí hay una pregunta similar: http://stackoverflow.com/questions/997482/does-java-support-default-parameter-values ​​ – Upgradingdave

+1

¿Puedes proporcionar un poco más de contexto? a tu problema Hay algunas maneras en que podrías hacer esto. Podría usar AOP para envolver sus captadores y devolver los valores predeterminados si devuelven null. O simplemente podría escribir algún código para envolver sus objetos en un proxy dinámico que esencialmente hace lo mismo. Con algo más de información, podemos elegir una opción adecuada. –

Respuesta

38

Puede usar la reflexión para iterar sobre el campo del objeto y configurarlos. Obviamente, necesitarías algún tipo de mapeo entre los tipos o incluso los nombres de los campos y los valores predeterminados necesarios, pero esto se puede hacer con bastante facilidad en un ciclo. Por ejemplo:

for (Field f : obj.getClass().getFields()) { 
    f.setAccessible(true); 
    if (f.get(obj) == null) { 
    f.set(obj, getDefaultValueForType(f.getType())); 
    } 
} 

[Actualización]

Con Java moderna, puede utilizar las anotaciones para establecer los valores predeterminados de los campos en una base por clase. Una implementación completa podría tener este aspecto:

// DefaultString.java: 
import java.lang.annotation.Retention; 
import java.lang.annotation.RetentionPolicy; 

@Retention(RetentionPolicy.RUNTIME) 
public @interface DefaultString { 
    String value(); 
} 

// DefaultInteger.java: 
import java.lang.annotation.Retention; 
import java.lang.annotation.RetentionPolicy; 

@Retention(RetentionPolicy.RUNTIME) 
public @interface DefaultInteger { 
    int value(); 
} 

// DefaultPojo.java: 
import java.lang.annotation.Annotation; 
import java.lang.reflect.Field; 

public class DefaultPojo { 

    public void setDefaults() { 
     for (Field f : getClass().getFields()) { 
      f.setAccessible(true); 
      try { 
       if (f.get(this) == null) { 
        f.set(this, getDefaultValueFromAnnotation(f.getAnnotations())); 
       } 
      } catch (IllegalAccessException e) { // shouldn't happen because I used setAccessible 
      } 
     } 
    } 

    private Object getDefaultValueFromAnnotation(Annotation[] annotations) { 
     for (Annotation a : annotations) { 
      if (a instanceof DefaultString) 
       return ((DefaultString)a).value(); 
      if (a instanceof DefaultInteger) 
       return ((DefaultInteger)a).value(); 
     } 
     return null; 
    } 

} 

// Test Pojo 
public class TestPojo extends DefaultPojo { 
    @DefaultString("Hello world!") 
    public String stringValue; 
    @DefaultInteger(42); 
    public int integerValue; 
} 

valores por defecto para una continuación TestPojo se pueden establecer sólo con la ejecución test.setDetaults()

+6

Sugiero usar getDeclaredFields en lugar de getFields, funcionó para mí, porque declaró campos incluye campos privados. –

+1

Eso es verdad. Normalmente, esperaría que este comportamiento se utilizara solo para objetos de datos (POJO), por lo que 'getFields()' debería ser suficiente e incluso probablemente sea el "Cosa Correcta", pero si los requisitos requieren también inicializar campos privados, entonces 'getDeclaredFields()' debe ser utilizado. – Guss

9

Es necesario filtrar manualmente la entrada a constructores y definidores. Bueno ... podrías usar el reflejo, pero yo no lo aconsejaría. Parte del trabajo de los constructores y los instaladores es validar la entrada. Esto puede incluir cosas como:

public void setPrice(double price) { 
    if (price < 0.0d) { 
    throw new IllegalArgumentException("price cannot be negative " + price); 
    } 
    this.price = price; 
} 

y

public void setName(String name) { 
    if (name == null) { 
    throw new NullPointerException("name cannot be null"); 
    } 
    this.name = name; 
} 

Usted podría utilizar funciones de contenedor para la verificación real y lanzando la excepción.

+0

Sí, solo especifique los valores predeterminados al introducir las variables y/o en el constructor y luego en cada guardia setter contra pasar en nulo. –

+4

No arrojaré una 'NullPointerException' en caso de argumento' null': no da ninguna pista. Lanzaré 'IllegalArgumentException' en todos los casos y dejaré que la JVM arroje' NullPointerException' en sí misma cuando encuentre uno. No conviene usar la reflexión. –

+0

Ver http://stackoverflow.com/questions/3881/illegalargumentexception-or-nullpointerexception-for-a-null-parameter para una discusión sobre esto. Personalmente, prefiero usar 'InvalidArgumentException' en ambos casos. –

0

Puede crear una función que devuelva un valor booleano y verifique cada atributo. Puede llamar a esa función para hacer el trabajo por usted.

Como alternativa, puede inicializar el objeto con valores predeterminados. De esa forma no hay necesidad de que haga ninguna verificación.

5

Tal vez marque Hibernate Validator 4.0, la Implementación de referencia del JSR 303: Bean Validation.

Este es un ejemplo de una clase anotada:

public class Address { 

    @NotNull 
    private String line1; 
    private String line2; 
    private String zip; 
    private String state; 

    @Length(max = 20) 
    @NotNull 
    private String country; 

    @Range(min = -2, max = 50, message = "Floor out of range") 
    public int floor; 

     ... 
} 

Para una introducción, ver Getting started with JSR 303 (Bean Validation) – part 1 y part 2 o la sección "Introducción" de la guía de referencia que es parte de la distribución Hibernate Validator.

0

No tengo suficiente contexto para darle una respuesta correcta, pero le sugiero que haga código inmutable tanto como sea posible. Use los campos public final. No más getters o setters: cada campo tiene que estar definido por constructor. Su código es más corto, más legible y le impide escribir código con efectos secundarios.

No obstante, no le permite pasar argumentos nulos a su constructor ... Todavía puede verificar cada argumento como lo sugiere @cletus, pero le sugiero que lance IllegalArgumentException en lugar de NullPointerException que no da ninguna nueva pista sobre lo que has hecho.

De todos modos, eso es lo que hago tanto como puedo y mejoró mi código (legibilidad, estabilidad) en gran medida. Todos en mi equipo lo hacen y estamos muy contentos con eso. Aprendimos eso cuando tratamos de escribir un código erlang donde todo es inmutable.

Espero que esto ayude.

0

Intenté esto y funciona sin problemas para validar si el campo está vacío. he respondido a su pregunta en parte ya que no he probado personalmente para agregar valores por defecto para los atributos

if(field.getText()!= null && !field.getText().isEmpty()) 

creo que sirve

0

Esto no es para comprobar nula, en vez esto sea útil en la conversión de una objeto existente a un objeto vacío (objeto nuevo). No sé si esto es relevante o no, pero tenía ese requisito.

@SuppressWarnings({ "unchecked" }) 
static void emptyObject(Object obj) 
{ 
    Class c1 = obj.getClass(); 
    Field[] fields = c1.getDeclaredFields(); 

    for(Field field : fields) 
    { 
     try 
     { 
      if(field.getType().getCanonicalName() == "boolean") 
      { 
       field.set(obj, false); 
      } 
      else if(field.getType().getCanonicalName() == "char") 
      { 
       field.set(obj, '\u0000'); 
      } 
      else if((field.getType().isPrimitive())) 
      { 
       field.set(obj, 0); 
      } 
      else 
      { 
       field.set(obj, null); 
      } 
     } 
     catch(Exception ex) 
     { 

     } 
    } 
} 
3

solución no reflectante para Java 8, sin necesidad de utilizar una serie de Si de, serían para transmitir todos los campos y comprobar si hay nullness:

return Stream.of(id, name).allMatch(Objects::isNull); 

Este sigue siendo bastante fácil de mantener, evitando el reflejo martillo. Esto devolverá verdadero para atributos nulos.

+0

¿está optimizado para funcionar tan eficientemente como una serie de 'si's durante el tiempo de ejecución? –

+0

@WoodrowBarlow No. ¿Por qué debería ser? – shmosel

+0

@shmosel porque esto (al menos la forma en que se usa aquí) podría ser preprocesado en una serie de 'si's sin cambiar sus efectos. no sé si eso es verdad en el caso general, sin embargo. –

Cuestiones relacionadas