Solucionado (ver solução)
Solucionado
(ver solução)
4
respostas

Referência a um objeto

Estou com dúvida em conceito de orientação a objetos. Já estudei Java e orientação a objetos, mas não lembro como funciona a instanciação de um objeto. Quando estou instanciando um objeto em Swift, a constante que recebe a referência a este objeto não pode ser alterada depois de ser inicializada, pois é uma constante, mas os seus atributos podem ser alterados posteriormente, pois foram declarados como variáveis. É isso?

let brownie = Meal()
brownie.happiness = 3
brownie.name = "chicken"

No caso acima então, a constante brownie recebe a referência a um objeto? É isso? Um objeto pode conter variáveis, que são atributos? Poderia me esclarecer melhor?

4 respostas

Olá Erica. Não conheço de Swift, mas vamos ver se consigo lhe ajudar. Em Java, um objeto é criado/instanciado através da palavra reservada "new". Logo, assim temos um novo objeto criado em memória:

Meal var1 = new Meal();

O código declara uma variável do tipo "Meal" chamada "var1", cria um novo objeto na memória do tipo "Meal" e atribui a referência deste objeto na variável "var".

Se você quiser, é possível alterar a variável para que ela receba a referência de outro objeto. Conforme abaixo:

Meal var1 = new Meal();
Meal var2 = new Meal();
var2 = var1; //Agora, "var2" também referencia para o mesmo objeto da variável "var1"

Já o código abaixo define uma "constante", onde uma vez inicializada, não pode mais ser alterada.

final Meal var2 = new Meal();

O mesmo exemplo acima, por exemplo, não é possível:

Meal var1 = new Meal();
final Meal var2 = new Meal();
var2 = var1; //Erro de compilação!

Contudo, sendo constante ou não, ambas variáveis podem alterar as propriedades do objeto. Por exemplo:

final Meal var1 = new Meal();

var1.happiness = 3;
var1.name = "chicken";

Isso acontece, pois o modificador "final" apenas define que a variável "var1" é uma constante, mas não as propriedades do objeto.

Se você quiser que as propriedades de Meal sejam constantes, teria que na definição da classe utilizar o modificador também em suas propriedades. Algo como:

class Meal {
    final int happiness = 3;
    final String name = "chicken";
}

Desta maneira, o código não pode mais alterar essas propriedades

Meal var1 = new Meal();
var1.happiness = 4; //Erro de compilação!

final Meal var2 = new Meal();
var2.name = "pig"; //Erro de compilação

Não sei se consegui responder a sua dúvida.

Sim, entendi o que você quer dizer. Mas a minha dúvida é mais de conceito de orientação a objetos. Ainda não consigo entender direito a diferença entre variáveis primitivas e variáveis de referência. Sei que a variável de referência recebe o endereço de memória de um objeto. Mas e se essa variável for um parâmetro dentro de um método? Essa variável some depois que o método é utilizado? E como fica o objeto? Outra dúvida que eu tenho é se um novo objeto é criado, e a variável que recebe a referência a esse objeto for alterada, como conseguirei ter acesso ao objeto?

solução!

Bom, vamos lá. Na verdade, ambos tipos de variáveis que você comentou recebem endereços de memória. Quando você declara uma variável, o programa solicita ao Sistema Operacional que seja reservado um espaço na memória cujo tamanho vai depender do seu tipo, e em troca o SO devolve o endereço que corresponde a esse espaço. Quando a variável é inicializada/atribuída, aquele espaço na memória reservada pelo SO é preenchido com o valor correspondente.

Por exemplo. Vamos supor que o seu programa tem a linha abaixo:

int x;

Ao localizar essa linha, o SO vai procurar um espaço livre na memória equivalente a 4 bytes, ou 32 bits se preferir, e se encontrar vai devolver o endereço que corresponda a esse espaço. Vamos supor que o SO devolveu o número "1000" que identifique esse espaço. Logo, esse espaço está reservado para o seu programa, especificamente para a variável nomeada como "x". Quando essa variável receber um valor, o seu programa simplesmente vai solicitar ao SO, através do endereço "1000" para que aloque o conteúdo recebido.

Quando estamos falando de objeto, é tudo igual ao que citei acima. Ou seja, quando você declara algo como:

Meal y;

O programa vai solicitar ao SO um espaço de memória equivalente a 4 bytes (ou 8, dependendo da plataforma) e este irá devolver o endereço e assim por diante. O que muda, é que neste espaço de 4 bytes (ou 8 bytes), em vez de ser atribuído um "objeto" (cujo tamanho em bytes pode variar muito, dependendo de quantos membros ele tenha etc.), é atribuído o endereço no qual esse objeto existe.

Para exemplificar, veja no exemplo do "int". Suponha que exista a linha de código abaixo:

int x = 1;

Quando essa linha for executada, lá no endereço 1000 do nosso exemplo, vamos encontrar o valor abaixo (número 1 em binário, ocupando o espaço de 32 bits ou 4 bytes):

00000000000000000000000000000001

Já quando a linha abaixo é executada, em vez de ser o objeto em si, é armazenado o endereço onde esse objeto existe.

Meal y = new Meal();

Lá no endereço da variável "y", vamos encontrar algo como:

00000000000000000000000001010011

Que é o endereço onde o seu programa deve solicitar ao SO para que seja encontrado o objeto propriamente dito.

Portanto, quando você cria um objeto com o uso do "new" e o atribui a uma variável, na verdade esse objeto está sendo criado em um endereço qualquer que o SO encontrar na hora para ele, e este endereço é armazenado em sua variável.

Quando você passa um argumento para um método, na linguagem Java, você está passando portanto uma "cópia" do seu conteúdo.

Veja o exemplo abaixo:

public static void main(String[] args) {
  int x = 1;
  System.out.println(x); //Vai imprimir 1
  soma(x);
  System.out.println(x); //Vai imprimir 1 de novo
}

static void soma(int y) {
 y = y + 1;
}

Veja que vai imprimir 1 duas vezes. Isso porque o Java vai enviar, como eu falei, uma cópia do conteúdo de "x" para o método "soma". Portanto, a variável x declarada lá no main não vai sofrer alteração, pois apenas foi enviada uma cópia do seu conteúdo.

Veja esse outro exemplo:

public static void main(String[] args) {
  Meal x = new Meal();
  x.name = "a";
  System.out.println(x.name); //Vai imprimir "a"
  altera(x);
  System.out.println(x.name); //Vai imprimir "b"
}

static void altera(Meal y) {
 y.name = "b";
}

Veja que a propriedade "name" foi alterada, mas se a variável apontando para um objeto funciona da mesma forma, então porque houve essa alteração? É porque a variável nesse caso recebe um endereço de onde o objeto está?

Portanto, a variável "y" do método altera recebe uma cópia do conteúdo da variável "x", ou seja, recebe uma cópia do endereço de onde o objeto do tipo Meal foi criado anteriormente. Logo, ambas as variáveis podem alterar o mesmo objeto, pois ambas possuem o mesmo endereço. Agora, veja o último exemplo abaixo:

public static void main(String[] args) {
  Meal x = new Meal();
  x.name = "a";
  System.out.println(x.name); //Vai imprimir "a"
  altera(x);
  System.out.println(x.name); //Vai imprimir "a" de novo!
}

static void altera(Meal y) {
 y = new Meal()
 y.name = "b";
}

Veja que nesse caso, a variável "y" teve uma nova atribuição, recebendo nesse caso um novo endereço de um outro objeto do tipo Meal. Como eu falei, o Java passa "cópias" das variáveis como argumento, portanto, a nova atribuição dentro do método não altera a variável x.

Quando o método "altera" terminar de ser executado, a variável "y" é descartada, pois o escopo dela se encerra. Assim como a variável "x" é descartada quando o método main se encerra. E os dois objetos criados no exemplo anterior serão removidos pelo Garbage Collector quando ele decidir que ambos estão elegíveis para serem removidos.

Bom, isso tudo é bem complexo mesmo Erica, então não se sinta um peixe fora d'água =)

Entendi agora, muito mais claro! Obrigada!