Sobre o Java clonável

Eu estava procurando alguns tutoriais explicando sobre o Java Cloneable , mas não Cloneable nenhum link bom, e o Stack Overflow está se tornando uma escolha mais óbvia de qualquer maneira.

Eu gostaria de saber o seguinte:

  1. Cloneable significa que podemos ter um clone ou uma cópia de objects, implementando a interface Cloneable . Quais são as vantagens e desvantagens de fazer isso?
  2. Como a clonagem recursiva acontece se o object é um object composto?

A primeira coisa que você deve saber sobre Cloneable é – não use.

É muito difícil implementar a clonagem com direito Cloneable , e o esforço não vale a pena.

Em vez disso, use algumas outras opções, como o apache-commons SerializationUtils (clone profundo) ou BeanUtils (clone superficial), ou simplesmente use um construtor de cópia.

Veja aqui as visões de Josh Bloch sobre clonagem com Cloneable , o que explica as muitas desvantagens da abordagem. ( Joshua Bloch era um funcionário da Sun e liderou o desenvolvimento de vários resources Java.)

O próprio clonável infelizmente é apenas uma interface de marcação, isto é: ele não define o método clone ().

O que é, é alterar o comportamento do método Object.clone () protegido, que lançará um CloneNotSupportedException para classs que não implementam Cloneable e executará uma cópia superficial inteligente para classs que não o fazem.

Mesmo que esse seja o comportamento que você está procurando, ainda será necessário implementar seu próprio método clone () para torná-lo público.

Ao implementar seu próprio clone (), a idéia é começar com o object create by super.clone (), que é garantido para ser da class correta, e então fazer qualquer população adicional de campos no caso de uma cópia superficial não ser o que você quer. Chamar um construtor de clone () seria problemático, pois isso quebraria a inheritance caso uma subclass queira adicionar sua própria lógica clonável adicional; se fosse chamar super.clone (), ele obteria um object da class errada nesse caso.

Essa abordagem ignora qualquer lógica que possa ser definida em seus construtores, o que poderia ser problemático.

Outro problema é que qualquer subclass que esquecer de replace clone () herdará automaticamente a cópia superficial padrão, o que provavelmente não é o que você deseja no caso de um estado mutável (que agora será compartilhado entre a origem e a cópia).

A maioria dos desenvolvedores não usa Cloneable por esses motivos e simplesmente implementa um construtor de cópias.

Para mais informações e possíveis armadilhas de Cloneable, eu recomendo o livro Effective Java de Joshua Bloch

  1. Clonagem invoca uma maneira extra-lingüística de construir objects – sem construtores.
  2. Clonagem requer que você trate de alguma forma com CloneNotSupportedException – ou para incomodar o código do cliente para tratá-lo.
  3. Os benefícios são pequenos – você simplesmente não precisa escrever manualmente um construtor de cópia.

Então, use Cloneable judiciosamente. Não lhe dá benefícios suficientes em comparação com o esforço que você precisa aplicar para fazer tudo certo.

Clonagem é um paradigma básico de programação. O fato de o Java ter implementado mal de várias maneiras não diminui a necessidade de clonagem. E é fácil implementar a clonagem que funcionará como você quiser, superficial, profunda, mista, qualquer coisa. Você pode até usar o nome clone para a function e não implementar Cloneable se quiser.

Suponha que eu tenha as classs A, B e C, onde B e C são derivados de A. Se eu tenho uma lista de objects do tipo A assim:

 ArrayList list1; 

Agora, essa lista pode conter objects do tipo A, B ou C. Você não sabe de que tipo são os objects. Então, você não pode copiar a lista assim:

 ArrayList list2 = new ArrayList(); for(A a : list1) { list2.add(new A(a)); } 

Se o object for realmente do tipo B ou C, você não obterá a cópia correta. E se E é abstrato? Agora, algumas pessoas sugeriram isso:

 ArrayList list2 = new ArrayList(); for(A a : list1) { if(a instanceof A) { list2.add(new A(a)); } else if(a instanceof B) { list2.add(new B(a)); } else if(a instanceof C) { list2.add(new C(a)); } } 

Esta é uma ideia muito, muito ruim. E se você adicionar um novo tipo derivado? E se B ou C estiverem em outro pacote e você não tiver access a eles nessa class?

O que você gostaria de fazer é isto:

 ArrayList list2 = new ArrayList(); for(A a : list1) { list2.add(a.clone()); } 

Muitas pessoas indicaram por que a implementação Java básica do clone é problemática. Mas é facilmente superado dessa maneira:

Na class A:

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

Na class B:

 @Override public B clone() { return new B(this); } 

Na class C:

 @Override public C clone() { return new C(this): } 

Eu não estou implementando Cloneable, apenas usando o mesmo nome de function. Se você não gosta disso, nomeie outra coisa.

A) Não há muitas vantagens de clone sobre um construtor de cópias. Provavelmente, o maior deles é a capacidade de criar um novo object do mesmo tipo dynamic (assumindo que o tipo declarado seja clonável e tenha um método clone público).

B) O clone padrão cria uma cópia superficial e continuará sendo uma cópia superficial, a menos que sua implementação de clone altere isso. Isso pode ser difícil, especialmente se sua turma tiver campos finais

Bozho está certo, o clone pode ser difícil de acertar. Um construtor / fábrica de cópias atenderá a maioria das necessidades.

Quais são as desvantagens de Cloneable?

A clonagem é muito perigosa se o object que você está copiando tiver composição. É necessário pensar sobre o possível efeito colateral abaixo neste caso, pois o clone cria uma cópia superficial:

Vamos dizer que você tem um object para manipular a manipulação relacionada ao database. Digamos que esse object tenha o object Connection como uma das propriedades.

Então, quando alguém cria um clone de originalObject , o object que está sendo criado, digamos, cloneObject . Aqui o originalObject e cloneObject mantêm a mesma referência para o object Connection .

Vamos dizer que originalObject fecha o object Connection , então agora o cloneObject não funcionará porque o object de connection foi compartilhado entre eles e foi fechado pelo originalObject .

Um problema semelhante pode ocorrer se disser que você deseja clonar um object que tenha o IOStream como uma propriedade.

Como a clonagem recursiva acontece se o object é um object composto?

Cloneable realiza cópia superficial. O significado é que os dados do object original e do object clone apontarão para a mesma referência / memory. Ao contrário, no caso de cópia profunda, os dados da memory do object original são copiados para a memory do object clone.