Certificarse en Java SE 8

OCP-JavaSE8-Programmer-clr

El pasado 1 de abril me certifiqué en Java SE 8. Desde entonces, han sido varias las personas que me han preguntado como preparé el examen. Hoy escribo esta entrada con la idea de compartir mi experiencia e intentar dar respuesta a esa pregunta.

No es la primera vez que me preparo para una de estas certificaciones, concretamente, es la cuarta vez. Por tanto, he pasado varias veces por el proceso y puedo afirmar, sin lugar a dudas, que esta última ha sido la más dura y a la vez la más enriquecedora de todas a las que me he presentado.

La motivación: ¿Vale la pena certificarse?

Yo creo que sí. No obstante, hay que ser realista y asumir que no es garantía de nada. Al final, lo más importante es la práctica y la propia experiencia. Esto ayuda pero dudo mucho que sea determinante. En mi caso, utilizo estos exámenes para aprender en profundidad las nuevas características que aparecen en las distintas versiones de Java. Es la excusa perfecta para poder ver todas las novedades y mantenerme al día. Por un lado aprendes y por el otro consigues un cierto «reconocimiento» que siempre ayuda en el mundo laboral.

Java SE 8 ha supuesto un avance importante en el lenguaje. La introducción de Lambdas y Streams  supone un antes y un después; la programación funcional es ahora parte de Java. Todas estas novedades se traducen en una nueva sintaxis a la que hay que ir acostumbrando la vista: estos cambios han venido para quedarse. La única forma de ir cogiendo soltura es empezar a ver ejemplos e intentar aplicarlos durante el día a día. Si bien hay novedades que debemos conocer pero que difícilmente aplicaremos, véase los métodos ‘default’, hay otras, como el uso de los Streams, que harán que ya no filtremos nunca más una colección de la misma forma.

El contexto

Como esto de las certificaciones es algo que lleva vivo bastantes años, en función de tu situación actual existen varios caminos a los que puedes optar :

  • Si empiezas desde cero, ergo no tienes ninguna certificación previa, deberás superar dos exámenes distintos para obtenerla. El primero es el 1Z0-808 (Java SE 8 Programmer I) y el segundo es el 1Z0-809 (Java SE 8 Programmer II).
  • Si ya tenías una certificación de la versión Java SE 7, tu objetivo es el 1Z0-810 (Upgrade Java SE 7 to Java SE 8 OCP Programmer)
  • Finalmente, si posees una certificación de la versión Java SE 6 o anterior, tu nuevo amigo es el 1Z0-813 (Upgrade to Java SE 8 OCP from Java SE 6 and all prior versions)

De las tres opciones, yo he ido por el tercer camino puesto que hace unos años obtuve mi primera certificación mediante la versión Java SE 6. La cantidad de temario es la principal diferencia entre las tres opciones.

Si empiezas de cero, estás obligado a pasar dos exámenes. En cierta manera es normal, después de 8 versiones, el lenguaje ha crecido muchísimo y el temario es muy extenso. El primer examen es una toma de contacto con los conceptos básicos del lenguaje y el segundo entra mucho más en detalle.

En los otros dos caminos sólo te examinas de las novedades respecto a la versión que posees. En su momento decidí no presentarme al examen para Java SE 7 puesto que las novedades del mismo no eran tan abundantes ni transcendentales y la siguiente versión no tardaría mucho en aparecer. A veces hay que plantearse también la proximidad de la siguiente actualización. Ahora mismo, Java SE 9 no tiene previsto su lanzamiento hasta el 23 de Marzo de 2017, por lo que hay margen para estar al día. También hay que pensar que muy posiblemente no aparezca un examen de java SE 9 hasta bien bien 2018.

La preparación

Hay una serie de elementos clave que me han permitido superar este desafío de forma satisfactoria:

  1. La guía de estudio.
  2. La práctica mediante los bancos de pruebas.
  3. La documentación oficial.
  4. La comunidad.

A continuación vamos a ir desgranando cada uno de estos elementos.

La guía de estudio

Sin duda el elemento más importante, piedra angular de todo el proceso. Para la preparación de estos exámenes siempre suele haber uno o dos libros de referencia. Si no lo hay significa que estas preparando algo muy nuevo o muy poco conocido. Piensa además que la elaboración de estos libros es una tarea titánica y requiere bastante tiempo, los autores comienzan estos proyectos en cuanto el examen entra en fase beta para intentar tenerlo listo cuanto antes y con la mejor calidad posible.

¿Qué libro he utilizado? Pues el OCP: Oracle Certified Professional Java SE 8 Programmer II Study Guide: Exam 1Z0-809 de Jeanne Boyarsky Scott Selikoff. Este libro cubre todos los objetivos necesarios para 1Z0-809,  1Z0-810 y 1Z0-813. ¿Qué ocurre si tienes que examinarte del 1Z0-808? Pues que tendrás que leerte también la primera parte de los mismos autores, el OCA: Oracle Certified Associate Java SE 8 Programmer I Study Guide: Exam 1Z0-808.

¿Son estos libros perfectos? Ya te avanzo que no. La realidad es que contienen muchas erratas por lo que antes de empezar a leer cada uno de sus capítulos te vas a tener que pasar por la web del autor para revisar la tabla de erratas de cada libro. Obviando este pequeño detalle, los libros son muy útiles. Te indicarán qué debes estudiar, tratarán de explicártelo de forma sencilla y te proporcionarán material en forma de preguntas para ir cogiendo práctica.

La práctica mediante los bancos de pruebas.

Otro punto clave. Puedes leer infinidad de recursos distintos pero sólo mediante la práctica conseguirás interiorizar los conceptos. Estos exámenes están hechos a mala leche. La realidad es que debes entrenar tu habilidad como compilador. Debes ser capaz de ver un trozo de código e identificar rápidamente si compilará o no. Para esto solo hay una forma de prepararse y es mediante preguntas similares.

El mejor material que he encontrado para este fin es el que ofrece la web de Enthuware, sí, verás que es de pago pero merece la pena hasta el último céntimo invertido. No solo ofrece material en forma de preguntas sino que han desarrollado todo un conjunto de software para poder utilizarlo desde prácticamente cualquier sitio (Offline desde escritorio y Online desde web, móvil incluido). Cada pregunta viene acompañada de su explicación, es el perfecto complemento al libro. Te puedes examinar por objetivos, simulando el examen real, etc. Ofrece multitud de posibilidades que vienen de fábula para la preparación.

La documentación oficial.

La visita obligada en este viaje. Ante cualquier duda acabarás acudiendo a la fuente original, el Javadoc del JDK8. Mi recomendación es que la vayas añadiendo ya a tu barra de marcadores o favoritos y te prepares a leer multitud de veces la sección de los Streams, Collectors o Files, entre otros. No nos engañemos, debes conocerte bastante bien las secciones relacionadas con los objetivos del examen. Por lo menos que te suenen un poco la mayoría de métodos. Es imposible tener material para todos los métodos que existen, deberás explorar las distintas posibilidades por tu cuenta.

La comunidad.

Este proceso se lleva mejor en grupo que solo, si de alguna forma puedes formar algún grupo de estudio, aunque sea únicamente con otra persona, la preparación será MUCHO más llevadera. En el peor de los casos siempre puedes acudir a las comunidades online. Para este cometido existe una que es la mejor referencia en este mundillo, CodeRanch.

En CodeRanch encontrarás a gente de todo el mundo compartiendo recursos y dudas sobre la mayoría de certificaciones sobre Java,. Tiene subforos específicos para cada certificación y los mismos autores de los libros participan de forma activa resolviendo dudas y recibiendo feedback. Si tienes una duda, lánzala allí sin miedo, alguien acudirá al rescate. Yo mismo compartí mi experiencia en el subforo de resultados y he añadido mi nombre al OCJP Wall Of Fame.

Mi propia experiencia

En lo que respecta a mí, compré el libro a mediados de febrero y empecé a leerlo durante los viajes de tren que suelo hacer entre semana, aproximadamente una hora y media diaria. Me lo tomé con calma y me saltaba aquello que más o menos ya conocía, no me hacía falta revisar el libro entero pero aun así lo hice, nunca viene mal refrescar conceptos.

Una vez leído el libro empecé con el siguiente método:

  1. Hacer entre 10 y 20 preguntas de un objetivo concreto.
  2. Entender las explicaciones de cada una de las respuestas, las hubiera acertado o no. Si hacía falta volvía a revisar el libro o acudía al javadoc del JDK8.
  3. Practicar un poco con el IDE para interiorizar conceptos y ver otras posibilidades.
  4. Seleccionar otro objetivo y empezar de nuevo el ciclo.

De esta manera pasé un par de veces por las más de 300 preguntas que había en el material de Enthuware, a parte de las propias del libro. Os podéis imaginar que le dediqué unas cuantas horas.

Con todo lo dicho, me gustaría remarcar un detalle, estudiar a ratos está bien pero llega un momento que debes marcarte una fecha objetivo para generar también un poco de auto presión. Así lo hice, a principios de marzo compré el examen y ya marqué la fecha límite, el 1 de abril. En el momento que tienes fecha, la cosa cambia, hay que tomárselo en serio.

Finalmente llegó el día, acudí al centro y gasté hasta el último minuto de las dos horas y diez minutos que tienes disponibles para terminar las 60 preguntas. No fue fácil la verdad, pero cuando vi el resultado me supo a gloria, un 91%. ¡Objetivo superado!

Poco más que añadir, si os decidís a pasar por el proceso, ánimos y mucha suerte. Podéis dejar un comentario y preguntar cualquier cosa, intentaré ayudaros en la medida que me sea posible.

Telescoping constructor anti-pattern & Builder pattern

Cuando nuestros objetos crecen, no sólo los métodos equals() y hashCode() pueden volverse inmanejables, los constructores también pueden volverse un arma de doble filo. Instanciar un objeto con tres o cuatro parámetros es sencillo mediante su correspondiente constructor, en cambio, con objetos con un número mayor de propiedades la misma solución se vuelve poco práctica.

Vamos a ver un ejemplo práctico, imaginar que decidimos crear nuestra propia clase para manejar fechas. Nuestra clase, a la que llamaremos ‘Date’, contará con siete propiedades tal que así:


public class Date {

	private int year;

	private int month;

	private int day;

	private int hour;

	private int minute;

	private int second;

	private int millisecond;

}

¿Cómo queremos que los objetos de nuestra clase ‘Date’ sean creados?

Algo habitual podría ser el crear constructores para los casos más habituales, algo de este estilo:


	// Creamos un constructor para las combinaciones
	// más habituales
	public Date(int year, int month, int day) {
		this(year, month, day, 0, 0, 0, 0);
	}

	public Date(int year, int month, int day, int hour) {
		this(year, month, day, hour, 0, 0, 0);
	}

	public Date(int year, int month, int day, int hour,
			int minute) {
		this(year, month, day, hour, minute, 0, 0);
	}

	public Date(int year, int month, int day, int hour,
			int minute, int second) {
		this(year, month, day, hour, minute, second, 0);
	}

	public Date(int year, int month, int day, int hour,
			int minute, int second,int millisecond) {
		super();
		this.year = year;
		this.month = month;
		this.day = day;
		this.hour = hour;
		this.minute = minute;
		this.second = second;
		this.millisecond = millisecond;
	}

De esta manera tendríamos cubiertos los casos más típicos para esta clase y a la hora de instanciar una clase en nuestro IDE nos aparecería algo así:

Date01

Por otro lado, podríamos incluso ignorar completamente los constructores dejando únicamente el que se genera por defecto y crear los objetos a partir de los métodos ‘get’ y ‘set’ tal que así:


	Date date = new Date();
	date.setYear(2013);
	date.setMonth(8);
	date.setDay(4);
	date.setHour(0);
	date.setMinute(0);
	date.setSecond(0);
	date.setMillisecond(0);

¿Qué desventajas suponen estas dos alternativas?

La primera tiene nombre propio y se conoce como ‘Telescoping constructor anti-pattern‘, sí, es una antipatrón, una de esas soluciones que a priori puede parecer buena pero que a la larga se vuelve en tu contra. Imagina que en lugar de siete propiedades tienes quince, o peor aún, piensa que esas quince propiedades no siguen ningún orden más o menos lógico y te toca ir creando tantos constructores como combinaciones posibles de propiedades, un infierno. Por otro lado, piensa que la sobrecarga en Java tiene ciertos límites y según que combinaciones de propiedades y tipos no podrás repetirlas más de una vez, por ejemplo:


	public Date(int year, int month, int day, int hour) {
		this(year, month, day, hour, 0, 0, 0);
	}

	public Date(int year, int month, int day, int minute) {
		this(year, month, day, 0, minute, 0, 0);
	}

	public Date(int year, int month, int day, int second) {
		this(year, month, day, 0, 0, second, 0);
	}

	public Date(int year, int month, int day, int milisecond) {
		this(year, month, day, 0, 0, 0, milisecond);
	}

Estos cuatros constructores no podrían coexistir al mismo tiempo, los cuatro reciben el mismo número y tipo de parámetros , el compilador no podría deducir cual intentas invocar.

La otra alternativa sería la típica estrategia que se aplica con los JavaBeans, como tal, hereda todas sus desventajas. Es muy común instanciar clases sin haber asignado todas las propiedades necesarias o con estados inválidos, el ejemplo más típico sería crear accidentalmente instancias de nuestra clase ‘Date’ con valores ‘0’ en año, mes o día porque hemos olvidado llamar al método ‘set’ que toca. ¿Quién no ha copiado / pegado estas asignaciones y ha generado algún ‘bug’ que luego ha estado molestando? Por otro lado, esta alternativa elimina la posibilidad de hacer la clase inmutable, algo tan común en nuestros querida clase ‘String‘ y que en según que situaciones puede venirnos muy bien.

¿Cómo solucionamos esta situación?

Para estos casos tenemos el ‘Builder pattern‘, vamos a verlo en acción con nuestra clase ‘Date’:


/**
 * Builder pattern.
 */
public class Date {

	// Todas las propiedades son 'final'
	// por lo tanto nuestro objeto es inmutable.
	private final int year;

	private final int month;

	private final int day;

	private final int hour;

	private final int minute;

	private final int second;

	private final int millisecond;

	// Creamos una clase interna 'Builder'
	public static class Builder {

		// Las propiedades obligatorias se declaran
		// 'final'
		private final int year;

		private final int month;

		private final int day;

		// Las propiedades opcionales se inicializan
		private int hour = 0;

		private int minute = 0;

		private int second = 0;

		private int millisecond = 0;

		// Pasaremos las propiedades obligatorias en el
		// constructor del 'Builder'
		public Builder(int year, int month, int day) {
			this.year = year;
			this.month = month;
			this.day = day;
		}

		// Crearemos un método público para cada una de
		// las propiedades opcionales, éstos se devolverán
		// a sí mismos ('this') para poder encadenar las
		// llamadas
		public Builder withHour(int hour) {
			this.hour = hour;
			return this;
		}

		public Builder withMinute(int minute) {
			this.minute = minute;
			return this;
		}

		public Builder withSecond(int second) {
			this.second = second;
			return this;
		}

		public Builder withMillisecond(int millisecond) {
			this.millisecond = millisecond;
			return this;
		}

		// Este método permitará crear el objeto 'Date'
		// resultante
		public Date build() {
			return new Date(this);
		}
	}

	// Sólo habrá un constructor privado para forzar
	// la creación de los objetos a partir del 'Builder'
	private Date(Builder builder) {
		this.year = builder.year;
		this.month = builder.month;
		this.day = builder.day;
		this.hour = builder.hour;
		this.minute = builder.minute;
		this.second = builder.second;
		this.millisecond = builder.millisecond;
	}

	// Omitimos los siete métodos 'get'
}

Veamos algunos ejemplos de como instanciar ahora esta clase:


	Date date01 = new Date.Builder(2013, 8, 4)
		.build();

	Date date02 = new Date.Builder(2013, 8, 4)
		.withHour(4)
		.build();

	Date date03 = new Date.Builder(2013, 8, 4)
		.withHour(5)
		.withMinute(15)
		.build();

	Date date04 = new Date.Builder(2013, 8, 4)
		.withHour(6)
		.withMinute(30)
		.withSecond(6)
		.build();

	Date date05 = new Date.Builder(2013, 8, 4)
		.withHour(6)
		.withMinute(30)
		.withSecond(6)
		.withMillisecond(300)
		.build();

Aspectos a tener en cuenta:

  • Nuestra código es ahora mucho más limpio y legible, los métodos públicos dentro del ‘Builder’ nos permiten dar mucha más expresividad a sus nombres.
  • La instanciación del objeto se lleva a cabo en una sola linea, de esta manera evitamos estados inconsistentes.
  • Podemos encadenar sin orden específico las asignaciones de las distintas propiedades opcionales mediante las llamada a los métodos oportunos, tantas como sean necesarias.
  • Siempre terminamos la instanciación del objeto con el método ‘build()’, éste se encarga de crear el objeto mediante el constructor privado.
  • Tenemos facilidad para hacer inmutable nuestra clase.
  • Es aconsejable aplicar este patrón como mínimo teniendo cuatro o cinco propiedades, escala muy bien y es fácil añadir nuevas propiedades, sobre todo opcionales.

Pero todo no podían ser ventajas, tener que crear un ‘Builder’ a mano para cada una de nuestras entidades puede llegar a ser bastante lento y tedioso, al fin y al cabo, nuestro IDE genera de forma automática la mayoría de código para constructores, ‘getters’ y ‘setters’, necesitamos una forma eficaz de aplicar este patrón que mostraré en la siguiente entrada.

Libros técnicos, una buena razón para comprar un e-Book

ebooks

Hace unos tres años empecé a desplazarme a diario en transporte público (Renfe cercanías) para ir a trabajar a Barcelona. Como actualmente vivo en Terrassa,  tardo aproximadamente una hora y cuarto desde que salgo de casa hasta que llego a mi trabajo. Debido a que hago dos trayectos diarios, ida y vuelta, tranquilamente dedico dos horas y media cada día sólo en desplazamientos. Como voy a trabajar cinco veces a la semana, de lunes a viernes, al final gasto entre doce y trece horas semanales sólo en trayectos.

Al principio los viajes se hacen llevaderos sin necesidad de entretenerte con nada en concreto, puedes distraerte con el paisaje, pensando un poco, viendo a la gente que sube y baja, etc. Con el tiempo los viajes se vuelven muy monótonos así que necesitas distraerte con algo.  Hay muchas posibilidades: dormir, escuchar música, ver alguna serie/película vía smartphone o tablet, leer, etc. Al final acabas haciendo un poco de todo, en mi caso, me decanté por la lectura.

Siendo como soy y dedicándome a lo que me dedico, empecé a comprar algún que otro libro técnico en papel para sacar provecho a esas horas de trayecto. Cuando consultas estos libros en su versión digital, por ejemplo en PDF, te alegras de que sean bien completos y tengan un buen número de páginas con grandes ejemplos de código, diagramas, esquemas de bases de datos, etc. El problema viene cuando ves el libro en papel y te encuentras con un tocho considerable que tienes que mover de un lado para el otro. Tener que llevarte una maleta sólo para poder transportar el libro que te estas leyendo no es muy práctico.

Head_First_Servlets_Jsp

Ejemplo práctico, mi mano y uno de estos libros

El problema se agrava debido a que la mayoría de libros técnicos de referencia son verdaderos tochos, no cuesta demasiado encontrar un libro de 600, 700 o más páginas de carácter técnico, hay de todo, eso esta claro. Lamentablemente no se suelen hacer versiones de bolsillo así que toca lidiar con su tamaño.

Debido a lo expuesto, me he decidido a comprar un e-Book. De un plumazo se solventan todos los problemas de movilidad, ya puede ser largo el libro, la ventaja de lo digital es evidente en este aspecto. Pero claro, no todo podían ser ventajas, resulta que no es lo mismo leer la versión e-Book de una novela donde mayormente sólo hay texto que un libro técnico donde hay multitud de ejemplos de código. Las novelas son fácilmente adaptables a los distintos formatos existentes para e-Book pero los libros técnicos pueden resultar una pesadilla para según que tamaños de pantalla y necesitan de una edición cuidadosa. Si bien puedes encontrar libros técnicos apropiadamente formateados en editoriales como Manning u O’Reilly, todavía no es del todo ‘habitual’ que sea así.

Y en esta situación me encuentro, leer la versión en PDF es horrible en dispositivos tipo Kindle Paperwhite o Sony PRS-T2, se hacen necesarias pantallas más grandes, tipo tablet. Precisamente es en tablets donde he leído buenas críticas a la hora de leer las versiones en PDF, lo que pasa es que no estoy dispuesto a comprar algo tipo iPad, iPad mini, Nexus 7 sólo para leer libros, me niego, por muchos otros usos que puedan tener. La diferencia de precio entre tablets y e-books no me convence.

En definitiva, pese a las claras desventajas que puedes encontrarte en las ediciones digitales de los libros técnicos, el tema de la movilidad tiene un mayor peso para mí, sólo por esto me decantaré por un e-Book, aunque no sé todavía cual, tengo que acabar de decidirme y seguramente escribiré alguna entrada más sobre estos temas.

¿Qué opináis vosotros?

Mi experiencia con Pepephone (I)

logo_pepephone

Hace algo más de año y medio decidí probar suerte con los Operadores Móviles Virtuales (OMVs), más concretamente con Pepephone. Cansado de lidiar con las torpezas de Vodafone y viendo las buenas críticas que este OMV estaba cosechando me decidí a dar el salto.  A día de hoy puedo decir que fue una decisión muy acertada para mí.

¿Por qué me decidí a cambiar?

Sentía que Vodafone me estaba tomando el pelo. Estaba en los últimos 6 meses de permanencia y era un buen momento para volver a hacer un amago de portabilidad, sin embargo, no lo veía claro. Las permanencias de 24 meses se estaban empezando a instaurar y no me parecía nada apropiado atarme con Vodafone durante todo ese tiempo, si 18 meses ya me parecía una eternidad, las nuevas permanencias me parecían una aberración contra el consumidor.

A más a más, recientemente había sufrido uno de los famosos errores de facturación de Vodafone con el descuento que obtuve del último amago. Durante 6 meses me estuvieron facturando 20€ de la tarifa @XS y a la vez 9€ de consumo mínimo en la línea, un despropósito con el que cada mes tenía que lidiar para poder reclamar lo facturado de más.

El problema no era tanto el dinero sino la frustración e impotencia que uno siente al verse obligado a pasar por su servicio de atención al cliente mes a mes porque no aprenden, harto de esta situación di por perdido un mes, tenía 6 facturas erróneas y 5 abonos, me faltaba uno y no había manera de hacérselo entender a ninguno de sus operadores (tener que hacerlo por teléfono tampoco ayudaba demasiado). Era hora de buscar alternativas.

¿Por qué Pepephone?

La verdad es que nunca me había llegado a cambiar de compañía, siempre había jugado con la baza de los amagos de portabilidad y siempre había conseguido algo interesante en forma de terminal y/o descuento en tarifa durante varios meses. Debido a esto, una de las principales razones para escoger a Pepephone fue que al fin y al cabo iba a seguir usando la misma red (para el que no lo sepa, Pepephone utiliza la red de Vodafone). De esta manera reducía el riesgo de sufrir algún problema de cobertura con el cambio de compañía, en el peor de los casos, como no asumía ningún compromiso de permanencia con Pepephone siempre podía dar marcha atrás, sin permanencia sería libre para cambiar de compañía.

Pero seamos francos, la principal razón del cambio fue un tema económico, desde que estoy con ellos mi gasto mensual ronda los 6€ con tarifa de datos incluida, aquí la prueba:

1 año de facturas de pepehone

Algunas de mis facturas en Pepephone

¿Dónde está la trampa? Realmente no hay trampa como tal, tengo un hábito de consumo muy concreto, conozco cual es y he escogido la tarifa que mejor se complementa con ese hábito, de esa manera he conseguido un gasto óptimo.

Me sucede a menudo que cuando recomiendo a alguien que se mire las tarifas que ofrece Pepephone, esa persona entra en la web y se mira el apartado correspondiente donde sólo encuentra 2 o 3 tarifas distintas, más tarde vuelve a mí confuso por no ser capaz de encontrar las que yo suelo mencionar. No les falta razón, en la web no hay apenas información sobre las mismas, la gracia del asunto está en que Pepephone se dedica a crear tarifas específicas para productos, servicios y comunidades concretas. De esa manera cada portal / comunidad se encarga de promocionar su tarifa personalizada, el problema es que si no conoces esa comunidad su oferta queda ‘oculta’ para ti.

¿Donde podemos ver todas las tarifas juntas? En el subforo de Pepephone de GSMspain. Allí encontraremos a un usuario que periódicamente recopila y actualiza las distintas tarifas del operador, más concretamente en el hilo llamado  “Recopilación y Comparador de Tarifas Pepephone (v.XX)” . Tener en cuenta que con el tiempo ese hilo dejará de estar vigente puesto que el usuario va creando nuevas versiones del mismo a medidas que las tarifas evolucionan, sólo tenéis que buscar la versión más actual en el mismo subforo.

Sería muy tedioso describir cada una de las tarifas que hay en ese hilo, os invito a leéroslo con calma e incluso a plantear vuestras preguntas en el mismo hilo, hay mucha gente dispuesta a ayudar.

¿Qué he ganado?

No hay duda alguna, he ganado más de lo que he ‘perdido’, veamos algunos ejemplos que yo valoro:

  • Adiós a la distinción entre clientes ‘nuevos’ y ‘viejos’, todos tenemos las mismas condiciones. No hay cosa que más odie que ver como mi compañía se baja los pantalones de forma sistemática para atraer nuevos clientes y deja al resto en el olvido o con programas de fidelización patéticos (véase el canje de puntos de ONO, ‘patético’ es ser muy políticamente correcto).
  • Mejoras de tarifas automáticas para todos los clientes, si tu tarifa mejora Pepephone te lo notificará y te lo aplicará de forma automática. Nada de tener que llamar para suplicar que te apliquen los nuevos y mejorados precios.
  • Adiós a las permanencias, si un día Pepephone no me convence soy libre de ir donde quiera.
  • Capacidad para reconocer errores y rectificarlos, en Vodafone siempre tenía la sensación de que me estaban perdonando la vida al realizar alguna reclamación.

¿Qué he perdido?

No todo puede ser positivo:

  • Se acabaron los móviles ‘gratis’,  la compra y mantenimiento de tus terminales debes gestionarla tu mismo, no se puede tener todo.
  • Las tarifas de datos no son ‘ilimitadas’, si te pasas del tope establecido te cobrarán a razón de 3 céntimos el MB. Tienes formas de establecer avisos o de incluso cortar la conexión a través de la web pero siempre era más cómodo simplemente despreocuparte de esto y que te rebajaran la velocidad al pasarte del tope.

En definitiva, Pepephone y el resto de OMVs son una alternativa real a las grandes compañías que hasta hace poco monopolizaban el mercado, recomiendo encarecidamente que empecéis a tenerlas en cuenta. Como  la entrada se alarga y no quiero hacerme pesado, dedicaré otra a dar algunas directrices para detectar vuestro hábito de consumo y escoger una tarifa adecuada (usaré mi tarifa actual como ejemplo). ¡Estad atentos!

EqualsBuilder & HashCodeBuilder, mejorando lo visto

Siguiendo lo comentado en la entrada anterior, cuando nuestras clases empiezan a acumular un buen número de propiedades, generar de forma automática el método ‘equals()’ engendra un monstruo.

Si bien podemos refactorizar e intentar maquillar el asunto, lo más probable es que caigamos en lo que se conoce como un antipatrón, y más concretamente acabaremos reinventando la rueda cuadrada. ¿Qué significa esto? Pues que gastaremos tiempo en hacer algo que ya se ha hecho con anterioridad y seguramente nuestra solución sea ‘peor’ que la que ya existe.

Esta situación ocurre a diario, cuando estamos aprendiendo nos viene bien para ejercitar un poco el coco pero cuando tenemos plazos de entrega, hay que ser práctico. En muchas ocasiones me sorprendo a mi mismo uniendo piezas ajenas para llevar a cabo pequeñas funcionalidades, esto no es malo mientras entiendas lo que estas haciendo, de lo contrario la cosa puede degenerar en una variante de la programación basada en el copiar/pegar (Otro antipatrón).

Pero no nos desviemos del tema, necesitamos encontrar una solución para nuestro problema con los ‘equals()’. Las preguntas clave que hay que hacerse son:

  1. ¿Soy el primero en enfrentarme a este problema? (Probablemente no)
  2. ¿Habrá alguna solución ya existente para este problema? (Probablemente sí)

Es aquí donde entra en juego la fundación Apache y sus archiconocidas librerías Apache Commons, sin entrar en grandes detalles, estas librerías te harán la vida más fácil como programador java, poco a poco iremos examinándolas con más detalle.

Imagen

Concretamente para este caso, las clases EqualsBuilder y HashCodeBuilder de la librería Apache Commons Lang cubren nuestras necesidades. Vamos a verlas en acción, comencemos por el método ‘equals()’ de la clase ‘Persona’ utilizada en la entrada anterior:


	@Override
	public boolean equals(Object obj) {

		if (this == obj) {
			return true;
		}

		if (obj == null) {
			return false;
		}

		if (!(obj instanceof Persona)) {
			return false;
		}

		Persona other = (Persona) obj;

		EqualsBuilder eBuilder = new EqualsBuilder();

		eBuilder = eBuilder.append(this.dni, other.dni);
		eBuilder = eBuilder.append(this.edad, other.edad);
		eBuilder = eBuilder.append(this.nombre, other.nombre);

		return eBuilder.isEquals();
	}

La mejora en cuanto a legibilidad y mantenibilidad es muy notable, veamos si sucede igual con el método ‘hashCode()’:


	@Override
	public int hashCode() {

		// Creamos una instancia, el primer valor es para la
		// inicialización, el segundo el número primo para las
		// multiplicaciones
		HashCodeBuilder hcBuilder = new HashCodeBuilder(17, 31);

		hcBuilder.append(this.dni);
		hcBuilder.append(this.edad);
		hcBuilder.append(this.nombre);

		return hcBuilder.toHashCode();
	}

La respuesta es afirmativa, el código se simplifica a costa de añadir una libería (‘jar’) a nuestro proyecto. Además podemos estar tranquilos en lo que se refiere a su implementación, el método ‘hashCode()’ esta hecho siguiendo las directrices de Effective Java (Igual este libro es interesante :)).

No he comentado nada en relación a como añadir la librería, todo depende de como gestionemos las dependencias en nuestro proyecto, para este ejemplo no me he complicado en exceso y he añadido el ‘jar’ de la librería (‘commons-lang3-3.1.jar’) al Classpath del proyecto, con Maven añadiríamos la dependencia en el POM de nuestro proyecto (Un tema interesante para una futura entrada).

Entendiendo la relación entre ‘Equals()’ & ‘HashCode()’

Recientemente he empezado a leer Effective Java de Joshua Bloch, una lectura casi obligada en el mundo Java para empezar a escribir código eficiente. De entre los muchos temas que en el libro se tratan, hoy me ha parecido conveniente hablar de la estrecha relación que existe entre los métodos ‘equals()’ y ‘hashCode()’.

Tarde o temprano llega el momento en el que necesitas comparar tus objetos entre sí para determinar si son iguales o no, para ello, sobrescribir el método ‘equals()’ es el camino a seguir. Ocurre sin embargo, ya sea por desconocimiento o por pereza, que la mayoría de veces olvidamos implementar también el método ‘hashCode()’ contribuyendo así, y sin saberlo, a la proliferación de pequeños y molestos ‘bugs’ en nuestro código.

Veamos un ejemplo sencillo, una clase que representa el concepto ‘Persona’ con tres propiedades y un constructor:

public class Persona {

	String dni;
	String nombre;
	int edad;

	public Persona(String dni, String nombre, int edad) {
		super();
		this.dni = dni;
		this.nombre = nombre;
		this.edad = edad;
	}
}

Pongamos que decidimos que dos objetos ‘Persona’ son iguales si sus tres propiedades son iguales, con esto en mente nos servimos de la funcionalidad de nuestro IDE (Ecplise en mi caso) para generar automáticamente el método ‘equals()’ para esas propiedades, algo que quedaría similar a lo siguiente:

@Override
public boolean equals(Object obj) {

	if (this == obj) {
		return true;
	}

	if (obj == null) {
		return false;
	}

	if (!(obj instanceof Persona)) {
		return false;
	}

	Persona other = (Persona) obj;

	if (dni == null) {
		if (other.dni != null)
			return false;
	}
	else if (!dni.equals(other.dni)) {
		return false;
	}

	if (edad != other.edad) {
		return false;
	}

	if (nombre == null) {
		if (other.nombre != null)
			return false;
	}
	else if (!nombre.equals(other.nombre)) {
		return false;
	}

	return true;
}

En este punto tenemos una implementación propia para el ‘equals()’ y la implementación por defecto de ‘hashCode()’ que nos ofrece la clase Object. Dicho lo cual, vamos a ejecutar una pequeña prueba y analizaremos el resultado:

El test:

public class MainTest {
	public static void main(String[] args) {
		// Creamos dos objetos persona distintos con los mismos datos
		Persona persona1 = new Persona("00000014Z", "Oscar", 26);
		Persona persona2 = new Persona("00000014Z", "Oscar", 26);

		// Mostramos sus HashCode
		System.out.println("HashCode persona1->'" + persona1.hashCode() + "'");
		System.out.println("HashCode persona2->'" + persona2.hashCode() + "'");

		// Los comparamos
		System.out.println("¿Son iguales? resultado->'" + persona1.equals(persona2) + "'");

		// Creamos un pequeño HashMap para almacenar personas
		Map<Persona, String> map = new HashMap<Persona, String>();

		// Añadimos persona1 al HashMap
		map.put(persona1, "¡Visita RizandoElRizo.com!");

		// Comprobamos si el HashMap contiene a persona2, si los dos objetos son
		// iguales así debería ser.
		System.out.println("¿El HashMap contiene a persona2? resultado->'" + map.containsKey(persona2) + "'");
	}
}

El resultado:

HashCode persona1->'11394033'
HashCode persona2->'4384790'
¿Son iguales? resultado->'true'
¿El HashMap contiene a persona2? resultado->'false'

¿Qué está pasando aquí? persona1 y persona2 tienen ‘hashcodes’ distintos, parece ser que son ‘iguales’ pero no podemos usar uno para encontrar al otro dentro de un HashMap aún teniendo los mismos datos. El problema es sencillo, al sobrescribir nuestro ‘equals()’ hemos ignorado una de las clausulas del contrato del método ‘hashCode()’, si consultamos su javadoc veremos algo del estilo:

“If two objects are equal according to the equals(Object) method, then calling the hashCode method on each of the two objects must produce the same integer result.”

No hay duda, si queremos cumplir esta clausula siempre que implementemos uno deberíamos implementar el otro también. Hay que tener en cuenta que el método Object.hashCode() devuelve un entero relacionado con la dirección de memoria del objeto en cuestión, por lo tanto, no nos sirve. La siguiente pregunta es bastante obvia:  ¿Cómo creamos un ‘hashCode()’ de forma adecuada? Seamos prácticos y dejemos que nuestro IDE haga el trabajo por nosotros:

	@Override
	public int hashCode() {

		// Escogemos un número primo para los cálculos, tradicionalmente el 31
		final int prime = 31;

		// Inicializamos el resultado con un valor distinto de 0 para minimizar
		// las colisiones
		int result = 17;

		// Para cada una de las propiedades a tratar:
		// - Calculamos su hashCode en función de su tipo.
		// - Aplicamos la formula: 'resultado = numPrimo * resultado + hashCodePropiedad'.
		result = prime * result + ((dni == null) ? 0 : dni.hashCode());
		result = prime * result + edad;
		result = prime * result + ((nombre == null) ? 0 : nombre.hashCode());

		return result;
	}

Eclipse lo ha generado siguiendo las directrices que aparecen en Effective Java (Item 9), ciertamente no es casualidad. Si ahora volvemos a ejecutar el test obtenemos el resultado esperado:

HashCode persona1->'1143596956'
 HashCode persona2->'1143596956'
 ¿Son iguales? resultado->'true'
 ¿El HashMap contiene a persona2? resultado->'true'

Generar un ‘hashcode’ de forma eficiente no es algo trivial, el objetivo aquí es tener una idea clara de como funciona el asunto y salir del paso con una solución decente para la mayoría de casos.

En conclusión, al final uno siempre tiene que tener en cuenta lo siguiente cuanto trate con estos métodos:

  • Siempre que implementes ‘equals()’ debes implementar ‘hashCode()’.
  • Ambos métodos deben tratar los mismos atributos, de lo contrario perderás fácilmente la consistencia entre ambos.
  • Si dos objetos son iguales, su’ hashcode’ también lo debe ser.
  • Hay que tener en cuenta que dos objetos pueden tener el mismo ‘hashcode’ y ser diferentes a la vez, distintas combinaciones de propiedades podrían originar el mismo valor, el cálculo no garantiza un valor único.
  • La mayoría de Colecciones de Java basadas en ‘Hashes’ utilizan el método ‘hashCode()’ para realizar comparaciones de forma eficiente, si no lo implementas bien, tendrás problemas como el visto aquí.
  • Consulta la documentación de ambos métodos para tener claro sus características: Object.equals() y Object.hashCode()

Pero todavía podemos mejorar esto, ¿Qué ocurre si en nuestras clases tenemos un número importante de propiedades? Sencillo, el método ‘equals()’ se vuelve una monstruosidad, con tres propiedades hemos generado un método con 8 o 9 sentencias ‘if’, si tenemos 15 propiedades, haz números. Para solventar esto, la fundación Apache acude al rescate con la librería Apache Commons y las clases HashCodeBuilder y EqualsBuilder, tema central de mi próxima entrada. 🙂

Novedades Java SE 7: ‘Strings’ en sentencias ‘Switch’

¡Por fin! Ha hecho falta llegar hasta la séptima versión de Java (Java SE 7) pero finalmente ya tenemos aquí esta ‘ansiada’ característica.

Actualmente son muchos los lenguajes que incluyen esta funcionalidad o simplemente ya nacieron con ella, aun así, más vale tarde que nunca.

Vamos a imaginar un ejemplo sencillo, una pequeña función que nos devuelve un valor numérico en función de un color.

En Java SE 6 podríamos hacer algo parecido a esto:

public class StringSwitchTest {

	public static int getColorNumber(String color) {

		int out = 0;

		if (color == null) {
			return out;
		}

		if (color.equalsIgnoreCase("rojo")) {
			out = 1;
		}
		else if (color.equalsIgnoreCase("amarillo")) {
			out = 2;
		}
		else if (color.equalsIgnoreCase("verde")) {
			out = 3;
		}

		return out;
	}

	public static void main(String[] args) {

		String color = "verde";
		int colorNumber =
			StringSwitchTest.getColorNumber(color);

		System.out.print("Color->'" +
			color + "' y Valor->'" + colorNumber + "'.");

	}
}

Ahora en cambio, en Java SE 7, podremos hacer lo mismo mediante el uso de ‘Strings’ en un ‘switch’:

public class StringSwitchTest {

	public static int getColorNumberWithSwitch(String color) {

		int out = 0;

		if (color == null) {
			return out;
		}

		switch (color.toLowerCase()) {
			case "rojo":
				out = 1;
				break;

			case "amarillo":
				out = 2;
				break;

			case "verde":
				out = 3;
				break;

			default:
				out = 0;
				break;
		}

		return out;
	}

	public static void main(String[] args) {

		String color = "verde";
		int colorNumber =
			StringSwitchTest.getColorNumberWithSwitch(color);

		System.out.print("Color->'" +
			color + "' y Valor->'" + colorNumber + "'.");

	}
}

Cosas a tener en cuenta:

  1. Las comparaciones dentro del ‘switch’ se llevan a cabo mediante el método ‘equals’ correspondiente, de ahí a que en el segundo ejemplo se invoque al método ‘toLowerCase’ antes de realizar la comparación para evitar problemas de mayúsculas y minúsculas.
  2. Conviene comprobar a priori la existencia de valores ‘null’, de lo contrario, obtendríamos una preciosa excepción de tipo ‘NullPointerException’ al intentar realizar la comparación, un clásico.

Al final, el uso de la sentencia ‘switch’ frente al ‘if – else’ puede convertirse en una mera cuestión de legibilidad de código, siempre me ha parecido más elegante el empleo del primero cuando se acumulan muchas condiciones, otro tema a tener en cuenta sería la complejidad ciclomática, algo que trataré en futuras entradas.