Trabajamos no solo para producir, sino para dar valor al tiempo.
Eugène Delacroix

Copiar vs Duplicar objetos en Java

Java tiene gravísimos puntos oscuros desde sus orígenes, debido a que la API y el lenguaje están muy orientados a la implementación de la JVM y a la compatibilidad hacia atrás en lugar de orientarse a facilitar un lenguaje claro y sin ambigüedades.

Baste decir que cuando hablamos de J2EE no hablamos de “Java v2 Enterprise Edition” sino de “Java v1.2 Enterprise Edition“, pero todo el mundo (incluido yo) utiliza J2EE en lugar de simplemente JEE o J7EE (para referirnos a la versión 1.7 que es la actual de Java). Por cierto, cuánta falta hace que llegue por fin una versión 2.0 de Java!!!

Una de estas áreas de mejora es el mal uso de un concepto tan común como “duplicar” un objeto. La mayoría de las pesonas que leen este código,

1
2
3
Person personA= new Person();
personA.setName("Juan");
Person personB = personA;

piensan que personB es una copia de personA, dos objetos diferentes pero con los mismos valores, porque los humanos interpretamos la línea 3 como “Crear un nuevo objeto del tipo Person llamado personB que inicalizamos con los valores que tiene el objeto llamado personA”, o simplemente decimos “Copiar personA a un nuevo objeto del tipo Person llamado personB” pero queremos decir “Duplicar” en lugar de copiar.

Sin embargo Java se comporta de un modo diferente al esperado ya que la JVM interpreta esa línea de diferente forma. Si ahora realizamos un cambio en personB, comprobaremos asombrados cómo personA también ha cambiado!!!

4
5
personB.setName("Luisa");
//En este punto personA.getName() devolería "Luisa" y no "Juan".

¿Por qué? Porque lo que realmente significa para la JVM la conflictiva línea 3 anterior, es “Crear una nueva variable del tipo Person llamada personB y asginar la misma referencia que tiene la variable personA“, o simplemente “Copiar la referencia (y no el contenido) de personA en una nueva variable del tipo Person llamada personB“. El resultado es que tenemos dos variables apuntando a una única referencia (espacio en memoria) en lugar de dos variables independientes con sus respectivas y diferentes referencias (espacios de memoria). Así, cuando una de las dos variables cambia, cambian las dos. Sí, Java se orienta más a punteros que a objetos, que es más útil en programación a bajo nivel y para ahorra unos bits; no en vano Java se basó en C.

Entonces, Java no facilita una funcionalidad para duplicar objetos?!?!?! Java ofrece un interfaz Clonable, pero no su imlementación. Ocurre lo mismo para comparar dos objetos o comprobar si dos objetos son iguales o imprimir los valores de un objeto, te obliga a escribirte tu propia implementación para estas funciones de uso común, más común (para un desarrollador) que las interpretaciones estándares, ralentizando el desarrollo al escribir código de poco valor añadido y propenso a los bugs pero sobre todo complicando el mantenimiento ya que tiene que evolucionar al tiempo que evoluciona el negocio.

O utilizar implementaciones genéricas que son peligrosas.

Duplicación de objetos genérica

Podemos implementar nuestra propia solución genérica para solucionar esta laguna de Java, o podemos utilizar soluciones ya existentes. Así org.apache.commons.beanutils.BeanUtils parece ofrecernos la solución:

Person personB = null;
try {
	personB = (Person) BeanUtils.cloneBean(personA);
} catch (Exception e) {
	e.printStackTrace();
}

En este caso funcionaría, y ahora al volver a ejecutar un cambio en personB no cambiamos personA.

Pero qué pasa si el objeto Person tiene a su vez otros objetos, como por ejemplo un objeto User?

@Test public void duplication2Test2 () {
	User userA;
	Person personA, personB;
	personA = new Person();
	personA.setName("nameA");
	userA = new User();
	userA.setUsername("usernameA");
	personA.setUser(userA);
	personB = personDuplication.duplication2(personA);
	personB.setName("nameB");
	personB.getUser().setUsername("usernameB");
System.out.println("duplication1Test - personA.name = " + personA.getName());
System.out.println("duplication1Test - personB.name = " + personB.getName());
System.out.println("duplication1Test - personA.user.username = " + personA.getUser().getUsername());
System.out.println("duplication1Test - personB.user.username = " + personB.getUser().getUsername());
	Assert.assertEquals(personA.getName(), "nameA");
	Assert.assertEquals(personB.getName(), "nameB");
	Assert.assertEquals(personA.getUser().getUsername(), "usernameA");
	Assert.assertEquals(personB.getUser().getUsername(), "usernameB");
}

Este Test falla. La razón es simple, BeanUtils duplica las variables primitivas y las variables de tipos de Java como Boolean, Integer o String (que es la que se utiliza en el ejemplo), pero en lugar de duplicar los objetos que nosotros implementamos (como el usado en el ejemplo User) está copiando su referencia.

Duplicación profunda de objetos genérica

Una forma sencilla es serializar nuestros objetos y desearializarlos en nueva instancia, para lo que podemos utilizar org.apache.commons.lang.SerializationUtils. La parte negativa de esta solución es que nuestros objetos deben ser serializables (implementar el intefaz Serializable), y también todos los objetos que los componen, y también todos los objetos que componen a estos, y también… por lo cual es una solución con peligros de cara al mantenimiento evolutivo.

Por fin, la siguiente implementación pasaría el test anterior:

public Person duplicationPerson(Person personSource) {
	Person personTarget = null;
	try {
		personTarget = (Person) SerializationUtils.clone(personSource);
	} catch (Exception e) {
		e.printStackTrace();
	}
	return personTarget;
}

Lo podríamos hacer más genérico sustituyendo el tipo Person por Serializable. Más potencia, más peligro.

Java – Easy Overrided toString()

@Override
public String toString() {
return ToStringBuilder.reflectionToString(this, ToStringStyle.SHORT_PREFIX_STYLE);
}

 

ToStringBuilder class belongs to org.apache.commons.lang library.

Tutoriales Java

Eres nuevo en Java?

Entonces dos consejos:

  1. Como fuente primaria de información siempre elige la documentación oficial. Vamos, que te manejes como Peter on the house (o era in, o quizás at… umm…) con el inglés.
  2. No pares de practicar y aprender.

Así que, a qué esperas para ponerte con estos manuales de Java de la página oficial de Oracle? Al menos, ponte con los que más te interesen…

Java7 Liberado

Java7Para ser exactos Java7 se liberó ya hace unos días. A  principios de Julio ya se publicó una versión candidata (Release Candidate) y finalmente se ha liberado la versión definitiva a finales del mismo mes (Major Release).

Pero de eso ya os habréis enterado por otros medios y yo necesitaba un tiempo para leer y cacharrear antes de informar y opinar :)

El caso es que ya está entre nosotros la nueva versión de Java, la primera gran entrega de Oracle, que compró Sun. “Sólo” han pasado unos 5 años desde la última liberación importante, un tiempo enorme en términos de nuestra industria en los que ha habido significativos cambios tecnológicos  (HTML5, Chrome, Android, iPad..) .

Los cambios más relevantes en esta entrega han sido

  • La JVM ha sido extendida para mejorar la gestión automática de la memoria, mejorando entre otros puntos el colector de basura y ofrecer soporte a otros lenguajes.
  • Extensiones varias en la API, que afectan entre otros a Swing, IO, Seguridad, XML, Internacionalización.
  • Modificaciones de la sintaxis de Java. Publicaré otras dos entradas en la que se mostrarán en más profundidad.

A pesar de los 5 años transcurridos, se han debido aparcar para próximas versiones varias funcionalidades que se esperaban para esta versión.

Aunque la calidad de las liberaciones han mejorado con Oracle (por ejemplo son más estables las nuevas versiones de MySQL), ya se han reportado un bug de considerable impacto : una optimización en Java6 con una deficiente gestión de recursos  por defecto no activada pasa a estar por defecto activada en Java7, por lo cual al Apache Software Foundation recomienda no utilizar Java7 en un entorno de producción con sus productos.

Oracle ha admitido que tuvo conocimiento de dicho bug unos días antes de la publicación, y sin embargo no hizo nada! Oracle debió retrasar la publicación? Creo que no, nos da tiempo a leer y cacharrear. Seamos prácticos, el asunto del bug se está sobrevalorando.

Pero debió comunicar mejor el bug? Por supuesto.

Este no es un caso aislado sino una muestra más de una comunicación deficiente sobre Java por parte de Oracle. También he percibido mucha confusión respecto a la información de esta liberación desde Oracle: informaciones duplicadas, otras antiguas, es más fácil encontrar documentación buscando en Google que en el propio site de Oracle.

Supongo que en parte se deberá a que no es para estar orgulloso ni por parte de Sun ni por parte de Oracle, y que mejorará con el tiempo cuando tengan claro dónde quieren llevar a Java.

Mi Conclusión

Mi opinión de esta Release es poca sustancia con maquillaje pero con suficientes lagunas para no ser utilizada, aún. Así mismo Oracle debe mejorar su deficiente comunicación. Aunque la siguiente entrega queda lejos, esperemos que sea una versión v02.00 en lugar de la v01.08.

Y vosotros qué pensáis de esta publicación? Suficiente? Extraordinaria? Decepcionante?

Más información del Sitio Oficial Java7

PD

Como con todas las publicaciones suficientemente significativas de cualquier producto, aún debemos dejar pasar un tiempo prudencial antes de utilizarlo en un entorno de producción para estudiarlo y esperar a que se estabilice lo suficiente.

PPDD

Esperemos que la modularización de la JDK aparcada para la próxima entrega traiga consigo una refactorización significativa y nos encontremos al fin con la tan (al menos por mí) esperada versión 2.0 de Java en lugar de con la versión 1.8 o un nuevo aplazamiento, aunque sea a coste de incompatibilidades con versiones anteriores: Java se está haciendo una bola de nieve gigantesca en crecimiento parcheado eterno y continuo.

Sobre el eterno debate sobre composición, y sobre si la POO debería ser enterrada dignamente por los servicios prestados, da material para otro post.

Eres un experto en Java ?

La respuesta a esa pregunta es un "lo dudo mucho". Al hilo de una entrada anterior sobre lo necesario de la formación continua en nuestra profesión, he encontrado esta entrada (en inglés, no recuerdo dónde lo vi y ni siquiera si ya lo publiqué) donde nos muestra un mapa de conocimientos de Java

Dentro de toda esta maraña de concoimientos, yo me he "especializado" en J2EE que como puedes ver es una pequeña rama dentro del conjunto.

Y como apuntaba en la entrada mencionada sobre formación continua, no sólo hay que saber Java : hay que saber utilzar frameworks sobre Java, otros lenguajes de programación, un IDE, servidores de aplicaciones, bases de datos, control de versiones y otros conceptos de ingeniería del software. Todo ello en constante evolución.

Por si eres alguien que está empezando : tranquilo, paso a paso, que si Lycka sabe todo esto cualquiera puede.

Si eres alguien que lleva tanto tiempo como yo… ánimo!

Comenta