blocks java constructor field

blocks - protected field java



¿Por qué mis campos se inicializan como nulos o con el valor predeterminado de cero cuando los he declarado e inicializado en el constructor de mi clase? (4)

Hay dos partes para usar variables en java / c / c ++. Una es declarar la variable y la otra es usar la variable (ya sea asignando un valor o usándolo en un cálculo).

Cuando declaras una variable debes declarar su tipo. Entonces usarías

int x; // to declare the variable x = 7; // to set its value

No tiene que volver a declarar una variable al usarla:

int x; int x = 7;

si la variable está en el mismo alcance, obtendrá un error de compilación; sin embargo, como está descubriendo, si la variable está en un ámbito diferente, enmascarará la primera declaración.

Se supone que esta es una pregunta y respuesta canónica para preguntas similares donde el problema es el resultado de la sombra .

He definido dos campos en mi clase, uno de tipo de referencia y otro de tipo primitivo. En el constructor de la clase, trato de inicializarlos a algunos valores personalizados.

Cuando más tarde consulto los valores de esos campos, vuelven con los valores predeterminados de Java para ellos, null para el tipo de referencia y 0 para el tipo primitivo. ¿Por qué está pasando esto?

Aquí hay un ejemplo reproducible:

public class Sample { public static void main(String[] args) throws Exception { StringArray array = new StringArray(); System.out.println(array.getCapacity()); // prints 0 System.out.println(array.getElements()); // prints null } } class StringArray { private String[] elements; private int capacity; public StringArray() { int capacity = 10; String[] elements; elements = new String[capacity]; } public int getCapacity() { return capacity; } public String[] getElements() { return elements; } }

Esperaba que getCapacity() devolviera el valor 10 y getElements() para devolver una instancia de matriz inicializada correctamente.


Las entidades (paquetes, tipos, métodos, variables, etc.) definidas en un programa Java tienen names . Estos se utilizan para referirse a esas entidades en otras partes de un programa.

El lenguaje Java define un scope para cada nombre

El alcance de una declaración es la región del programa dentro de la cual se puede hacer referencia a la entidad declarada por la declaración utilizando un nombre simple, siempre que sea visible (§6.4.1).

En otras palabras, el alcance es un concepto de tiempo de compilación que determina dónde se puede usar un nombre para referirse a alguna entidad del programa.

El programa que ha publicado tiene múltiples declaraciones. Empecemos con

private String[] elements; private int capacity;

Estas son declaraciones de field , también llamadas variables de instancia , es decir. Un tipo de miembro declarado en un cuerpo de clase . Los estados de la especificación del lenguaje Java

El alcance de una declaración de un miembro m declarado o heredado por una clase tipo C (§8.1.6) es todo el cuerpo de C , incluidas las declaraciones de tipo anidadas.

Esto significa que puede usar los elements nombres y la capacity dentro del cuerpo de StringArray para referirse a esos campos.

Las dos primeras declaraciones en tu cuerpo de constructor

public StringArray() { int capacity = 10; String[] elements; elements = new String[capacity]; }

son en realidad declaraciones de declaración de variables locales

Una declaración de declaración de variable local declara uno o más nombres de variables locales.

Esas dos declaraciones introducen dos nuevos nombres en su programa. Sucede que esos nombres son los mismos que los de sus campos ''. En su ejemplo, la declaración de capacity variable local también contiene un inicializador que inicializa esa variable local , no el campo del mismo nombre. Su campo llamado capacity se inicializa al valor predeterminado para su tipo, es decir. el valor 0 .

El caso de los elements es un poco diferente. La declaración de declaración de variable local introduce un nuevo nombre, pero ¿qué pasa con la expresión de asignación ?

elements = new String[capacity];

¿A qué entidad se refieren los elements ?

Las reglas del estado del alcance

El alcance de una declaración de variable local en un bloque (§14.4) es el resto del bloque en el que aparece la declaración, comenzando con su propio inicializador e incluyendo otros declaradores a la derecha en la declaración de declaración de variable local.

El bloque, en este caso, es el cuerpo del constructor. Pero el cuerpo del constructor es parte del cuerpo de StringArray , lo que significa que los nombres de campo también están dentro del alcance. Entonces, ¿cómo determina Java a qué se refiere?

Java introduce el concepto de scope para desambiguar.

Algunas declaraciones pueden estar sombreadas en parte de su alcance por otra declaración del mismo nombre, en cuyo caso no se puede usar un nombre simple para referirse a la entidad declarada.

(un nombre simple es un identificador único, por ejemplo, elements ).

La documentación también establece

Una declaración d de una variable local o parámetro de excepción llamado n sombras , en todo el alcance de d , (a) las declaraciones de cualquier otro campo llamado n que esté dentro del alcance en el punto donde ocurre d , y (b) las declaraciones de cualquier otras variables llamadas n que están dentro del alcance en el punto donde ocurre d pero no se declaran en la clase más interna en la que se declara d .

Esto significa que la variable local denominada elements tiene prioridad sobre el campo denominado elements . La expresion

elements = new String[capacity];

por lo tanto, inicializa la variable local, no el campo. El campo se inicializa al valor predeterminado para su tipo, es decir. el valor null

Dentro de sus métodos getCapacity y getElements , los nombres que usa en sus respectivas declaraciones de return refieren a los campos ya que sus declaraciones son las únicas en alcance en ese punto particular del programa. Como los campos se inicializaron a 0 y eran null , esos son los valores devueltos.

La solución es deshacerse por completo de las declaraciones de variables locales y, por lo tanto, hacer que los nombres se refieran a las variables de instancia, como originalmente quería. Por ejemplo

public StringArray() { capacity = 10; elements = new String[capacity]; }

Sombreado con parámetros de constructor

Similar a la situación descrita anteriormente, puede tener parámetros formales (constructor o método) sombreando campos con el mismo nombre. Por ejemplo

public StringArray(int capacity) { capacity = 10; }

Estado de reglas de sombreado

Una declaración d de un campo o parámetro formal llamado n sombras, en todo el alcance de d , las declaraciones de cualquier otra variable llamada n que esté en el alcance en el punto donde se produce d .

En el ejemplo anterior, la declaración de la capacity parámetro constructor sombrea la declaración de la variable de instancia también denominada capacity . Por lo tanto, es imposible hacer referencia a la variable de instancia con su nombre simple. En tales casos, debemos referirnos a él con su nombre calificado .

Un nombre calificado consiste en un nombre, un "." token y un identificador.

En este caso, podemos usar la expresión primaria this como parte de una expresión de acceso de campo para referirnos a la variable de instancia. Por ejemplo

public StringArray(int capacity) { this.capacity = 10; // to initialize the field with the value 10 // or this.capacity = capacity; // to initialize the field with the value of the constructor argument }

Hay reglas de sombreado para cada tipo de variable , método y tipo.

Mi recomendación es que use nombres únicos siempre que sea posible para evitar el comportamiento por completo.


Otra convención ampliamente aceptada es agregar algún prefijo (o sufijo, lo que prefiera) a los miembros de la clase para distinguirlos de las variables locales.

Por ejemplo, miembros de la clase con prefijo m_ :

class StringArray { private String[] m_elements; private int m_capacity; public StringArray(int capacity) { m_capacity = capacity; m_elements = new String[capacity]; } public int getCapacity() { return m_capacity; } public String[] getElements() { return m_elements; } }


La mayoría de los IDE ya tienen soporte disponible para esta notación, a continuación es para Eclipse


int capacity = 10; en su constructor está declarando una capacity variable local que sombrea el campo de la clase.

El remedio es soltar el int :

capacity = 10;

Esto cambiará el valor del campo. Lo mismo para el otro campo en la clase.

¿No te advirtió tu IDE de esta sombra?