descargar - java jdk
El inicializador recursivo funciona cuando agrego "esto"? (3)
En el primer caso, el compilador intenta evaluar la expresión ''x + 42'' pero falla porque x no está inicializado.
En el segundo caso, la expresión ''this.x + 42'' se evalúa en tiempo de ejecución (debido a ''this'' palabra clave), cuando x ya está inicializado y tiene valor 0.
Esto no se puede compilar (con un error de illegal forward reference
), como uno esperaría:
class test {
int x = x + 42;
}
Pero esto funciona:
class test {
int x = this.x + 42;
}
¿Que esta pasando? ¿Qué se asigna en este último caso?
Es muy difícil descubrir y prohibir todos los accesos a x
durante la inicialización de x. Por ejemplo
int x = that().x; | int x = getX();
|
Test that(){ return this; } | int getX(){ return x; }
La especificación se detiene en "acceso por nombre simple" y no intenta ser más exhaustivo.
En otra sección, "Asignación Definitiva", la especificación hace algo similar. Por ejemplo
public class Test
{
static final int y;
static final int z = y; // fail, y is not definitely assigned
static{ y = 1; }
}
public class Test
{
static final int y;
static final int z = Test.y; // pass... because it''s not a simple name
static{ y = 1; }
}
Curiosamente, "Asignación Definitiva" menciona específicamente que this.x
es equivalente a x
(o, para un campo, el nombre simple del campo calificado por esto)
esta cláusula también podría agregarse a la sección citada por NPE.
- el uso es a través de un nombre simple (o un nombre simple calificado por esto)
Pero al final, es imposible en el tiempo de compilación analizar todos los usos / accesos posibles a un campo.
Resumen: ambos inicializadores acceden a un campo que aún no se ha inicializado (y, por lo tanto, todavía tiene el valor predeterminado de cero). Como es probable que esto sea un error de programación, el lenguaje prohíbe algunas formas simples de dicho acceso. Sin embargo, no prohíbe la forma más compleja.
El comportamiento es compatible con el JLS, específicamente §8.3.2.3. Restricciones en el uso de campos durante la inicialización
La declaración de un miembro debe aparecer textualmente antes de que se use solo si el miembro es un campo de instancia (respectivamente
static
) de una clase o interfazC
y se cumplen todas las condiciones siguientes:
El uso se produce en un inicializador de variable de instancia (respectivamente
static
) de C o en un inicializador de instancia (respectivamentestatic
) de C.El uso no está en el lado izquierdo de una tarea.
El uso es a través de un nombre simple.
C
es la clase o interfaz más interna que encierra el uso.
El primer ejemplo satisface las cuatro condiciones y, por lo tanto, no es válido. El segundo ejemplo no satisface la tercera condición ( this.x
no es un nombre simple) y, por lo tanto, está bien.
La secuencia general de eventos es la siguiente:
- Cuando se crea una instancia de una clase, todos los campos se inicializan a los valores predeterminados de su tipo.
- Los inicializadores se ejecutan en orden textual (de arriba hacia abajo).
Por lo tanto, si un inicializador se refiere a un campo que aparece más adelante en la definición de la clase (o al campo mismo), vería el valor predeterminado de ese otro campo. Es probable que esto sea un error de programación y, por lo tanto, está explícitamente prohibido por §8.3.2.3.
Si elude §8.3.2.3, por ejemplo, usando this.
para reenviar-hacer referencia a un campo, verá el valor predeterminado (cero para int
). Por lo tanto, lo siguiente está bien definido y garantiza establecer x
a 42
:
class test {
int x = this.x + 42;
}