Subsecciones

Declaración de Variables

Declaraciones Opcionales

Dentro de las clases los atributos y las variables locales deben ser declaradas antes de su uso. Groovy utiliza los mismos modificadores que Java para modificar la visibilidad:

El modificador final se usa para desactivar nuevas asignaciones y static para denotar variables de clase. Otro nombre para los atributos no estáticos es variables de instancia.

Dentro de las clases, no es obligatorio definir el tipo de una variable pero si que es obligatorio declararlo. La palabra reservada def es usada para declarar una variable para especificar que es del tipo genérico Object. Veamos un ejemplo:

generaciondecodigos@nereida:~/Lgroovy/learning$ cat -n dec.groovy 
     1  class T {
     2    def static int x = 4
     3    def static String[] s = [ 1 .. 4 ]; 
     4    static Collection<?> c = [1, 2, "a"];
     5    def static y = x
     6  
     7   public static void main(String[] args) {
     8      println x
     9      println y
    10      println s
    11      println c
    12    }
    13  }
Obsérvese como Groovy ha realizado las conversiones adecuadas en todos los casos en los que ha sido necesario:
generaciondecodigos@nereida:~/Lgroovy/learning$ groovy dec.groovy 
4
4
[1..4]
[1, 2, a]

The unbounded wildcard above Collection<?> indicates a Collection which has an unknown object type.

Generic type parameters in Java are not limited to specific classes. Java allows the use of wildcards to specify bounds on the type of parameters a given generic object may have. Wildcards are type parameters of the form ?.

Conversiones y Coherciones de Entero a String

La conversión ocurre de la forma que cabría esperar, en el siguiente ejemplo el entero es promocionado a un array de una String:

~/Lgroovy/learning$ cat dec2.groovy 
class T {
  def static int x = 4
  def static String[] s = ( 1 .. 4 ).toList()

  public static void main(String[] args) {
    println s
    println s[0]
    println s[0].class
    println s.size()
    s = x
    println s
    println s[0]
    println s.class
  }
}
Sigue una ejecución:
~/Lgroovy/learning$ groovy dec2.groovy 
[1, 2, 3, 4]
1
class java.lang.String
4
[4]
4
class [Ljava.lang.String;

Conversiones a Números

Sin embargo, esta otra asignación produce una excepción:

~/Lgroovy/learning$ cat -n dec3.groovy 
     1  class T {
     2    def static int x = 4
     3    def static String[] s = ['a', 'b']
     4  
     5    public static void main(String[] args) {
     6      x = s
     7      println x
     8    }
     9  }
Ejecución:
~/Lgroovy/learning$ groovy dec3.groovy 
Caught: org.codehaus.groovy.runtime.typehandling.GroovyCastException: 
Cannot cast object '[Ljava.lang.String;@3a4c5b4' with class '[Ljava.lang.String;' 
to class 'java.lang.Number'
        at T.main(dec3.groovy:6)

Las asignaciones de expresiones a objetos de tipo numérico deben ser de tipo numérico:

generaciondecodigos@nereida:~/src/groovy/objects$ cat -n Conform.groovy 
     1  int a
     2  try {
     3    a = 'hello'
     4  }
     5  catch(e){
     6    println "Assignments to typed references must conform to the type"
     7  }
La ejecución muestra como se produce la ejecución del código que captura la excepción:
generaciondecodigos@nereida:~/src/groovy/objects$ groovy Conform.groovy 
Assignments to typed references must conform to the type

Conversiones a Objetos Genéricos

Si el objeto no esta tipado presenta una conducta polimorfa, aceptandose la asignación:

generaciondecodigos@nereida:~/src/groovy/objects$ cat -n Nontyped.groovy 
     1  def a
     2  int b = 4
     3  String c = 'hello'
     4  Float d = 4.5
     5  
     6  a = b
     7  println a
     8  
     9  a = c
    10  println a
    11  
    12  a = d
    13  println a
Ninguna de las asignaciones a a produce un error:
generaciondecodigos@nereida:~/src/groovy/objects$ groovy Nontyped.groovy 
4
hello
4.5

Modificadores y Declaraciones

El siguiente ejemplo ilustra una variedad de declaraciones, incluyendo diversos modificadores:

generaciondecodigos@nereida:~/src/groovy/objects$ cat -n Declarations.groovy 
 1  class SomeClass {
 2    public fieldWithModifier
 3    String typedField
 4    def unTypedField
 5    protected field1, field2, field3
 6    private assignedField = new Date()
 7  
 8    static classField
 9  
10    public static final String CONSTA = 'a', CONSTB = 'b'
11  
12    def someMethod() {
13      def localUntypedMethodVar = 1
14      int localtypedMethodVar = 1
15      def localVarWithoutAssignment, andAnotherOne
16    }
17  }
18  
19  def localvar = 1
20  boundVar1 = 1
21  
22  def someMethod() {
23    localMethodVar = 1
24    boundVar2 = 1
25  }
La siguiente sesión con groovysh muestra las formas de acceso a algunos de los entes definidos en la clase SomeClass:
generaciondecodigos@nereida:~/Lgroovy/objects$ groovysh
Groovy Shell (1.6.5, JVM: 1.6.0_0)
Type 'help' or '\h' for help.
-----------------------------
groovy:000> import Declarations
===> [import Declarations]
groovy:000> a = new SomeClass() 
groovy:000> a.fieldWithModifier = 4
===> 4
groovy:000> SomeClass.fieldWithModifier
ERROR groovy.lang.MissingPropertyException: No such property: fieldWithModifier for class: SomeClass
        at groovysh_evaluate.run (groovysh_evaluate:3)
        ...
groovy:000> SomeClass.classField = 'hola'
===> hola
groovy:000> SomeClass.CONSTA             
===> a
groovy:000> SomeClass.CONSTA = 'c'
ERROR groovy.lang.ReadOnlyPropertyException: Cannot set readonly property: CONSTA for class: SomeClass
        at groovysh_evaluate.run (groovysh_evaluate:3)
        ...
groovy:000> a.assignedField
===> Thu Feb 18 10:21:28 WET 2010
groovy:000> a.typedField = 'hola'
===> hola
groovy:000> a.typedField = [1,2] 
===> [1, 2]
groovy:000> a.typedField.class  
===> class java.lang.String
groovy:000> (0..<a.typedField.size()).each { print "<${a.typedField[it]}> " }; println ''
<[> <1> <,> < > <2> <]> 
===> null

Usando JUnit para Comprobar una Excepción de Conversión de Tipos

Obsérvese en el siguiente ejemplo que el valor asignado a la variable de tipo cadena PI no está entre comillas.

generaciondecodigos@nereida:~/src/groovy/objects$ cat -n Shouldfail.groovy 
     1  final String PI = 3.14
     2  
     3  println "PI.class.name = ${PI.class.name}"
     4  println "PI.length() = ${PI.length()}"
     5  
     6  new GroovyTestCase().shouldFail(org.codehaus.groovy.runtime.typehandling.GroovyCastException) {
     7    Float area = PI
     8  }
     9  
    10  try {
    11    Float area = PI
    12  }
    13  catch(e){
    14    println "Assignments to typed references must conform to the type. ${e.class}"
    15  }
    16

La asignación deja en PI la cadena "3.14". La asignación de PI a area produce una excepción:

generaciondecodigos@nereida:~/src/groovy/objects$ groovy Shouldfail.groovy 
PI.class.name = java.lang.String
PI.length() = 4
Assignments to typed references must conform to the type. 
class org.codehaus.groovy.runtime.typehandling.GroovyCastException

Un objeto GroovyTestCase es construido en las líneas:

     6  new GroovyTestCase().shouldFail(org.codehaus.groovy.runtime.typehandling.GroovyCastException) {
     7    Float area = PI
     8  }
La clase GroovyTestCase provee una serie de métodos como

protected  String       shouldFail(Class clazz, Closure code)
      Asserts that the given code closure fails when it is evaluated 
      and that a particular exception is thrown.
protected  String     shouldFail(Closure code)
      Asserts that the given code closure fails when it is evaluated

que simplifican el proceso de pruebas JUnit.

JUnit is a unit testing framework for the Java programming language.

JUnit has been important in the development of test-driven development, and is one of a family of unit testing frameworks collectively known as xUnit based on the design by Kent Beck (the creator of Extreme Programming), originally implemented for Smalltalk as SUnit.

JUnit has been ported to many other languages.

Como JUnit forma parte del entorno de ejecución Groovy, es posible escribir pruebas JUnit dentro de las clases Groovy y Java utilizando la sintáxis de Groovy.

Casiano Rodríguez León
2010-04-30