Java distingue entre tipos primitivos
int
,
double
,
char
y
byte
,
y los tipos de referencia (tales como
Object
y
String
).
Los tipos primitivos son fijos y son los únicos que tienen semantica de valor.
No es posible crear tipos-valor en Java.
No es posible llamar a un método en un valor de un tipo primitivo.
Las
variables con tipos referencia contienen una referencia a un objeto.
Para facilitar el paso de tipos primitivos a tipos referencia, Java provee un tipo wrapper
para
cada tipo primitivo. Por ejemplo, el wrapper
para el tipo int
es java.lang.Integer
.
Tales tipos de datos se denominan type objects. La wikipedia dice
lo siguiente sobre los type objects:
In computer science, an object type (a.k.a. wrapping object) is a datatype which is used in object-oriented programming to wrap a non-object type to make it look like a dynamic object.
Some object-oriented programming languages make a distinction between reference and value types, often referred to as objects and non-objects on platforms where complex value types don't exist, for reasons such as runtime efficiency and syntax or semantic issues. For example, Java has primitive wrapper classes corresponding to each primitive type: Integer and int, Character and char, Float and float, etc. Languages like C++ have little or no notion of reference type; thus, the use of object type is of little interest.
Por otro lado, los operadores sobre los tipos primitivos no se soportan sobre los correspondientes tipos referencia. Esto daba lugar en las versiones de Java anteriores a J2SE 5.0 a códigos innecesariamente complejos, como éste en que se suman dos listas en una tercera:
~/Lgroovy/simpledatatypes$ cat -n Sum.java 1 // Java code 2 import java.io.*; 3 import java.util.*; 4 public class Sum { 5 public static void main (String [] args) { 6 ArrayList listOne = new ArrayList(); 7 ArrayList listTwo = new ArrayList(); 8 ArrayList results = new ArrayList(); 9 10 int s = (args.length > 0)? Integer.parseInt(args[0]) : 5; 11 for (int i = 1; i < s; i++) { 12 System.out.println(" - Storing (" + i + ")"); 13 listOne.add(new Integer(i)); 14 // listTwo = 2*listOne 15 listTwo.add(new Integer(2 * ((Integer) listOne.get(i-1)).intValue())); 16 } 17 18 for(int i = 0; i < listOne.size(); i++) { 19 Integer first = (Integer) listOne.get(i); 20 Integer second = (Integer) listTwo.get(i); 21 22 int sum = first.intValue()+second.intValue(); 23 results.add (new Integer(sum)); 24 } 25 26 for (int i = 0; i < results.size(); i++) { 27 System.out.println(" - results(" + i + ") = " + results.get(i)); 28 } 29 } 30 }
Autoboxing is the term for treating a value type as a reference type without any extra source code. The compiler automatically supplies the extra code needed to perform the type conversion.
En Groovy, todos los tipos son objetos. Las operaciones de boxing y unboxing son realizadas automáticamente. Este es un programa Groovy equivalente al anterior:
generaciondecodigos@nereida:~/src/groovy/simpledatatypes$ cat Sum.groovy #!/usr/bin/env groovy a = (1..10).toList() # objeto IntRange -> ArrayList b = a.collect { 2*it } # objeto ArrayList r = (0..9).collect { a[it]+b[it] } # objeto IntRange -> ArrayList println rDamos permisos de ejecución al fichero y ejecutamos:
generaciondecodigos@nereida:~/src/groovy/simpledatatypes$ chmod a+x Sum.groovy generaciondecodigos@nereida:~/src/groovy/simpledatatypes$ ./Sum.groovy [3, 6, 9, 12, 15, 18, 21, 24, 27, 30]O usando
groovysh
:
groovy:000> (c, d) = [(1..10).toList(), (1..10).collect {2*it }] ===> [[1, 2, 3, 4, 5, 6, 7, 8, 9, 10], [2, 4, 6, 8, 10, 12, 14, 16, 18, 20]] groovy:000> println (0..9).collect { c[it]+d[it] } [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]Véanse las entradas Multiple Assignment y Collections en groovy.codeHaus.org asi como la documentación de la clase Object del Groovy JDK.
Groovy siempre usa la clase wrapper en vez de la clase primitiva, pero nos permite utilizar las operaciones sobre los tipos básicos. La conversión de un tipo primitivo en un tipo wrapper se conoce como boxing. La acción inversa se denomina unboxing. Groovy realiza estas operaciones donde quiera que sea necesario.
En las versiones posteriores de Java a J2SE 5.0 es posible simplificar la suma, ya que se soporta autoboxing:
for(int i = 0; i < listOne.size(); i++) { results.add ((Integer) listOne.get(i) + (Integer) listTwo.get(i)); }
La presencia de genericidad, bucles for-each
y autoboxing
hace que en las últimas versiones de Java se simplifiquen estos
problemas. El siguiente programa calcula las frecuencias de las palabras que se pasan
en la línea de comandos:
generaciondecodigos@nereida:~/src/groovy/simpledatatypes$ cat -n Frequency.java 1 import java.util.*; 2 3 // Prints a frequency table of the words on the command line 4 public class Frequency { 5 public static void main(String[] args) { 6 Map<String, Integer> m = new TreeMap<String, Integer>(); 7 for (String word : args) { 8 Integer freq = m.get(word); 9 m.put(word, (freq == null ? 1 : freq + 1)); 10 } 11 System.out.println(m); 12 } 13 }sigue un ejemplo de ejecución:
generaciondecodigos@nereida:~/src/groovy/simpledatatypes$ javac Frequency.java generaciondecodigos@nereida:~/src/groovy/simpledatatypes$ java Frequency en todos los y son todos las operaciones de boxing y boxing son y {boxing=2, de=1, en=1, las=1, los=1, operaciones=1, son=2, todos=2, y=3}
El programa declara primero un Map de String a Integer
, implementado según un TreeMap,
el cual asociará el número de veces que una palabra ocurre en la línea de comandos.
La declaración:
Map<String, Integer> m = new TreeMap<String, Integer>()es un ejemplo de uso de genericos. Los genéricos nos proveen con un mecanismo para comunicar el tipo de una colección al compilador, de manera que su correcto uso pueda ser comprobado en tiempo de compilación. Como el compilador conoce ahora el tipo de un elemento de una colección puede insertar los ahormados o casts y comprobar que el uso de la colección es consistente con la declaración.
El siguiente fragmento está tomado del libro The Java Language Specification, Third Edition:
A class is generic if it declares one or more type variables. These type variables are known as the type parameters of the class. The type parameter section follows the class name and is delimited by angle brackets. It defines one or more type variables that act as parameters. A generic class declaration defines a set of parameterized types, one for each possible invocation of the type parameter section. All of these parameterized types share the same class at runtime.
For instance, executing the code
Vector<String> x = new Vector<String>(); Vector<Integer> y = new Vector<Integer>(); boolean b = x.getClass() == y.getClass();
will result in the variableb
holding the valuetrue
.
Cuando veamos algo como <Type>
deberemos leer "of Type”
.
La declaración anterior se lee un MAP of String to Integer
. Esto es mejor que usar
un ahormado o cast, porque un cast
nos informa de algo que el programador cree que es cierto en cierto punto del código
y que la JVM deberá comprobar en tiempo de ejecución.
Después se itera sobre cada argumento que aparece en la línea de comandos
usando el nuevo bucle for-each
.
for (String word : args) { Integer freq = m.get(word); m.put(word, (freq == null ? 1 : freq + 1)); }En la tabla puede verse las equivalencias del nuevo bucle, dependiendo que lo que se recorra sea un Arrays o un Iterator. En ambos casos se requiere una variable extra, un índice para el array y un iterador para la colección.
Uso | Definición |
for (type var : arr) { body-of-loop } |
for (int i = 0; i < arr.length; i++) { type var = arr[i]; body-of-loop } |
for (type var : coll) { body-of-loop } |
for (Iterator<type> iter = coll.iterator(); iter.hasNext(); ) { type var = iter.next(); body-of-loop } |
Para cada palabra word
, obtenemos la correspondiente entrada en el Map (freq = m.get(word)
)
y actualizamos la entrada (m.put(word, (freq == null ? 1 : freq + 1))
).
Esta actualización es un caso de autoboxing. No es posible poner un int
en el Map, ya que la signature
de put
es:
Object put(Object key, Object value) Associates the specified value with the specified key in this map (optional operation).y tampoco es posible añadir uno a un Integer.
Aún así el correspondiente programa Groovy es mas pequeño y legible:
generaciondecodigos@nereida:~/src/groovy/simpledatatypes$ cat Frequency.groovy #!/usr/bin/env groovy def m = [:] args.each { v = m[it]; m[it] = (v ? ++v : 1) } println(m);Sigue una ejecución:
generaciondecodigos@nereida:~/src/groovy/simpledatatypes$ ./Frequency.groovy en todos son y son en en [en:3, todos:1, son:2, y:1]