• Revista PROGRAMAR: Já está disponível a edição #53 da revista programar. Faz já o download aqui!

Nazgulled

Isto faz-me uma deep-copy ou shallow-copy?

34 mensagens neste tópico

Tenho o seguinte:

private HashMap<String, Voo> listaVoos;

public Voo getVoo(String codigo) {
if(this.listaVoos.containsKey(codigo)) {
	return this.listaVoos.get(codigo);
} else {
	return null;
}
}

E a classe Voo tem o construtor e override necessário ao clone() para que ao fazer:

Voo v1 = new Voo();

Voo v2 = v1.clone();

Seja criada uma deep-copy (o que eu pretendo) e não shallow-copy.

A minha pergunta é: O método getVoo() definido lá em cima, devolve-me uma deep ou shallow copy de um determinado Voo? Se devolver shallow, como faço para devolver deep?

0

Partilhar esta mensagem


Link para a mensagem
Partilhar noutros sites

Shallow. O get dá-te a referência do objecto que vai aceder através do parâmetro.

Para ser deep basta:

return this.listaVoos.get(codigo).clone();

0

Partilhar esta mensagem


Link para a mensagem
Partilhar noutros sites

O problema é que quando eu clico no "." para me aparecer a lista de cenas que posso escrever (não estou a usar o BlueJ :thumbsup:) não me aprece o método clone() e se eu o adicionar na mesma e compilar, dá erro e de incompatible types o que me faz pensar que aquele clone() vai devolver um Object e não um Voo como manda o meu override feito na classe Voo.

Se eu fizesse isto:

return (Voo)(((Voo)this.listaVoos.get(codigo)).clone());

Já não me dava erro ao compilar e o clone aparece-me quando escrevo o ".", mas aqueles dois casts não fazem muito sentido para mim, dai a minha dúvida inicial. Deve haver outra forma de dar volta ao problema...

0

Partilhar esta mensagem


Link para a mensagem
Partilhar noutros sites

Já te responderam mas só para acrescentar: um método get não deve devolver cópias do objecto, deve sim, devolver o objecto ou, caso se trate de uma estrutura, devolver uma nova estrutura, com os mesmos objectos e não com cópias.

Esse código nem faria uma shallow nem um deep-copy, dado que ele devolve a referência para o objecto e não uma cópia da referência para o objecto, que isso sim, é uma shallow-copy. Resumido, esse código não copia o objecto, apenas o devolve.

0

Partilhar esta mensagem


Link para a mensagem
Partilhar noutros sites

(...)dá erro e de incompatible types o que me faz pensar que aquele clone() vai devolver um Object e não um Voo como manda o meu override feito na classe Voo.

O teu override está errado, aliás, não é um override!

Para redefinires um método tens de implementar um método com a mesma assinatura, se alteras o tipo de retorno, então alteras a assinatura, logo estás a criar um método diferente.

Assim, tens dois métodos, um clone da tua classe Voo e um da classe Object, e um não é a redifinição do outro.

0

Partilhar esta mensagem


Link para a mensagem
Partilhar noutros sites

Então se ele devolve o objecto se calhar faz exactamente o que eu quero...

Tipo, o HashMap lá em cima, regista um conjunto de objectos do Tipo Voo associado a um código e está definido uma classe chamada MapaVoos. E essa classe tem um método assim:

public void addVoo(Voo voo) {
this.listaVoos.put(voo.getCodigo(), (Voo)voo.clone());
}

A classe Voo tem um conjunto de variáveis de instância onde algumas e, apenas algumas, são inicializadas nos construtores, outras dependem de certas acções. Por inicializadas refiro-me a atribuição de valores passados como parâmetros para o respectivo construtor, todas são inicializadas, as que dependem de certas acções são automaticamente inicializadas a null ou vazio.

Depois, tenho outra classe onde tem uma variável de instância do tipo MapaVoos que é onde trato de registar os voos e bla bla. E sempre que quero adicionar um novo voo faço, por exemplo:

mapaVoo.addVoo(new Voo(bla bla...));

E quando quero actualizar um voo para actualizar as tais variáveis que não foram definidas no construtor do Voo faço o seguinte:

voo = mapaVoo.getVoo(codigoVoo);

voo.defineOutrasVariaveis();

mapaVoo.addVoo(voo);

Tendo em consideração que o put() do HashMap vai substituir o valor da chave porque a chave já existe, vai praticamente substituir o voo por um actualizado.

Isto está bem então e devolver o objecto faz exactamente o que eu quero?

Não sei se me expliquei bem, mas já não ando com muita cabeça para isto...

0

Partilhar esta mensagem


Link para a mensagem
Partilhar noutros sites

Não percebi metado do que queres fazer.

Vamos por partes

public void addVoo(Voo voo) {
this.listaVoos.put(voo.getCodigo(), (Voo)voo.clone());
}

Isto só faz sentido se o que queres é colocar uma cópia do objecto voo dentro da estrutura, se o que quere é colocar o objecto então o código deveria ser apenas:

public void addVoo(Voo voo) {
this.listaVoos.put(voo.getCodigo(), voo);
}

Depois, isto

voo = mapaVoo.getVoo(codigoVoo);
voo.defineOutrasVariaveis();
mapaVoo.addVoo(voo);

é a mesma coisa que simplesmente

voo = mapaVoo.getVoo(codigoVoo);
voo.defineOutrasVariaveis();

Não precisas voltar a colocar o objecto dentro da estrutura porque nunca o removeste te lá, apenas pediste o objecto, alteraste o objecto.

Não percebo porque estás a criar cópias de objectos, ou porque é que estás a criar cópias de referencias, isso ainda me faz mais sentido.

O que é que pretendes exactamente?

0

Partilhar esta mensagem


Link para a mensagem
Partilhar noutros sites

Pois, o problema é que, pelo menos no meu tempo, o profs diziam que "dentro" da cadeira de POO, nunca havia partilhas de referências (embora dissessem que havia situações em que isso poderia não fazer sentido)...

Por esse motivo, talvez deva usar o clone na introdução do valor no HashMap, e também deverá fazer o clone no getVoo, razão pela qual, precisará de voltar a inserir o voo depois de lhe alterar as variáveis.

0

Partilhar esta mensagem


Link para a mensagem
Partilhar noutros sites

Estou a criar cópias de objectos porque isso é uma das obrigações do prof. Vou-te dar um exemplo de uma classe (relativamente a esta parte não na totalidade) daquilo que o prof quer que a gente faça para qualquer classe:

public class Teste {
int var1;
String var2;

public Teste(Teste t) {
	this.var1 = t.getVar1();
	this.var2 = t.getVar2();
}

public int getVar1() {
	return this.var1;
}

public String getVar2() {
	return this.var2;
}

public Teste clone() {
	return new Teste(this);
}
}

E depois, em todo o lado, devemos usar o clone e não trabalhar com a referência do objecto. Se isto é bom ou mau, neste momento não me interessa, é uma regra do prof/cadeira e eu tenho de a seguir...

Quanto ao problema em si, o método getVoo() terá de ser o meu, com clone(), pelo que a minha dúvida persiste:

voo = mapaVoo.getVoo(codigoVoo);
voo.defineOutrasVariaveis();
mapaVoo.addVoo(voo);

Isto está correcto?

0

Partilhar esta mensagem


Link para a mensagem
Partilhar noutros sites

(...)e também deverá fazer o clone no getVoo, razão pela qual, precisará de voltar a inserir o voo depois de lhe alterar as variáveis.

Pois, mas como disse, a única maneira que consegui fazer clone() no getVoo foi assim:

(Voo)(((Voo)this.listaVoos.get(codigo)).clone());

Mas isto parece-me um bocado estúpido...

0

Partilhar esta mensagem


Link para a mensagem
Partilhar noutros sites

Parece-me que tens aí um casting a mais... Devia bastar (Voo)(this.listaVoos.get(codigo).clone());

E acredita que há castings mais estúpidos...

0

Partilhar esta mensagem


Link para a mensagem
Partilhar noutros sites

Mas lá está, eu estou a usar o JCreator e isto tem um tipo de IntelliSense, eu quando clico no "." ele mostra aquilo que eu posso fazer e até agora sempre funcionou bem, quando eu escrevo:

this.listaVoos.get(codigo)

E depois ".", o clone() não me aparece e se eu adiciona-lo na mesma, ele vai devolver um Object. Tudo bem, no teu código estás a fazer um casting, mas tendo em consideração que o get() do HashMap vai devolver o valor (do tipo Voo)  correspondente a chave "codigo", chamar o clone() não deveria invocar o método clone codificado por mim que existe na classe Voo?

0

Partilhar esta mensagem


Link para a mensagem
Partilhar noutros sites

Pois, o problema é que, pelo menos no meu tempo, o profs diziam que "dentro" da cadeira de POO, nunca havia partilhas de referências (embora dissessem que havia situações em que isso poderia não fazer sentido)...

Eu gostava de ver é onde é que existe uma situação onde faz sentido copiares referências... a própria noção é tão estranha que nem sei o que dizer... realmente que raio de Java vos estão a ensinar, agora percebo alguns dos problemas que aqui apareceram.

Estou a criar cópias de objectos porque isso é uma das obrigações do prof. Vou-te dar um exemplo de uma classe (relativamente a esta parte não na totalidade) daquilo que o prof quer que a gente faça para qualquer classe:

A classe teste, é uma classe normal que respeita o encapsulamento, até aí nada de especial.

Quanto ao problema em si, o método getVoo() terá de ser o meu, com clone(), pelo que a minha dúvida persiste:

voo = mapaVoo.getVoo(codigoVoo);
voo.defineOutrasVariaveis();
mapaVoo.addVoo(voo);

Isto está correcto?

Dentro do que me disseste, e se percebi bem as obrigações a que estás sujeito, sim, esse código estará correcto. Pelo menos irá funcionar, no sentido em que te dá uma referência para um objecto, te permite alterar os seus valores e te permite colocar o objecto de volta na estrutura.

Pois, mas como disse, a única maneira que consegui fazer clone() no getVoo foi assim:

(Voo)(((Voo)this.listaVoos.get(codigo)).clone());

Mas isto parece-me um bocado estúpido...

E é :thumbsup:, verifica se tens bem os casts.

Sinceramente não sei como ajudar neste caso, muito do código que aqui apresentaram não faz grande sentido,  e essa de copiar as referências ainda me parece estranha...

0

Partilhar esta mensagem


Link para a mensagem
Partilhar noutros sites

Se estas a dizer que o que fiz está bem, porque me estas a dizer para verificar bem os casts?

Mas ninguém está a copiar as referências, eu não quero copiar as referências, mas sim uma cópia do objecto e para isso, tenho de usar o clone implementado por mim, só que o método getVoo() não estou a conseguir aplicalo ao get() do HashMap, é só esse o meu problema...

0

Partilhar esta mensagem


Link para a mensagem
Partilhar noutros sites

O que tens de usar é o método clone redefinido por ti, e para isso tens de redefinir o método correcto, que tem de devolver um Object. Os casts fazem-se depois. Não implementas um método da API alterando a assinatura.

Agora tive de olhar bem para a classe que tinhas implementado, o método clone na class teste está mal implementado. Correctamente tem de devolver um Object.

O que te disse, foi que o código apresentado irá funcionar, tirando algum bug que tenhas, e como não estou a ver todo o código, apenas estou a ver alguns pedaços, é complicado detectar todos os erros.

Quanto aos casts, parece-me que tens casts a mais.

E desculpa mas ainda me custa perceber o que estás a fazer. Isto porque o que me parece é que estás a colocar um objecto na hash, cada vez que o vais buscar crias uma cópia desse objecto, alteras essa cópia e colocas a cópia dentro da hash novamente para substituir o objecto que lá tinhas colocado inicialmente. É isto? É que se for isto, não faz sentido nenhum, além de que, não estás a trabalhar como estruturas sincronizadas, isso é, mais um, poço de problemas.

Se queres que o clone funcione correctamente, implementa-o correctamente, e isso significa não criar um método novo mas sim redefinir o que já existe.

0

Partilhar esta mensagem


Link para a mensagem
Partilhar noutros sites
Isto porque o que me parece é que estás a colocar um objecto na hash, cada vez que o vais buscar crias uma cópia desse objecto, alteras essa cópia e colocas a cópia dentro da hash novamente para substituir o objecto que lá tinhas colocado inicialmente. É isto?

Mas lá está, é assim que tenho de fazer, não posso simplesmente usar a referência e alterar o voo e pronto, já está, não precisava de actualizar a hash. Eu sei que isto seria o correcto, mas não posso fazer nada...

Se queres que o clone funcione correctamente, implementa-o correctamente, e isso significa não criar um método novo mas sim redefinir o que já existe.

Mas mais uma vez, regras do prof... Se bem que aqui não tenho a certeza total se os clones devem retornar o tipo da classe ou Object... De qualquer maneira isso não deveria ser problema digo eu), eu posso ter os métodos overloaded que quiser e como tal, ele deveria usar aquele que eu quero e neste caso o meu clone e assim não precisava do cast para nada...

0

Partilhar esta mensagem


Link para a mensagem
Partilhar noutros sites
Pelas razões atrás apontadas, não utilizaremos o método clone() da classe Object e vamos definir nas nossas

classes um método próprio. Se em versões anteriores de JAVA alguma complicação poderia existir com o nome de

tal método, em JAVA5, com a possibilidade de o tipo de resultado de um método redefinidor ser subtipo do

redefinido, então os nossos método de cópia podem chamar-se clone() e devolver um objecto do tipo da classe

onde forem implementados, como se mostra nos exemplos a seguir:

public Ponto2D clone() {

public Pixel clone() {

public Ponto3D clone() {

Todos estes métodos são portanto redefinições correctas do método clone() de Object, pois têm o mesmo nome,

modificador de acesso mais lato e tipo de resultado que é um subtipo de Object. Quando usados, estes métodos

devolverão de imediato um objecto da mesma classe do objecto “clonado”, não sendo necesário realizar qualquer

casting:

Ponto2D p2 = p1.clone();

Pixel pix2 = pix1.clone();

Pequeno excerto de um PDF da cadeira... Esta é a razão pela qual os nosso métodos clone() devolvem o tipo da classe e sendo uma regra da cadeira, eu tenho de a usar se quero ter a melhor nota possível. Seja isto discutível se é boa prática ou não, não me interessa, pelo menos neste momento, talvez um dia mais tarde, neste momento não quero saber se é correcto ou não o que interessa é que é assim que tenho de fazer e como tal, continuo com o mesmo problema no método getVoo().

0

Partilhar esta mensagem


Link para a mensagem
Partilhar noutros sites

Não pretendo discutir se é ou não bom, não tinha, até há pouco tempo, percebido que eram restrições da forma como vos ensinam.

Mas então, o código que tens, está a dar erro onde concretamente ou de que forma é que está a desviar-se do que pretendes?

Podes colocar um excerto maior de código, para que perceba melhor os problemas? Ou enviar-me parte do código?

0

Partilhar esta mensagem


Link para a mensagem
Partilhar noutros sites

É complicado porque não faço ideia do que enviar e não está a dar erro nenhum, eu só não sei se estou a fazer as coisas direitas mas acabei de fazer um teste e se calhar até estou... não sei bem... Vamos lá ver se a gente se entende:

Tipo, no HashMap tenho uma lista de objectos do Tipo Voo associado a um código do tipo String e tenho um método getVoo() que é suposto devolver uma cópia sem partilha de endereço de determinado voo de código especificado pelo que a assinatura da função será:

public Voo getVoo(String codigo);

Tendo em conta que a minha classe Voo é tipo a classe Teste que apresentei anteriormente, ou seja, assim:

public Voo clone() { return new Voo(this); }

Como é que eu codifico o método getVoo() sem que haja partilha de endereços? Teoricamente seria:

this.listaVoos.get(codigo).clone();

Mas este clone está a devolver um objecto, ou seja, não é o meu clone e a minha dúvida aqui é, porquê? Para eu conseguir usar o meu clone tenho de fazer um cast assim:

((voo)this.listaVoos.get(codigo)).clone();

Mas o get() do HashMap não deveria devolver um objecto do tipo Voo? Tendo em consideração que a definição do HashMap é HashMap<String, Voo>.

O que me leva ao seguinte... Experimentei codificar o método getVoo() apenas assim:

return this.listaVoos.get(codigo);

E tenho o seguinte código para testes:

Voo v1 = AEROGEST.mapaVoo.getVoo("A");
v1.setCompanhiaArea("TAP");

AEROGEST.mapaVoo.addVoo(v1);

Voo v2 = AEROGEST.mapaVoo.getVoo("A");

Consola.writeLine(v1.toString());
Consola.writeLine(v2.toString());

E o output é:

VooComercial@1d58aae

VooComercial@de6f34

Ou seja, está com endereços diferentes né? O que me leva a pergunta, o getVoo() devolveu uma cópia ou uma referência do Voo?

O que está a azul são as minhas principais dúvidas...

Algo que não sei se será importante e/ou talvez responda ás minhas dúvidas mas a classe Voo é abstracta e o seu clone está definido como public abstract Object clone() e as suas subclasses é que têm o clone definido por exemplo: public VooComercial clone() { return new VooComercial(this); }.

0

Partilhar esta mensagem


Link para a mensagem
Partilhar noutros sites

Mas o get() do HashMap não deveria devolver um objecto do tipo Voo? Tendo em consideração que a definição do HashMap é HashMap<String, Voo>.

A declaração da estrutura tem de ser HashMap<String, Voo> m = new HashMap<String, Voo>(). Se a tiveres assim, então o get terá de devolver um Voo e o clone invocado tem de ser o teu.

Ou seja, está com endereços diferentes né? O que me leva a pergunta, o getVoo() devolveu uma cópia ou uma referência do Voo?

Referencias diferentes são objectos diferentes, nesse sentido, os dois objectos do tipo Voo são objectos diferentes. Não sabendo mais sobre como os métodos introduzem dados nas estruturas e que mais implicações tens no código não posso ajudar muito.

Algo que não sei se será importante e/ou talvez responda ás minhas dúvidas mas a classe Voo é abstracta e o seu clone está definido como public abstract Object clone() e as suas subclasses é que têm o clone definido por exemplo: public VooComercial clone() { return new VooComercial(this); }.

Não existe qualquer problema em teres declarado um método como abstracto, e a classe que o define. Apenas estás a dizer que as subclasses terão de ser abstractas ou de implmentar todos o método.

0

Partilhar esta mensagem


Link para a mensagem
Partilhar noutros sites

A declaração da estrutura tem de ser HashMap<String, Voo> m = new HashMap<String, Voo>(). Se a tiveres assim, então o get terá de devolver um Voo e o clone invocado tem de ser o teu.

Mas é exactamente isso que eu tenho e o get não devolve um Voo mas sim um Object. Se eu quiser Voo tenho de fazer cast e assim, posso chamar o meu clone.

Referencias diferentes são objectos diferentes, nesse sentido, os dois objectos do tipo Voo são objectos diferentes. Não sabendo mais sobre como os métodos introduzem dados nas estruturas e que mais implicações tens no código não posso ajudar muito.

Isso não me respondeu lá muito bem lol... Se referências diferentes são objectos diferentes, significa que o código que tenho está a devolver-me dois objectos diferentes, ou seja, está a fazer o que o meu clone fazia mas sem o usar. Certo? Só não percebo como é que o get() está a fazer isso, será que já tem um clone() implícito e ai está a chamar o meu?

Não existe qualquer problema em teres declarado um método como abstracto, e a classe que o define. Apenas estás a dizer que as subclasses terão de ser abstractas ou de implmentar todos o método.

A questão não era haver algum problema mas tipo, como já deu para perceber, o get() devolve um Object e como tal, faço um cast e depois invoco o meu clone() até aqui tudo bem, mas o tipo de Voo a retornar pode ser um entre muitos (as subclasses de Voo) e como eu não sei qual é e não vou ter um método para cada tipo, o objectivo é retornar um objecto do tipo Voo, dai os dois casts aqui:

(Voo)(((Voo)this.listaVoos.get(codigo)).clone());

O de dentro para conseguir usar o meu clone() e o de fora porque o meu clone vai no fundo devolver um Object devido à declaração abstracta do método clone() na classe Voo, apesar de as subclasses terem, cada uma, um método clone() que devolve o seu tipo.

Eu só não percebo é porquê que preciso de fazer um cast ao resultado do get() para puder usar o meu clone()...

EDIT:

Com este teste (um bocadinho diferente do outro):

Voo v0 = AEROGEST.mapaVoo.getVoo("B");

Voo v1 = AEROGEST.mapaVoo.getVoo("A");
v1.setCompanhiaArea("TAP");

AEROGEST.mapaVoo.addVoo(v1);

Voo v2 = AEROGEST.mapaVoo.getVoo("A");

Consola.writeLine("V0: " + v0.toString());
Consola.writeLine("V1: " + v1.toString());
Consola.writeLine("V2: " + v2.toString());

Usei duas codificações diferentes para o getVoo():

Primeira:

public Voo getVoo(String codigo) {
if(this.listaVoos.containsKey(codigo)) {
	return (Voo)(((Voo)this.listaVoos.get(codigo)).clone());
} else {
	return null;
}
}

Segunda:

public Voo getVoo(String codigo) {
if(this.listaVoos.containsKey(codigo)) {
	return this.listaVoos.get(codigo);
} else {
	return null;
}
}

O output foi exactamente o mesmo:

V0: VooComercial@1d58aae
V1: VooComercial@de6f34
V2: VooComercial@156ee8e

O que me leva a pensar (perguntar?), que ambos fazem exactamente a mesma coisa. Ou seja, se num deles eu estou a fazer o cast para chamar o meu clone e depois o cast para converter para Voo e o resultado é X e o método mais simples que é devolver o resultado do get() também tem como resultado X, é porque em nenhum dos casos os endereços estão a ser partilhados, correcto? Mais vale usar o método mais simples...

0

Partilhar esta mensagem


Link para a mensagem
Partilhar noutros sites

O resultado não devia ser o mesmo, não sei o que se passa nesse código mas faz uma depuração do código e segue a linha de execução para ver que valores estão a ser devolvidos e que métodos estão a ser chamados.

0

Partilhar esta mensagem


Link para a mensagem
Partilhar noutros sites

Já tentei e não vi nada de especial... ele simplesmente saca o valor da hash e devolve-o...

0

Partilhar esta mensagem


Link para a mensagem
Partilhar noutros sites

Eu gostava de ver é onde é que existe uma situação onde faz sentido copiares referências... a própria noção é tão estranha que nem sei o que dizer... realmente que raio de Java vos estão a ensinar, agora percebo alguns dos problemas que aqui apareceram.

Há altura em que faz sentido copiar o objecto todo, de modo a que quando alteras uma cópia não alteras a outra, e há situações em que o objectivo é mesmo partilhares a mesma referência para um objecto.

0

Partilhar esta mensagem


Link para a mensagem
Partilhar noutros sites

Eu falei de cópia de referências, que é o que o clone faz por omissão, e não cópia de objectos. Algures pelo tópico pareceu-me ser isso que se estava a fazer, agora que procurei o texto por alto não vejo onde, devo ter feito alguma confusão.

Mesmo assim, muitas, senão todas, as restrições que vos estão a impor não estão bem explicadas. Eu consigo encontrar alguns casos onde essas restrições são aplicáveis, mas parecem-me tão específicos que causam alguma estranheza, isto pelo que me dizem que são obrigados a fazer.

Considerando a cópia de objectos, repara no que está a ser feito:

a) colocas o objecto na estrutura;

;) retiras uma cópia do objecto;

c) alteras a cópia;

d) colocas a cópia na estrutura de forma a substituires o original.

Isto ensina o quê? Que há situações onde pode ser útil? Isso não é ensinar pelo caso particular em vez de ensinar pelo caso geral? Não seria mais produtivo para o aluno aprender, usando, os problemas e soluções inerentes ao uso de referências. Ensinar-lhe como as referências funcionam, onde estão os erros mais comuns, etc.?

Mas claro, daqui de onde estou apenas posso comentar sobre o que escreveram, e confesso que já me confundi algumas vezes ;)

Nazgulled, tinhas falado que o trabalho era para entregar entretanto, já está entregue? Se ainda não resolveste o problema envia-me o código, posso dar uma vista de olhos e tentar ver o que se está a passar.

0

Partilhar esta mensagem


Link para a mensagem
Partilhar noutros sites

Crie uma conta ou ligue-se para comentar

Só membros podem comentar

Criar nova conta

Registe para ter uma conta na nossa comunidade. É fácil!


Registar nova conta

Entra

Já tem conta? Inicie sessão aqui.


Entrar Agora