viernes, 25 de febrero de 2011

Implementación de un Comparator genérico en Java con ayuda de Groovy

JavaGroovy
Al programar en Java ¿no has tenido la necesidad de hacer unas cuantas clases que implementen la interfaz Comparator para realizar ordenaciones con el método Collections.sort pero no querías hacer una clase por cada una de ellas con unas pocas líneas? Cuando necesitas uno o dos Comparator puede valer hacer una clase para cada una de ellas pero cuanto empiezas a tener muchas la cosa empieza a exasperar por tener que hacer todo el rato lo mismo con un pequeño cambio, que es la expresión que obtiene los objetos que se comparan.

Para evitar tener que crear una clase por cada Comparator que queramos podemos hacer uso del soporte de Java 6 para los lenguajes de scripting (Consulta el enlace para ver como poder hacer uso de Groovy en Java) para crear un Comparator genérico. Básicamente la siguiente implementación de la interfaz Comparator, GroovyComparator, recibirá un script de groovy que lo aplicará a cada uno de los objetos del método compare de la interfaz Comparator y que devolverá un objeto que implememnte la interfaz Comparable por el que haremos la comparación entre los dos objetos.

package com.blogspot.elblogdepicodev.util;

import java.util.Comparator;
import java.util.HashMap;
import java.util.Map;

import javax.script.Bindings;
import javax.script.ScriptEngine;
import javax.script.ScriptEngineManager;
import javax.script.ScriptException;

public class GroovyComparator implements Comparator {

 private String script;
 private Map bindings;

 public GroovyComparator(String script) {
  this(script, null);
 }

 public GroovyComparator(String script, Map bindings) {
  this.script = script;
  this.bindings = bindings;
 }

 @SuppressWarnings({ "unchecked" })
 @Override
 public int compare(T o1, T o2) {
  try {
   ScriptEngineManager manager = new ScriptEngineManager();
   engine = manager.getEngineByName(Constantes.GROOVY_ENGINE_NAME);
   
   Bindings b = engine.createBindings();
   if (bindings != null) {
    b.putAll(bindings);
   }

   b.put("it", o1);
   Comparable r1 = (Comparable) engine.eval(script, b);

   b.put("it", o2);
   Comparable r2 = (Comparable) engine.eval(script, b);

   return r1.compareTo(r2);
  } catch (ScriptException e) {
   throw new RuntimeException(e);
  }
 }
}

Suponiendo que tengamos una lista de objetos con una propiedad nombre (String) y queramos ordenar la lista por esa propiedad:

List l = ...;
 ...
 Collections.sort(l, new GroovyComparator("it.nombre"));

La verdad es que utilizando los lenguajes de scripting para hacer ciertas tareas se nos puede hacer la vida más fácil a los programadores.

Referencia:
Lenguajes de scripting sobre la plataforma Java
Scripting for the Java Platform
The Mustang Meets the Rhino: Scripting in Java 6