blog de ezamudio

Java 8: mis primeros tropiezos

Pues ahora que ya salió oficialmente Java 8, lo instalé y empecé a hacer pruebas, con un proyecto grande que ya llevo desde hace varios años (empezó en Java 1.4 y lo he migrado a Java 5, 6, 7 y ahora seguirá la 8).

Cuando he migrado, las etapas suelen ser así:

Primero correr las aplicaciones tal cual están, sin modificar nada, sobre la nueva JVM. Eso parece que está funcionando bien (pero tendrán que estar en observación varios días obviamente).

Luego, compilar el código tal cual está, sin modificar nada, con el nuevo compilador. Aquí es donde me di de topes ya desde ahorita. Pasaron dos cosas:

Utilizo Lombok para reducir código en varios lugares y facilitarme la vida en general cuando programo en Java. Una de las cosas que trae es la anotación @SneakyThrows, que permite tratar una excepción verificada como si fuera RuntimeException al momento de compilar. Pero al compilar código que utiliza esta anotación en Java 8:

post-compiler 'lombok.bytecode.SneakyThrowsRemover' caused an exception: null

Y esto es usando la versión 1.12.6 que se supone ya trae soporte para Java 8.

Clases internas en Groovy

Acabo de toparme con un problema en Groovy. Tristemente la solución que todo mundo da en foros es simplemente "no lo hagas, es mejor si haces [cualquier otra solución]"; pero, qué tal si lo que necesitaba era esto?

Bueno y ¿cuál es el problema? Es simple: el soporte en clases internas tiene problemas con atributos heredados en la clase externa. Tiene solución, y es sencilla, pero me parece una leaky abstraction y honestamente la encontré por pura suerte, buscando maneras de darle vuelta al asunto.

El problema se da bajo estas circunstancias:

//Una clase padre, normalita, con un atributo normalito
class Parent {
  String name
}
//Una subclase, normalita, con una clase interna
class External extends Parent {
  //Un atributo propio de esta clase
  String outer
  //Y esta es la clase interna
  class Internal {
    //También tiene un atributo propio
    String inner
    //Este método truena como ejote
    void boom() {
      //Esta línea se ejecuta bien, se despliega el atributo externo y el interno
      println "Outer is " + outer + ", inner is " + inner
      //Pero esta línea truena con un error macabro,

Web Services en Axis2 con Spring y Gradle

Recientemente en un proyecto un cliente nos dijo que la interfaz entre nuestros sistemas sería... esperen... sí... un web service. Un web service que ellos invocarían y que por lo tanto nosotros tenemos que implementar. Así que nos dieron un par de archivos WSDL y unos ejemplos de XML para asegurarnos que salga como debe ser (no entiendo para qué, si todas las herramientas de generación de web services se encargan de que el XML siempre salga como debe ser, ¿verdad?).

Cuando tengo que invocar un web service, generalmente uso Axis2. Me ha resultado de lo más sencillo porque consiste simplemente en ejecutar el script wsdl2java dándole el URI del WSDL y con eso tengo una clase que puedo usar para invocar los servicios (junto con mil clases internas que envuelven los datos dentro de envoltorios intermedios porque parece que los de Axis entendieron OOP como Onion-Oriented Programming). Así que en esta ocasión decidí probar el otro lado de la implementación: publicar un servicio hecho con Axis2.

Comparativo de desempeño para formato de fechas

Comparto esto para los que tengan la misma duda que yo tenía: Saber si es más rápido usar String.format o un SimpleDateFormat para formatear una fecha.

Es claro que String.format puede ser más simple de codificar, sobre todo en ambientes concurrentes, ya que SimpleDateFormat no es thread-safe y por tanto hay que usarlo solamente de manera local o bien envolverlo en un ThreadLocal.

Pero pues hice un programa muy simple en Groovy para comparar el performance de ambos:

import java.text.SimpleDateFormat

SimpleDateFormat df = new SimpleDateFormat('yyyyMMddHHmmss')
Date now = new Date()
//calentamiento
10000.times {println df.format(now)}
10000.times {println String.format('%tY%<tm%<td%<tH%<tM%<tS', now)}
System.currentTimeMillis()

long t0=System.currentTimeMillis()
10000.times {
    df.format(now)
}
long t1 =System.currentTimeMillis()
10000.times {
    String.format('%tY%<tm%<td%<tH%<tM%<tS', now)
}
long t2 = System.currentTimeMillis()
println "SimpleDateFormat: ${t1-t0} vs String.format: ${t2-t1}"

Game Of Life en Ceylon

En el Code Retreat que hubo en Agosto de 2012, el reto fue implementar el famoso Game of Life, en parejas. En mi equipo, @juwe y yo lo desarrollamos en Ceylon (no me impuse; él se animó, jejej).

Apenas hoy lo pude subir a un repositorio que tenemos en GitHub especialmente para ejemplos de Ceylon, después de darle una buena actualizada al código porque han habido varios cambios en la sintaxis y en el módulo de lenguaje desde que escribimos esto originalmente: ya teníamos comprensiones, funciones de orden superior, funciones anónimas y varias otras cosas, pero no teníamos por ejemplo el assert, tuplas, ni la sintaxis actual para secuencias y colecciones iterables; la sintaxis para funciones anónimas cambió ligeramente, al igual que la de parámetros variádicos (los famosos varargs).

Migrar de Subversion a Git, sin mirar atrás

Este post no es realmente acerca de Java, pero quiero dejarlo en este sitio de todas maneras, ya que realmente no tengo otro blog y además no deja de ser algo muy técnico.

En mi trabajo desde hace varios años utilizamos Subversion, pero decidimos pasarnos a Git. El mismo Git incluye herramientas para integración con svn, pero son algo limitadas. Además creo que es un buen pretexto para aprovechar y reorganizar el repositorio, partiéndolo en varios repositorios distintos, abandonando algunas cosas obsoletas, ramas que llevan años abandonadas, etc.

En mi búsqueda por lograr esto, me encontré con esta herramienta llamada svn2git:

http://www.gitorious.org/svn2git

Parece que la desarrolló el equipo de KDE, para realizar lo mismo que yo estaba buscando: migrarse de una buena vez de svn a git, sin mirar atrás, reorganizando su código en varios repositorios, etc.

Esta herramienta está hecha en C++ y requiere Qt para poderse compilar, aunque no tiene interfaz gráfica. Debe ejecutarse en el servidor que contiene el repositorio de Subversion que se quiere migrar (no una copia de trabajo).

Comprehensiones en Ceylon

Las comprehensiones (o algo que se les parezca) son una característica ya prácticamente obligatoria en los nuevos lenguajes, al menos si quieren ser considerados cool. Y Ceylon no se queda atrás.

Las comprehensiones en Ceylon funcionan usando una variante especial de for, la cual acepta una sola expresión, en vez de un bloque de código. La expresión puede ser a su vez otro for, o bien un if, que actúa como un filtro, o cualquier otra expresión que devuelva algún valor. Todo eso termina siendo azúcar sintáctica para crear y devolver un objeto Iterable que se puede pasar en invocaciones a métodos o funciones que esperan argumentos secuenciados (de los que terminan con elípsis), o usarse directamente en cualquier lugar donde se pueda usar un Iterable.

Esto a fin de cuentas puede ser más poderoso que tener métodos como map y filter en las colecciones, y además permite hacer implementaciones más eficientes, ya que los iteradores intermedios involucrados se van utilizando conforme se necesitan, en vez de tener que procesar completamente una colección antes de pasar a la siguiente fase.

Si usan Groovy o Scala, puede que ya estén familiarizados con algunas de estas operaciones. Por ejemplo, tomar una lista de palabras y devolverlas en reversa:

["hola", "mundo", "javaMexico", "Ceylon"].collect { it.reversed() } //Groovy

List("hola", "mundo", "javaMexico", "Ceylon").map { _.reverse } //Scala

for (w in { "hola", "mundo", "javaMexico", "Ceylon" }) w.reversed; //Ceylon

Funciones en Ceylon, segunda parte

En mi post anterior, hablé acerca de las funciones de orden superior, la manera en que se pasan referencias a métodos, cómo se invocan, etc. Pues bien, ha habido bastante progreso en Ceylon, en varias áreas, y una de las que considero importantes es precisamente el manejo de funciones; algunas de las cosas que mencioné en ese post han cambiado, mientras que ha surgido funcionalidad nueva que no estaba disponible previamente. Así que veamos los cambios:

Callables

Primero que nada, ya no tenemos la restricción de que un Callable no podía ser invocado; ahora ya se puede, por lo que esto ya es válido:

value obj = MiClase();
Callable<String,Integer> f = obj.metodo;
String s = f(1);

La otra forma sigue siendo igual de válida:

value obj = MiClase();
//Aqui usamos inferencia de tipos
function f1(Integer x) = obj.metodo;
//Aqui ya lo definimos de manera estática
String f2(Integer x) = obj.metodo;
String s1 = f1(1);
String s2 = f2(2);

Argumentos por nombre

En Ceylon hay dos formas de hacer una invocación: usando argumentos posicionales (que es la manera en que todo mundo está acostumbrado a hacerlo) o usando argumentos nombrados (que no todos los lenguajes tienen y los que lo tienen, usan distintas sintaxis).

Ceylon M2

El segundo release de Ceylon, M2 aka "Minitel", ya está disponible.

Algunos de los cambios más importantes desde M1 que salió en diciembre, están:

  • Interoperabilidad con Java - ya se puede invocar código Java desde Ceylon, con algunas restricciones pero en general ya está funcionando.
  • Referencias a funciones y métodos - con esto hay bastante avance en lo que respecta a funciones de orden superior; ya se pueden pasar referencias a métodos y/o funciones como parámetros a otros métodos/funciones y también puede haber métodos/funciones que devuelvan Callables. Asimismo, se pueden definir métodos o funciones simplemente asignándoles una referencia a otro método o función que tenga la misma firma.
  • Performance - se optimizaron las operaciones aritméticas con Integer y Float, así como algunas operaciones que requieren boxing/unboxing (principalmente para interoperar con Java)
  • SDK más completo - El SDK ya incluye varias interfaces y clases para colecciones: Collection, Array, List, Map, Set, FixedSized, etc.

Funciones de orden superior en Ceylon

Ceylon tiene una característica que en lo personal me parece muy atractiva, algo que en otros lenguajes anuncian con gran fanfarria pero en Ceylon no se ha hecho tanto barullo. Es algo que creo que ayudará mucho para el diseño de APIs en el futuro cercano. Se trata de las funciones de orden superior.

Visto de manera simple, las funciones de orden superior son funciones que reciben otras funciones como parámetros, o bien que devuelven funciones como valor de retorno. El concepto realmente no es complejo; el truco en lenguajes de tipado estático está en que la sintaxis no sea compleja (en lenguajes dinámicos es muy sencillo pasar una referencia si no se tiene que indicar el tipo de retorno de la función, ni su número ni tipo de argumentos).

Una cosa muy necesaria para poder manejar funciones de orden superior en un lenguaje de tipado estático, es tener un tipo que las represente. En el caso de Ceylon, tenemos la clase Callable. Podemos usar este tipo para guardar la referencia a un método de un objeto por ejemplo. Suponiendo que tuviéramos una clase MiClase con un método metodo que reciba un entero y devuelva un string, entonces podemos hacer esto:

MiClase obj = MiClase();
Callable<String,Integer> f = obj.metodo;

Ojo, no es lo mismo obj.metodo() (lo cual es una invocación al método) que obj.metodo, que es simplemente obtener la referencia a ese método; a partir de entonces se puede manejar como una función.

Distribuir contenido