style="display:inline-block;width:728px;height:90px"
data-ad-client="ca-pub-5164839828746352"
data-ad-slot="7563230308">

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,
      //al tratar de leer el atributo externo de la superclase
      println "But parent is " + name
    }
  }
  //Esto está nada más para poder crear una instancia de la clase interna
  Internal inner(String p) {
    new Internal(inner:p)
  }
}

//Con esta línea explota
new External(outer:"good", name:"fucked").inner("good").boom()

Este código truena, en tiempo de ejecución obviamente; se arroja una groovy.lang.MissingFieldException que nos dice que no existe el campo name en la clase External. Es decir, está tratando de acceder a name directamente como un campo, pero no como un atributo (invocar el getName() generado).

Soluciones

Este problema tiene tres soluciones:

La más fea es marcar el atributo de la superclase como public, y con eso ya funciona el código. Tal vez se puede hacer, tal vez no, tal vez es inconveniente, etc.

La menos fea es que en la clase interna se invoque getName() en vez de simplemente name. Con eso funciona, pero pues se cae la cortinita; me refiero a que la ilusión de que los getters y setters funcionan de maravilla en Groovy es eso, una ilusión. Creo que esto es un problema en el compilador de Groovy, que no detecta los campos heredados para convertir ese name en la clase interna en una invocación a getName() y por eso la tengo que escribir yo directamente, rompiendo la abstracción de los atributos autogenerados en Groovy.

Otra que tampoco está tan mal pero no entiendo por qué hay que usar, es que en vez de simplemente name, escribir External.this.name. Sin embargo esto me resulta algo confuso, pues en Java no es necesario hacer eso bajo las mismas circunstancias.

En fin, una cosita más que le encuentro a este lenguaje que si bien es muy práctico, tiene varios detallitos (para mi al menos), principalmente por ser tan dinámico.

Lo grave de esto es que me lo topé ya que tenía bastante tiempo invertido en ese esquema de clase padre abstracta + subclase con clase interna; después de visitar varios foros de Groovy, todos dicen "no uses clases internas, mejor usa closures" y cosas por el estilo. Pero, y si ya tengo esto hecho? Obvio no iba a hacer un refactoring medio choncho simplemente para cambiar la clase interna por closures, afectando un montón de cosas más; por eso me puse a buscarle hasta encontrar estas alternativas que publico aquí, que espero le sirvan a alguien en el futuro.

Comentarios

Opciones de visualización de comentarios

Seleccione la forma que prefiera para mostrar los comentarios y haga clic en «Guardar las opciones» para activar los cambios.
Imagen de ezamudio

Reporte

Lo bueno de Groovy es que sus devs están siempre al pendiente; en cuanto publiqué la liga a esto en twitter, uno de ellos me contactó para que levantara el reporte del bug. Y aquí está:

https://jira.codehaus.org/browse/GROOVY-6445

Imagen de arterzatij

:'( quiero llorar (que

:'( quiero llorar (que marica) pero esto sres es lo bonito de las comunidades :) sigamos por este camino...

Tal vez...

No sé si a lo mejor el uso de @Mixin te hubiera ayudado, y no tendrías que hacer un gran refactor. El problema/ventaja de Groovy es la forma en como resuelve las llamadas a los métodos(la excepción que te arroja es de tiempo de ejecución, y no de compilación ), como lo dije una vez, la gran ventaja de Groovy es su dinamismo, pero a veces es tan dinámico que aun cuando lo escribes mal, aún así funciona, no como debiera pero funciona.

En fin...

style="display:inline-block;width:728px;height:90px"
data-ad-client="ca-pub-5164839828746352"
data-ad-slot="7563230308">