Sobrecarga de operadores y otras monerías de Groovy
Recientemente he estado empezando a integrar código Groovy a aplicaciones que tengo hechas en Java. Esto implica que al final compilo las clases Groovy, y la aplicación Java sigo ejecutándola con java
, pero ahora tengo que agregar un par de bibliotecas (hasta la fecha, con Groovy 1.7.10, solamente tengo que agregar groovy-1.7.10.jar y asm-3.2.jar, no he tenido que agregar otras, y prefiero meter esas dos que groovy-all-1.7.10.jar porque este último trae varias cosas que podrían causarme conflictos en aplicaciones con muchas dependencias).
También he visto de qué manera puedo usar mejor mis clases Java desde Groovy. Y he descubierto que hay algunas monerías que se pueden implementar en Java, para simplificar el uso de algunas clases desde Groovy.
Por ejemplo, supongamos que tenemos una clase que entre otras cosas, tiene una lista la cual puede ser modificable desde fuera. Aquí pongo un ejemplo, de una clase Java muy simple (realmente es como un envoltorio de una lista, pero imagínense que además hace otras cosas):
//Esta clase está en Java
public class SoporteGroovy {
private ArrayList<Object> lista = new ArrayList<Object>();
//Este metodo es para obtener un elemento de la lista
public Object getAt(int idx) {
return lista.get(idx);
}
//Este metodo es para modificar la lista
public void putAt(int idx, Object value) {
lista.add(idx, value);
}
//Este metodo es también para modificar la lista
public void leftShift(Object value) {
lista.add(value);
}
//Esto es para la sobrecarga de operadores
public SoporteGroovy plus(Object other) {
if (other != null) lista.add(other);
return this;
}
public String toString() {
return String.format("Ejemplo soporte groovy con elementos %s", lista);
}
}
Los métodos getAt
y putAt
permiten hacer algo así en Groovy:
La asignación realmente invoca a putAt
y la siguiente línea invoca a getAt
. El otro método sirve en vez del típico add
:
Es cuestión de gustos y estética si les gusta esa sintaxis (tomada de C++) o no.
Ahora el método plus
, ese lo podemos usar así desde Groovy:
Con eso ya tenemos tres maneras de agregar objetos: llamando el método putAt
, usando el operador <<
, o usando el operador +=
.
Todavía podemos hacer más monerías... si hacemos que nuestra clase implemente Iterable
, podremos usar el famoso each
desde Groovy. Así que modificamos y agregamos un método a nuestra clase Java:
Y con eso ahora podemos hacer algo como esto:
Esto es porque Groovy permite que llamemos el each
en cualquier objeto; si la clase implementa iterator()
entonces se itera sobre ese iterador.
Y de hecho ahora que la clase es Iterable
, podemos también implementar una variante de plus
de manera que se sumen dos instancias de nuestra clase:
public SoporteGroovy plus(SoporteGroovy other) {
if (other == null) return this;
SoporteGroovy nuevo = new SoporteGroovy();
for (Object item : lista) {
nuevo.leftShift(item);
}
//Esto ya es posible porque la clase es Iterable
for (Object item : other) {
nuevo.leftShift(item);
}
return nuevo;
}
Y una vez que tengamos ese método, podremos hacer esto en Groovy:
println test3 //Nos debe imprimir que el objeto tiene los 5 elementos
Integración
Hoy en día, aún cuando implementamos clases en Java, hay que tomar en cuenta que existen otros lenguajes para la JVM. En el caso de Groovy, podemos seguir trabajando en Java pero el agregar algunos métodos como los que describí aquí, facilitan el uso de nuestras clases Java desde el lenguaje Groovy. Como autor de un par de proyectos de software libre en Java, esto me ha ayudado puesto que me permite simplificar el código que escribo en Groovy cuando utilizo dichos proyectos, aunque hayan sido hechos en Java, y agregar esos métodos a mis clases fue una tarea bastante simple.
- ezamudio's blog
- Inicie sesión o regístrese para enviar comentarios
Comentarios
Me agrada mucho, veo que la
Me agrada mucho, veo que la onda podria ser que se genere la estructura en java y la implementacion en Groovy para hacer un codigo todavia mas efectivo
Que buenos tips
Que buenos tips sobre como usar java con groovy.
Yo he usado groovy últimamente básicamente para hacer algunos scripts de soporte, pero me ha surgido la curiosidad de usar groovy para probar el código java y estos tips son muy utiles.
Esta super. Hay una lista de
Esta super. Hay una lista de todos los métodos que tienen estas conversiones? Por lo que veo en Groovy esta es una lista fija y predefinida así se pueden usar los métodos que en Java son ilegales.
A mi se me ocurre que hubiera una anotación ( por ejemplo @Alias ) con la cual pudieras decirle a tu clase en un lenguaje, que método es ese en otro lenguaje. Aún estoy por investigar sobre eso.
Lo del each, me surge una duda. Entonces, ¿el .each no es en realidad un método del objeto, sino más bien como un operador en forma de método? Eso está raro ¿ Que hace cuando le mandas .each a algo que no es in iterador ?
Si es un método,
Si es un método, revisa:
http://groovy.codehaus.org/groovy-jdk/java/lang/Object.html#each(groovy.lang.Closure)
http://groovy.codehaus.org/groovy-jdk/java/util/Map.html#each(groovy.lang.Closure)
No mandas un iterador al each, mandas un closure, y el closure recibe el elemento de la colección.
Por lo que veo, Groovy trata
Por lo que veo, Groovy trata de descomponer el objeto en partes que el dice que lo componen
por ejemplo podria lapractica decirnos que en Groovy el String es un arreglo de char y cuando le das el each te devuelve su elemento que cmpone el String y claro, no es directamente Iterable en java
Yo pienso que cuando no aplica e each devuelve el mismo objeto... segun mis pruebas:
aqui si que te hace la separacion:
cadena.each {c -> println c}
aqui no jala el each (porque nadamas es una posicion)
cadena.each {c -> println c}
bueno mas bien si jala pero no tiee sentido usarlo
Metaclass
Recuerda que en Groovy tienes la Metaclass con la que puedes agregar métodos en tiempo de ejecución a un objeto o a una clase. Para responder tu primera pregunta, sí hay una lista de los métodos que Groovy agrega a la clase Object. Eso mismo contesta la segunda pregunta:
each
es un método que TODOS los objetos en Groovy tienen (y algunas otras preguntas que tengan). La implementación default devuelve el resultado del métodoiterator()
que también todos los objetos tienen. La implementación default deiterator()
no es muy interesante, devuelve un iterator de una colección que contiene al receptor de la invocación.En tus clases donde venga al caso, puedes implementar
iterator()
para devolver algo que venga al caso. O puedes directamente sobreescribireach()
.Lo del
@Alias
yo no creo que fuera muy buena idea porque porque podría crear bastante confusión. Mejor investigas si hay un nombre especial de método en el otro lenguaje y si ya tienes algo así implementado simplemente llamas a tu método real dentro del método con el nombre especial para el otro lenguaje.Each
Algunas implementaciones de each:
String nombre
String apellido
Date fechaNacimiento
}
Persona p = new Persona(nombre:'Enrique', apellido:'Zamudio', fechaNacimiento:new Date())
Map m = [k1:'v1', k2:'v2', k3:'v3']
p.each { println it }
m.each { println it }
'hola'.each { println it }
"hola".each { println it }
Lo de
hola
primero es un String normal y luego un GString. Tal vez no parezca muy útil, pero si recuerdan por ahí alguien que pidió una tarea de contar caracteres, en Groovy sería simplemente:"enrique zamudio lopez (o cualquier otra cadena que se te ocurra)".each { m[it] = m[it]? m[it]+1 : 1 }
println m
Me refiero a una lista de
Me refiero a una lista de "conversiones" entre Groovy y Java, como: "plus" mapea a "+".
Entonces, Groovy tiene un metaclass que puede aumentar el comportamiento de la contraparte en Java. Ese java.lang.Object que lista domix es un meta class ? Interesante.
Un metaclass es solo un
Un metaclass es solo un registro que tiene cada clase en Groovy _solamente_. Ahi se guardan los métodos que se inyectan via el GDK (como el método each y otros mas) y los que un desarrollador agrega.
El uso y acceso de los metaclass esta solo desde clases Groovy, desde código Java no puedes dada la naturaleza del lenguaje Java. A menos que uses o accedas al metaclass con código Java (muy tedioso de hacer, no vale la pena)
no sé
Honestamente, no sé si ese Object es un metaClass o cómo lo implementan, seguro él nos podrá aclarar la duda, tiene bastante más experiencia con Groovy. Yo sospecho que por performance, lo que hacen al levantar una app con Groovy, es que le agregan métodos a varias clases de Java, usando asm. Probablemente eso mismo usan para implementar los mecanismos de metaclases, pero la cosa es que a Object le meten esos métodos que viste, igual que a varias colecciones y otras clases básicas como String le agregan varios métodos.
Hice un diagrama de como
Hice un diagrama de como funciona mas o menos la metaprogramación en Groovy para un curso, aqui la pueden ver:
http://www.flickr.com/photos/domix/4122968104/in/set-72157622724735681/
Syntactic sugar causes cancer
Syntactic sugar causes cancer of the semicolon. Alan Perlis, Epigrams in Programming http://www.cs.yale.edu/quotes.html
Clarke
For every expert, there is an equal and opposite expert - Arthur C. Clarke
Ladra que muerde no perra.
Ladra que muerde no perra. El Chapulín colorado
Je je je..
¿Ahh no verdad?
Ya en serio, lo que menciona bferro es interesante. Durante muchos años este ha sido el pensamiento de los lenguajes de programación principales; hubo poca azúcar sintáctica. Incluso Java mismo se había negado ( hasta Java 1.5 ) a utilizar este tipo de azucar. La razón era básicamente que se introducían instrucciones intermedias innecesarias que pegaban ( mínimamente, pero pegaban ) en el desempeño.
Sin embargo, el hardware mejoró muchísimo y muchas prácticas que eran casí tabú empezaron a tener más y más aceptación, al grado que los lenguajes de programación interpretados ahora no son descartados de inmediato por esta sola razón ( por ser interpretados ). Lenguajes como Ruby, Python, Perl y el mismísimo Java que tuvieron muchísimo rechazo inicialmente hoy son considerados como lenguajes de primera línea. Hay que recordar que Java es un lenguajes interpretado ( el bytecode se interpreta por la JVM, que en una JVM moderna compila a código de máquina, pero lo hace en línea, lo cual no lo hace menos interpretado, solo habla que el interprete es muuuuy bueno en su trabajo ) y que el bytecode mismo es syntactic sugar para ensamblador.
Lo importante es hacer una buena implementación de estas abstracciones sintácticas para que no se note el truco. Es como en la magia, mal hecho todos dicen "bah", bien ejecutada es sorprendente. Scala es syntactic sugar de Java ( y vaya que hay muchos triquitos por ahí ) Java de Bytecode, el Bytecode de C, C de ensamblador y el ensamblador de ... este... bueno lo que diablos haya allá abajo ( el hardware no es lo mío )
Re: Ladra que muerde no perra.
FTW!!!!
IMHO
La sobrecarga de operadores, es un asunto puramente sintáctico, y como bien dice ezamudio, es una cuestión de gusto usarla o no .
Groovy la tiene, Scala también (aunque más clara, ya que admite caracteres no alfanuméricos en los nombres), C++ la tuvo desde sus inicios, y de manera muy limitada Java la tiene, por ejemplo con el operador + para indicar una concatenación.
De hecho casi todos los lenguajes de programación sobrecargan el operador = para indicar una inicialización en el momento de definir una variable y también para asignar un nuevo valor a una variable existente, que son dos operaciones diferentes. Pascal por ejemplo, distingue esas operaciones con = y:=
En lo personal, trato de usar ese mecanismo sin para nada alterar la semántica de lo que escribo. Con esto quiero decir, que si por ejemplo, programo una clase para los números complejos, tiene sentido que sobrecargue los operadores aritméticos porque en el dominio del problema, el lenguaje que uso es ese: voy a sumar el complejo A con el complejo B, o voy a restarlos, etc.
Si la operación que quiero programar, como es el caso de añadir un elemento a una lista, prefiero no usar el operador + con ese propósito, pues normalmente no "sumo" un elemento a una lista. Al explicar el programa normalmente digo que voy a añadir un elemento a la lista, y en ese caso prefiero de manera explícita escribirlo: lista.add(nuevo_elemento).
Por supuesto que pienso que no es saludable resistirse a usar la sobrecarga de operadores. Es un recurso adicional que brinda el lenguaje.
Ah te referías solamente al
Ah te referías solamente al syntactic sugar en los operadores? Yo creí que en todo el lenguaje en sí. :)
En lo particular me gusta que no haya sobre carga de operadores en Java ( o al menos no definidos por el usuario). ¿Se imaginan un lenguaje de programación sin operadores? Bueno quizá solo el de asignación. Estaría raro no? ;) ( je je je je )
Me refiero en general a las "facilidades sintácticas"
En general me refiero a las facilidades sintácticas, como es el caso de la sobrecarga de operadores, notaciones infijas, prefijas postfijas, etc.
En lenguajes como Scala, que combinas lo imperativo con lo funcional, puedes aun más oscurecer la intención de lo que quieres si abusas mucho de esa "dulzura" sintáctica.
Pero, otra vez, voto a favor de esas facilidades.
Guardian.co.uk Switching from Java to Scala
Por si les interesa: http://www.infoq.com/articles/guardian_scala