Recientemente me encontré con una construcción Java que nunca había visto antes y me preguntaba si debería usarla. Parece que se llama bloques inicializadores .
public class Test {
public Test() { /* first constructor */ }
public Test(String s) { /* second constructor */ }
// Non-static initializer block - copied into every constructor:
{
doStuff();
}
}
El bloque de código se copiará en cada constructor, es decir, si tiene un constructor múltiple, no tiene que volver a escribir el código.
Sin embargo, veo tres inconvenientes principales con esta sintaxis:
- Es uno de los pocos casos en Java donde el orden de su código es importante, ya que puede definir múltiples bloques de código y se ejecutarán en el orden en que se escriben. Esto me parece dañino, ya que simplemente cambiar el orden de los bloques de código cambiará el código.
- Realmente no veo ningún beneficio al usarlo. En la mayoría de los casos, los constructores se llamarán entre sí con algunos valores predefinidos. Incluso si este no es el caso, el código podría simplemente ser puesto en un método privado y llamado desde cada constructor.
- Reduce la legibilidad, ya que podría poner el bloque al final de la clase y el constructor normalmente está al comienzo de la clase. Es bastante contra-intuitivo mirar una parte completamente diferente de un archivo de código si no espera que sea necesario.
Si mis afirmaciones anteriores son ciertas, ¿por qué (y cuándo) se introdujo este constructo de lenguaje? ¿Hay algún caso de uso legítimo?
{ doStuff(); }
nivel de clase es un bloque inicializador.doStuff()
Respuestas:
Hay dos casos en los que uso bloques inicializadores.
El primero es para inicializar miembros finales. En Java, puede inicializar un miembro final ya sea en línea con la declaración, o puede inicializarlo en el constructor. En un método, está prohibido asignar a un miembro final.
Esto es valido:
Esto también es válido:
Esto no es válido:
Si tiene varios constructores, y si no puede inicializar un miembro final en línea (porque la lógica de inicialización es demasiado compleja), o si los constructores no pueden llamarse a sí mismos, puede copiar / pegar el código de inicialización, o puede usar Un bloque inicializador.
El otro caso de uso que tengo para los bloques de inicialización es para construir pequeñas estructuras de datos auxiliares. Declaro un miembro y pongo valores justo después de sus declaraciones en su propio bloque inicializador.
fuente
squareVal = val * val
lo tanto, se quejarán de acceder a valores no inicializados. Los bloques de inicialización no pueden depender de ningún argumento pasado al constructor. La solución habitual que he visto para ese tipo de problema es definir un único constructor "base" con la lógica compleja, y definir todos los demás constructores en términos de ese. La mayoría de los usos de los inicializadores de instancia, de hecho, se pueden reemplazar con ese patrón.En general, no use bloques de inicializador no estáticos (y tal vez evite también los estáticos).
Sintaxis confusa
Mirando esta pregunta, hay 3 respuestas, pero engañaste a 4 personas con esta sintaxis. ¡Fui uno de ellos y llevo 16 años escribiendo Java! Claramente, la sintaxis es potencialmente propensa a errores. Me mantendría alejado de eso.
Constructores telescópicos
Para cosas realmente simples, puede usar constructores "telescópicos" para evitar esta confusión:
Patrón de constructor
Si necesita hacerStuff () al final de cada constructor u otra inicialización sofisticada, quizás un patrón de construcción sería lo mejor. Josh Bloch enumera varias razones por las cuales los constructores son una buena idea. Los constructores toman un poco de tiempo para escribir, pero si están escritos correctamente, son un placer usarlos.
Bucles de inicializador estático
Solía usar mucho los inicializadores estáticos , pero ocasionalmente me topé con bucles donde 2 clases dependían de los bloques de inicializadores estáticos que se llamaban antes de que la clase pudiera cargarse por completo. Esto produjo un "error al cargar la clase" o un mensaje de error similarmente vago. Tuve que comparar archivos con la última versión de trabajo conocida en control de código fuente para descubrir cuál era el problema. No es divertido en absoluto.
Inicialización perezosa
Tal vez los inicializadores estáticos son buenos por razones de rendimiento cuando funcionan y no son demasiado confusos. Pero en general, prefiero la inicialización diferida a los inicializadores estáticos en estos días. Está claro lo que hacen, aún no me he encontrado con un error de carga de clase con ellos, y funcionan en más situaciones de inicialización que los bloques de inicializador.
Definición de datos
En lugar de la inicialización estática para construir estructuras de datos (compárese con los ejemplos en las otras respuestas), ahora uso las funciones auxiliares de definición de datos inmutables de Paguro :
Conculsion
Al principio de Java, los bloques de inicialización eran la única forma de hacer algunas cosas, pero ahora son confusos, propensos a errores y, en la mayoría de los casos, han sido reemplazados por mejores alternativas (detalladas anteriormente). Es interesante saber acerca de los bloques de inicializador en caso de que los vea en el código heredado, o que salgan en una prueba, pero si estuviera haciendo una revisión del código y vi uno en el nuevo código, le pediría que justifique por qué ninguno de los las alternativas anteriores eran adecuadas antes de darle el visto bueno a su código.
fuente
Además de la inicialización de una variable de instancia que se declara como
final
(ver la respuesta de barjak ), también mencionaría elstatic
bloque de inicialización.Puede usarlos como una especie de "constructor estático".
De esa manera, puede hacer inicializaciones complejas en una variable estática la primera vez que se hace referencia a la clase.
Aquí hay un ejemplo inspirado en el de barjak:
fuente
Por lo que respecta a los bloques de inicializador no estáticos, su función básica es actuar como un constructor predeterminado en clases anónimas. Ese es básicamente su único derecho a existir.
fuente
Estoy totalmente de acuerdo con las afirmaciones 1, 2, 3. Tampoco uso nunca inicializadores de bloque por estos motivos y no sé por qué existe en Java.
Sin embargo, me veo obligado a usar el inicializador de bloque estático en un caso: cuando tengo que instanciar un campo estático cuyo constructor puede lanzar una excepción marcada.
Pero en cambio tienes que hacer:
Este lenguaje me parece muy feo (también evita que lo marques
context
comofinal
), pero esta es la única forma admitida por Java para inicializar dichos campos.fuente
context = null;
en su bloque catch, es posible que pueda declarar el contexto como final.The final field context may already have been assigned
static { JAXBContext tempCtx = null; try { tempCtx = JAXBContext.newInstance(Foo.class); } catch (JAXBException ignored) { ; } context = tempCtx; }