2009-02-13 17 views
5

Recientemente comencé un nuevo proyecto y estoy tratando de mantener mis variables de instancia siempre inicializadas con algún valor, por lo que ninguna de ellas es en ningún momento nula. Pequeño siguiente ejemplo:¿Debo mantener las variables de instancia en Java siempre inicializadas o no?

public class ItemManager { 

    ItemMaster itemMaster; 
    List<ItemComponentManager> components; 

    ItemManager() { 
    itemMaster = new ItemMaster(); 
    components = new ArrayList<ItemComponentManager>(); 
    } 

    ... 
} 

El punto es principalmente para evitar la comprobación tedioso para nula antes de utilizar una variable de instancia en algún lugar del código. Hasta ahora, está funcionando bien y en su mayoría no necesita valor nulo, ya que también puede verificar si hay cadenas vacías o listas vacías, etc. No estoy usando este método para las variables de ámbito de método ya que su alcance es muy limitado y entonces no afecta otras partes del código.

Todo esto es un poco experimental, por lo que me gustaría saber si este enfoque podría funcionar o si hay algunos escollos que todavía no veo. ¿Es generalmente una buena idea mantener las variables de instancia inicializadas?

+0

Nunca crees una ArrayList vacía solo para desecharla más tarde. Siempre use uno de los métodos en la clase Colecciones para obtener una colección vacía e inmutable. –

+0

Gracias por las respuestas hasta el momento, obtuve una valiosa contribución. – MicSim

Respuesta

9

que suelo tratar una colección vacía y una colección nulo como dos cosas separadas:

una colección vacía implica que sé que hay cero elementos disponibles. Una colección nula me dirá que no sé el estado de la colección, que es algo diferente.

Así que realmente no creo que sea una u otra cosa. Y declararía la variable final si los inicializo en el constructor. Si declara que es definitiva, queda claro para el lector que esta colección no puede ser nula.

0

¿Qué ocurre si en una de sus métodos de configurar

itemMaster = null; 

o se vuelve una referencia a la ItemManager a alguna otra clase y establece itemMaster como nulo. (Usted puede evitar que esto le devuelva fácilmente un clon de su ItemManager, etc.)

Me quedaré con los cheques ya que esto es posible.

+0

Esa configuración nula es lo que estoy evitando por diseño (como regla no escrita): ningún método llamará nunca "itemMaster = null" – MicSim

+0

Y se puede forzar el restablecimiento de la referencia (para clases mutables) utilizando final. – gimpf

+0

Configuración de código externo su referencia a itemMaster no tendrá ningún efecto. Simplemente eliminará la referencia local al objeto. No borrará la referencia interna del objeto ni destruirá el objeto. – Clayton

1

Sí, es una muy buena idea inicializar todas las variables de clase en el constructor.

0

Si es todo su código y desea establecer esa convención, debería ser algo bueno tener. Estoy de acuerdo con el comentario de Paul, sin embargo, que nada impide que un código erróneo accidentalmente establezca una de las variables de clase en nulo. Como regla general, siempre compruebo null. Sí, es un PITA, pero la codificación defensiva puede ser algo bueno.

3

Diría que está totalmente bien, siempre y cuando recuerde que tiene valores de marcador de posición "vacíos" allí y no datos reales.

Mantenerlos nulos tiene la ventaja de obligarlo a manejarlos; de lo contrario, el programa se bloquea. Si crea objetos vacíos, pero los olvida, obtiene resultados indefinidos.

Y solo para comentar sobre la codificación defencive - Si usted es el que está creando los objetos y nunca los establece nulos, no hay necesidad de buscar nulos cada vez. Si por algún motivo obtiene un valor nulo, entonces sabrá que algo ha fallado catastróficamente y que el programa se bloqueará de todos modos.

+0

En segundo lugar, la opinión crítica sobre la codificación defensiva. Con demasiada frecuencia, "defensivo" solo oculta errores de lógica, ya menudo he visto que la defensiva se ha ido mal. Mucho menos a menudo tuve problemas con una actitud de falla rápida. – gimpf

+0

"defensivo" no debe ocultar ningún error lógico con informes de errores adecuados. Si comprueba nulo e informa un error si se encuentra nulo, entonces está a la defensiva y puede detectar errores lógicos de forma rápida y fácil sin matar potencialmente una aplicación de producción. –

1

El punto es principalmente para evitar la tediosa comprobación de nula antes de usar una variable de clase en algún lugar del código .

Aún debe comprobar la nulidad. Las bibliotecas de terceros e incluso la API de Java algunas veces devolverán null.

Además, crear instancias de un objeto que nunca se puede utilizar es un desperdicio, pero eso dependería del diseño de su clase.

2

Yo los haría final si es posible. Luego deben inicializarse en el constructor y no pueden ser nulos.

También debe hacerlos privados en cualquier caso, para evitar que otras clases les asignen nulos. Si puede verificar que null nunca se asigna en su clase, entonces el enfoque funcionará.

+0

final !? Esto hace que sea difícil hacer una serialización adhoc (de). Persistir a un DB se vuelve difícil. Extender la clase se convierte en un dolor también. – Pat

+0

@Pat YAGNI (no lo necesitarás). Las subclases no pueden acceder al estado privado de una superclase de todos modos, y eso es algo bueno. – eljenso

2

He encontrado algunos casos donde esto causa problemas. Durante la deserialización, algunos frameworks no llamarán al constructor, no sé cómo o por qué eligen hacerlo, pero sucede. Esto puede hacer que sus valores sean null. También he encontrado el caso donde se llama al constructor, pero por alguna razón las variables miembro no se inicializan.

En realidad me gustaría usar el siguiente código en lugar de la suya:

public class ItemManager { 

    ItemMaster itemMaster = new ItemMaster(); 
    List<ItemComponentManager> components = new ArrayList<ItemComponentManager>(); 

    ItemManager() { 
    ... 
    } 
    ... 
} 
1

Un objeto debe ser de 100% listo para su uso después de que se construye. Los usuarios no deberían tener que comprobar nulos. La programación defensiva es el camino a seguir, mantenga los controles.

En aras de DRY, puede colocar los controles en el setters y simplemente hacer que el constructor los llame. De esta forma, no codifica los cheques dos veces.

+0

tenga cuidado al invocar setters desde un constructor: si una subclase anula a los setters, puede terminar llamando al código de la subclase antes de que la subclase se haya inicializado. –

+0

Pero si estoy en el constructor de la clase A, y llama a sus propios setters, ¿cómo importa una subclase B que los invalide? "esto" apunta a una instancia de A, así que estaría llamando A setters en su constructor, no B. ¿Verdadero? – duffymo

+0

@duffymo 'this' en un constructor siempre se refiere al objeto que se está construyendo. El tipo estático de 'this' es la clase en la que aparece. El tipo de tiempo de ejecución puede ser la misma clase o cualquier subclase de esa clase. – eljenso

4

En primer lugar, todos los variables de instancia no finales deben ser declarados privada si se quiere mantener el control!

Considere instanciación perezoso, así - esto también evita "mal estado", pero sólo se inicializa en el uso:

class Foo { 
    private List<X> stuff; 
    public void add(X x) { 
     if (stuff == null) 
      stuff = new ArrayList<X>(); 
     stuff.add(x); 
    } 
    public List<X> getStuff() { 
     if (stuff == null) 
      return Collections.emptyList(); 
     return Collections.unmodifiableList(stuff); 
    } 
} 

(nótese el uso de Collections.unmodifiableList - a menos que realmente quiere una persona que llama para poder para agregar/eliminar de su lista, debe hacerlo inmutable)

Piense en cuántas instancias del objeto en cuestión se crearán. Si hay muchos, y siempre crea las listas (y podría terminar con muchas listas vacías), podría estar creando muchos más objetos de los que necesita.

Aparte de eso, es realmente una cuestión de gusto y si puede tener valores significativos cuando construye.

Si está trabajando con una DI/COI, desea que el marco para hacer el trabajo por usted (aunque se puede hacer a través de la inyección de constructor; prefiero set) - de Scott

+0

Realmente, ¿cree que todas las variables miembro deben ser privadas? Protegido, sí. Privado, no. ¿Qué hay de la subclasificación? –

+0

Las subclases podrían establecerlos como nulos. – recursive

+0

Los campos de instancia deben ser privados para una encapsulación adecuada, nunca protegidos. – eljenso

2

La forma en que ocuparme de cualquier variable que declare es decidir si cambiará a lo largo de la vida del objeto (o clase si es estático).Si la respuesta es "no", la hago definitiva.

por lo que es finales fuerzas a darle un valor cuando se crea el objeto ... personalmente me gustaría hacer lo siguiente a menos que yo sabía que iba a estar cambiando lo que el punto en:

 
    private final ItemMaster itemMaster; 
    private final List components; 

    // instance initialization block - happens at construction time 
    { 
     itemMaster = new ItemMaster(); 
     components = new ArrayList(); 
    } 

La forma su código es ahora, debe verificar nulo todo el tiempo porque no marcó las variables como privadas (lo que significa que cualquier clase en el mismo paquete puede cambiar los valores a nulo).

+0

O simplemente final privado ItemMaster itemMaster = new ItemMaster(); private final List components = new ArrayList(); –

+0

Me gusta mantener la inicialización separada de la declinación. – TofuBeer

0

Desde el nombre de la clase "ItemManager", ItemManager suena como un singleton en alguna aplicación. Si es así, debes investigar y realmente, realmente, saber Dependency Injection. Use algo como Spring (http://www.springsource.org/) para crear e insertar la lista de ItemComponentManagers en ItemManager.

Sin DI, la inicialización a mano en aplicaciones serias es una pesadilla para depurar y conectar varias clases de "gerente" para hacer pruebas limitadas es un infierno.

Use DI siempre (incluso al construir pruebas). Para los objetos de datos, cree un método get() que cree la lista si no existe. Sin embargo, si el objeto es complejo, seguramente encontrará su vida mejor utilizando el patrón Fábrica o Constructor y hará que el F/B establezca las variables miembro según sea necesario.

Cuestiones relacionadas