Jump to content

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


Guest id194
 Share

Recommended Posts

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?

Link to comment
Share on other 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 👍 ) 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...

Link to comment
Share on other 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.

Link to comment
Share on other 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.

Link to comment
Share on other 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...

Link to comment
Share on other 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?

Link to comment
Share on other 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.

Link to comment
Share on other 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?

Link to comment
Share on other 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...

Link to comment
Share on other 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?

Link to comment
Share on other 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 é 👍 , 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...

Link to comment
Share on other 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...

Link to comment
Share on other 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.

Link to comment
Share on other 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...

Link to comment
Share on other 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().

Link to comment
Share on other 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?

Link to comment
Share on other 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); }.

Link to comment
Share on other 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.

Link to comment
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now
 Share

×
×
  • Create New...

Important Information

By using this site you accept our Terms of Use and Privacy Policy. We have placed cookies on your device to help make this website better. You can adjust your cookie settings, otherwise we'll assume you're okay to continue.