vazio - sobrecarga de construtores java




Por que meus campos foram inicializados para null ou para o valor padrão zero quando os declarei e inicializei no construtor da minha classe? (3)

Entidades (pacotes, tipos, métodos, variáveis, etc.) definidas em um programa Java possuem names . Estes são usados ​​para se referir àquelas entidades em outras partes de um programa.

A linguagem Java define um scope para cada nome

O escopo de uma declaração é a região do programa dentro da qual a entidade declarada pela declaração pode ser referida usando um nome simples, desde que seja visível (§6.4.1).

Em outras palavras, o escopo é um conceito de tempo de compilação que determina onde um nome pode ser usado para se referir a alguma entidade do programa.

O programa que você postou tem várias declarações. Vamos começar com

private String[] elements;
private int capacity;

Estas são declarações de field , também chamadas de variáveis ​​de instância , ou seja. um tipo de membro declarado em um corpo de classe . Os estados da especificação da linguagem Java

O escopo de uma declaração de um membro m declarado ou herdado por um tipo de classe C (§8.1.6) é o corpo inteiro de C , incluindo quaisquer declarações de tipo aninhadas.

Isso significa que você pode usar os elements nomes e a capacity no corpo de StringArray para se referir a esses campos.

As duas primeiras declarações no corpo do construtor

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

são, na verdade , declarações de declaração de variável local

Uma declaração de declaração de variável local declara um ou mais nomes de variáveis ​​locais.

Essas duas instruções introduzem dois novos nomes em seu programa. Acontece que esses nomes são os mesmos que os seus campos. No seu exemplo, a declaração de variável local para capacity também contém um inicializador que inicializa essa variável local , não o campo com o mesmo nome. Seu campo chamado capacity é inicializado com o valor padrão para seu tipo, isto é. o valor 0 .

O caso dos elements é um pouco diferente. A declaração de declaração de variável local introduz um novo nome, mas e a expressão de atribuição ?

elements = new String[capacity];

Qual entidade são elements referentes a?

As regras do estado do escopo

O escopo de uma declaração de variável local em um bloco (§14.4) é o resto do bloco no qual a declaração aparece, começando com seu próprio inicializador e incluindo quaisquer outros declaradores à direita na declaração de declaração de variável local.

O bloco, neste caso, é o corpo do construtor. Mas o corpo do construtor é parte do corpo de StringArray , o que significa que os nomes de campo também estão no escopo. Então, como o Java determina o que você está se referindo?

Java introduz o conceito de scope para desambiguar.

Algumas declarações podem ser sombreadas em parte de seu escopo por outra declaração com o mesmo nome, caso em que um nome simples não pode ser usado para se referir à entidade declarada.

(um nome simples é um identificador único, por exemplo, elements .)

A documentação também afirma

Uma declaração d de uma variável local ou parâmetro de exceção chamado n sombras , em todo o escopo de d , (a) as declarações de quaisquer outros campos nomeados n que estão no escopo no ponto onde d ocorre , e (b) as declarações de qualquer outras variáveis ​​denominadas n que estão no escopo no ponto em que d ocorre, mas não são declaradas na classe mais interna em que d é declarado.

Isso significa que a variável local denominada elements tem prioridade sobre o campo denominado elements . A expressão

elements = new String[capacity];

Portanto, está inicializando a variável local, não o campo. O campo é inicializado com o valor padrão para seu tipo, isto é. o valor null .

Dentro de seus métodos getCapacity e getElements , os nomes que você usa em suas respectivas instruções de return referem-se aos campos, pois suas declarações são as únicas no escopo naquele ponto específico do programa. Como os campos foram inicializados para 0 e null , esses são os valores retornados.

A solução é livrar-se das declarações de variáveis ​​locais e, portanto, fazer com que os nomes se refiram às variáveis ​​da instância, como você queria originalmente. Por exemplo

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

Sombreamento com parâmetros de construtor

Semelhante à situação descrita acima, você pode ter parâmetros formais (construtor ou método) sombreando campos com o mesmo nome. Por exemplo

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

Estado das regras de sombreamento

Uma declaração d de um campo ou parâmetro formal chamado n sombras, em todo o escopo de d , as declarações de quaisquer outras variáveis ​​nomeadas n que estão no escopo no ponto onde d ocorre.

No exemplo acima, a declaração da capacity parâmetro do construtor faz sombra à declaração da variável da instância também denominada capacity . Portanto, é impossível referir-se à variável de instância com seu nome simples. Em tais casos, precisamos nos referir a ele com seu nome qualificado .

Um nome qualificado consiste em um nome, um "." token e um identificador.

Nesse caso, podemos usar a expressão primária como parte de uma expressão de acesso de campo para se referir à variável de instância. Por exemplo

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
}

Existem regras de sombreamento para cada tipo de variável , método e tipo.

Minha recomendação é que você use nomes exclusivos sempre que possível para evitar o comportamento completamente.

Isso deve ser uma pergunta canônica e uma resposta para perguntas semelhantes em que o problema é resultado de sombreamento .

Eu defini dois campos na minha classe, um de um tipo de referência e um de um tipo primitivo. No construtor da classe, tento inicializá-los para alguns valores personalizados.

Quando eu mais tarde consultar os valores desses campos, eles retornam com os valores padrão do Java para eles, null para o tipo de referência e 0 para o tipo primitivo. Por que isso está acontecendo?

Aqui está um exemplo reproduzível:

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;
    }
}

Eu esperava que getCapacity() retornasse o valor 10 e getElements() para retornar uma instância de array adequadamente inicializada.


Existem duas partes para usar variáveis ​​em java / c / c ++. Uma é declarar a variável e a outra é usar a variável (seja atribuindo um valor ou usando-o em um cálculo).

Quando você declara uma variável, você deve declarar seu tipo. Então você usaria

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

Você não precisa declarar novamente uma variável ao usá-la:

int x;
int x = 7;   

se a variável estiver no mesmo escopo, você receberá um erro do compilador; no entanto, como você está descobrindo, se a variável estiver em um escopo diferente, você irá mascarar a primeira declaração.


int capacity = 10; em seu construtor está declarando uma capacity variável local que sombras o campo da classe.

O remédio é abandonar o int :

capacity = 10;

Isso irá alterar o valor do campo. Idem para o outro campo da turma.

O seu IDE não avisou você desse sombreamento?