Java e Ponteiros

23 respostas
L

Probleminha, galera!

Suponham que eu tenho essas 3 classes:

(Código 1)

public class Pacote implements Serializable{
   
   static final String serialVersionId = "blablabla1";
   
   int id;
   List<Item> itens;
   //... gets e sets ...
}

public class Item implements Serializable{
   
   static final String serialVersionId = "blablabla2";
   
   int id;
   Pacote pacote;
   List<ItemHistorico> historico;
   //... gets e sets ...
}

public class ItemHistorico implements Serializable{
   
   static final String serialVersionId = "blablabla3";
   
   int id;
   String nome;
   Item item;
   //... gets e sets ...
}

A seguir apresento este código:
(Código 2)

public static void montarItensHistoricos(List<Item> itens){
   
   Item item;
   ItemHistorico itemHistorico;
   
   for(int i = 0; i < itens.size(); i++){
      
      item = itens.get(i);
      
      itemHistorico = new ItemHistorico();
      itemHistorico.setNome("exemplo " + i);
      itemHistorico.setItem(item);
      
      item.setHistorico(new ArrayList<ItemHistorico>());
      item.getHistorico().add(itemHistorico);
   }
}

O que eu espero como resultado final deste código é o seguinte, após uma iteração de 2 itens:

No entanto o que eu obtenho é este resultado:

A causa disso eu mesmo posso lhes dizer.

(Como Java utiliza ponteiros implicitamente, buscarei abordar o problema orientando o caso ao uso de ponteiros)

Ao realizar o Código2, supondo a iteração daqueles 2 itens, eu tenho a lista de itens, e tenho um ponteiro “item”, que apontará para cada item durante cada uma das repetições, certo? Então, na primeira repetição, o ponteiro “item” está apontando para o primeiro item da lista “itens”, e em dado momento realiza esta operação: “itemHistorico.setItem(item);”. Ao fazer isto, estamos dizendo ao Java que o ponteiro pra Item que há dentro do objeto “itemHistórico” vai apontar para onde também aponta o ponteiro “item” (é confuso, mas pra quem tá acostumado com ponteiros, é só ler umas 3 vezes que dá pra entender :roll: ).

Na segunda repetição da iteração no Código 2, o ponteiro “item” vai apontar para o elemento seguinte na lista “itens”. Quando o ponteiro “item” passar a apontar para este elemento, aquele objeto “itemHistorico” também terá seu apontador de item apontando pra lá, pois a atribuição de objetos em Java é feita por referência.

Alguém sabe como eu posso fazer para obter o primeiro resultado mencionado?

Uma nota importante é o seguinte: eu não quero criar um novo objeto item dentro de itemHistorico para copiá-lo por valor. O que eu quero é referenciar os itens da lista “itens”, e que cada itemHistorico esteja referenciando seu item correspondente na iteração.

23 Respostas

ViniGodoy

Ok, essa foi uma pergunta completamente C++ no forum de java… :lol:

Todo objeto em java é passado por referência, sem exceção. Os tipos primitivos são os únicos que recebem cópias por valor.

Seu código está me parecendo correto. Você tem certeza que o conteúdo da sua lista de itens está correto quando você chama o método?

Andre.flu

O problema esta na logica que vc esta usando.

A cada iteração vc esta trocando a lista e não adicionando novas informações. Isto ocorre quando vc faz:

item.setHistorico(new ArrayList<ItemHistorico>());

Esperimenta trocar por:

if ( item.getHistorico() == null ){
    item.setHistorico(new ArrayList<ItemHistorico>());
}

Ou seja se o item já tem histórico apenas serão adicionadas informações.

ViniGodoy

Andre.flu:
O problema esta na logica que vc esta usando.

A cada iteração vc esta trocando a lista e não adicionando novas informações. Isto ocorre quando vc faz:

item.setHistorico(new ArrayList<ItemHistorico>());

Experimenta trocar por:

if ( item.getHistorico() == null ){
    item.setHistorico(new ArrayList<ItemHistorico>());
}

Ou seja se o item já tem histórico apenas serão adicionadas informações.

Não vejo onde isso possa resolver o problema… E se ele quiser sempre redefinir a lista? Até porque, a cada iteração ele faz isso para um item diferente… Certamente, não é isso que está causando o comportamento descrito pelo colega…

L

Nem todos os objetos são passados por referência. A exemplo, tente passar um objeto de String, Integer, Double, ou qualquer objeto de alguma classe em java.lang. Eu não sei ao certo o nome que se dá para essa passagem, mas se você passa um objeto desses para dentro de um método qualquer, e modifica um valor num desses objetos, essa modificação não reflete no objeto que está fora do método.

Tenho certeza sim, debuguei direitinho e infelizmente a lista está correta, antes estivesse errada. :frowning:

davidbuzatto

Bem, não existe passagem por referência em Java.

Mesmo que uma variável de referência seja uma referência a um objeto, é seu VALOR que é a referência, sendo assim não há passagem por referência e sim por valor.

Exemplo: Você consegue incrementar a referência (o valor da mesma) de um objeto Java como em C++ (onde a referência pode ser obtida de qualquer tipo)?

Não sou expert no assunto e é um pouco difícil conseguir explicar isso, mas é mais ou menos o que eu falei.

Até mais!

L

ViniGodoy, aquele item.setHistorico(new ArrayList()); está certo ali daquele jeito, sim. Pra cada item que ele percorre, eu quero que ele crie uma lista nova de historico, e quero que ele adicione apenas um objeto nessa lista, que será o objeto itemHistorico que ele está montando durante a iteração.

ViniGodoy

Logan-san:

Nem todos os objetos são passados por referência. A exemplo, tente passar um objeto de String, Integer, Double, ou qualquer objeto de alguma classe em java.lang. Eu não sei ao certo o nome que se dá para essa passagem, mas se você passa um objeto desses para dentro de um método qualquer, e modifica um valor num desses objetos, essa modificação não reflete no objeto que está fora do método.

Você se engana. Esses objetos são passados por referência sim. Mas são imutáveis. Entenda o seguinte, embora todos os objetos sejam passados por referência no Java, o ponteiro em si é passado por valor.

Andre.flu

ViniGodoy

Como posso ter um histórico em que as informações anteriores são apagandas.

Com aquela verificação ele tem o resultado esperado.

davidbuzatto

Isso acontece porque essas classes geram objetos que são imutáveis, ou seja, mesmo que você tenha a referência aos mesmos, o objeto o qual a refernecia “aponta” nunca é alterado. Se for forçado a “mudanca” do um objeto desses tipos, é criado um novo objeto com o valor novo e a nova referência é utilizada.

ViniGodoy

davidbuzatto:

Bem, não existe passagem por referência em Java.

Mesmo que uma variável de referência seja uma referência a um objeto, é seu VALOR que é a referência, sendo assim não há passagem por referência e sim por valor.

Exemplo: Você consegue incrementar a referência (o valor da mesma) de um objeto Java como em C++ (onde a referência pode ser obtida de qualquer tipo)?

Não sou expert no assunto e é um pouco difícil conseguir explicar isso, mas é mais ou menos o que eu falei.

Até mais!

Essa realmente é uma outra forma de se entender. Tudo em java é passado por valor. Mas todos os objetos são referênciados por ponteiros, que também serão passados por valor. Portanto, você até pode alterar o valor de um objeto num método, mas não o valor do ponteiro que o referencia. Alterações desse tipo estarão restritas ao método, e não mais alterarão o ponteiro original.

davidbuzatto

Isso que eu falei :smiley:

ViniGodoy

Sim, estava concordando com você. :slight_smile:
Na verdade, o complicado é tentar explicar java com vocabulário C++.

O ideal é fazer o seguinte:

Java não é C++.
Procure não falar em ponteiros ou referências. Quando você chama uma função, você passa um objeto inteiro para essa função.

A variável que referencia esse objeto é copiada e não poderá ser alterada.

ViniGodoy

Você pode colocar aí as classes Item e ItemHistorico em anexo para a gente dar uma olhada? Teu código está parecendo certinho, o erro deve estar em outro lugar!

L

Entendi o seu ponto, ViniGodoy.

Mas voltando… o problema não se trata de quantos elementos a lista de ItemHistorico tenha. Eu quero que aquele método CRIE uma lista nova, independente de, e quero que adicione apenas 1 elemento nela, que no caso é o itemHistorico que ele monta em cada repetição da iteração.

O problema é que pra todos os itemHistorico que ele montar e jogar numa lista de historico dentro do item, ele vai referenciar o último item da lista de itens que está sendo iterada, e esse é o resultado que eu NÃO desejo. O que eu desejo é que ele relacione cada itemHistorico de cada repetição ao item DAQUELA repetição.

O problema ocorre porque o objeto “item” fica apontando pra cada elemento da lista “itens”, e conforme a iteração corre, o lugar pra onde ele tá apontando muda, e com isso todos os itemHistorico que são criados ficam apontando pro mesmo lugar que o objeto “item” aponta, e no fim da iteração o “item” aponta pro último elemento da lista “itens”, e, conseqüentemente, então, todos os “itemHistorico” criados vão referenciar o último “item” na lista “itens” (está muito confuso, eu sei). Esse é o problema que eu quero evitar, percebem?

L

Tá na primeira mensagem! :stuck_out_tongue:

Andre.flu

Logan-san

Deixa eu enteder melhor o que quer:

vc quer que a cada iteração do metodo montarItensHistoricos
vc adicione um novo item de histórico a uma lista de varios itens?

Se for isso o erro esta na implemantação.
Vc não deve guardar a lista de histórico dentro do item pois só é necessário se vc necessita da lista de histórico do item.

Esta lista de histórico que guardaria todos os históricos de todos os itens deve estar na classe Pacote.

E assim ao adicionar um itemHistorico na lista de itens vc deverá adicionar o mesmo item na lista do Pacote.

ViniGodoy

Isso que você está descrevendo não pode acontecer em Java.

Veja bem, isso aconteceria se a variável item fosse um ponteiro, e o método setItem recebesse o endereço desse ponteiro.

Mas em java, isso não ocorre. Cada vez que você chama o set, o endereço do item iterado será copiado e o objeto de histórico receberá esse endereço.

Alterar o valor de item posteriormente não deverá alterar o valor que está dentro de itemHistorico.

Se isso está ocorrendo, o problema não está aí. Mas em alguma outra classe, que pode estar usando uma variável estática.

L

Andre.flu

Não, está correto. Eu vou precisar que o Item tenha uma lista de itens de histórico, sim, pois cada item poderá ser alterado e eu quero registrar essas alterações.

O pacote serve apenas para agrupar itens, não se trata de um problema de modelo.

É um problema de lógica, eu não estou sabendo manipular a iteração de maneira correta, para obter o resultado que eu desejo.

ViniGodoy

Sua lógica parece correta.

Novamente, tem como postar o código das classes Item e ItemHistorico?

L

ViniGodoy:
Isso que você está descrevendo não pode acontecer em Java.

Veja bem, isso aconteceria se a variável item fosse um ponteiro, e o método setItem recebesse o endereço desse ponteiro.

Mas em java, isso não ocorre. Cada vez que você chama o set, o endereço do item iterado será copiado e o objeto de histórico receberá esse endereço.

Alterar o valor de item posteriormente não deverá alterar o valor que está dentro de itemHistorico.

Se isso está ocorrendo, o problema não está aí. Mas em alguma outra classe, que pode estar usando uma variável estática.

Implicitamente, em Java, tudo é ponteiro (menos int, double, etc).

Então quando eu faço item = itens.get(i);, eu to falando, implicitamente, que eu quero que item aponte para o elemento i na lista de itens.

Na primeira iteração o código transcrito ficaria item = itens.get(0);, ele estaria apontando pro elemento 0. Na segunda iteração seria item = itens.get(1);, e estaria apontando pro elemento 1, e assim por diante.

Quando eu faço itemHistorico.setItem(item), eu estou dizendo pra ele acoplar o ponteiro “item”, ou seja: para onde item estiver apontando, o itemHistorico.getItem() irá me retornar o mesmo resultado que “item” retornaria.

Eu não sei se o fato de eu estar abordando ponteiros explicitamente nas minhas palavras está confundindo vocês, mas em java quase tudo é ponteiro, de maneira implícita. Você só não tem acesso de manipulação a eles diretamente.

L

Vini, essas classes são fictícias pra representar o meu problema, as inventei só pra ilustrar o problema que o tópico propõe. Mas as classes em si apenas têm métodos gets e sets gerados autmaticamente pelo Eclipse.

L

Gente… vida de programador é uma absurdo!

A implementação FICTÍCIA que eu criei aqui estava CORRETA, e retornaria o resultado que eu desejo. :frowning:

O problema estava na minha implementação REAL, com a qual estou trabalhando. Quis criar uma representação dela só pra mostrar a vocês de forma mais simples para que pudessem entender o problema.

O erro na minha implementação era porque eu estava fazendo aquele new ItemHistorico() FORA da iteração. Aí não rola, mesmo. Toda vez ele ia modificar os valores do mesmo itemHistorico. :?

Fim das contas: acabei falando um punhado de abobrinha aqui, onde todo mundo se confundiu. Peço desculpas e agradeço o empenho de vocês em tentar me ajudar. :expressionless: Vai ver eu só preciso de um livro de AUTO-ajuda. :?

Obrigado a todos.

Abraços!

ViniGodoy

Hahahahaah… acontece nas melhores famílias.

Criado 4 de abril de 2007
Ultima resposta 4 de abr. de 2007
Respostas 23
Participantes 4