Sei sulla pagina 1di 8

Los punteros de java

Hay un par de ideas sobre java muy extendidas: java no tiene punteros y en java todo se
pasa por referencia.

La realidad, es que java se entiende mucho mejor si lo pensamos exactamente al revs. En
java slo hay punteros (con excepcin de los tipos primitivos) y en java todo se pasa por
valor (por copia).

Por ejemplo, en C++ hacemos esto

MiClase a;

y ya est todo correcto. La variable a est perfectamente inicializada. Si en java hacemos
eso, tenemos una variable a sin inicializar. Es necesario hacerle un new, exactamente igual
que un puntero en C++

MiClase a = new MiClase(); // Esto en Java
MiClase *a = new MiClase(); // Esto en C++

// o este otro tipo de inicializacin extraamente parecida ...

MiClase a = null; // en java
MiClase *a=NULL; // en C++

La nica diferencia es la notacin con el asterisco. Si pensamos que en java TODO son
punteros, no es necesario poner el asterisco para distinguir lo que es puntero de lo que no lo
es, por lo que smplemente lo han quitado.

Ahora imaginemos un mtodo que recibe una clase y que le hacemos una llamada

// en java...
void medodo (MiClase a)
{
a = new MiClase();
}
...
MiClase b = null;
metodo (b);

Bueno, pues cuando salimos del mtodo b sigue valiendo null, "apuntando a null". Eso quiere
decir que a y b son variables disintas, es decir, se ha pasado la variable b por valor al
mtodo.

Cmo podemos crear una nueva instancia y devolverla?. En java no queda ms remedio que
hacerlo en el return, es imposible hacerlo a travs de parmetros. Sin embargo, en C++
tenemos ms posibilidades. Podemos usar un puntero al puntero, es decir, hacer esto

void metodo (MiClase **a)
{
*a = new MiClase();
}
...
MiClase *b=NULL;
metodo (&b);

o bien, incluso usar referencias de verdad

// El & en la declaracin es lo que hace que realmente sea una referencia.
void metodo (MiClase * &a)
{
a=new MiClase();
}
...
MiClase *b=NULL;
metodo (b);
// Aqui b apunta al MiClase creado dentro del mtodo.

Hacindolo as, el puntero b de fuera del mtodo y el puntero a del parmetro son
exactamente la misma variable. Ojo, no quiero decir que sean dos punteros distintos que
apunten al mismo lado, sino que son realmente el mismo puntero. Cuando hacemos que a
apunte a otro sitio, b tambin apuntar al mismo sitio. Esto es lo que yo entiendo realmente
por referencia.

La clase Object
Objetivos: Mostrar que en ocasiones no se necesita saber nada de un objeto y
que en esos casos es til trabajar con l, como objeto pertenciente a la clase
Object.
Temas:
La clase Object.
Implementacin de contenedores de objetos.
Mezclando objetos con tipos primitivos.

La clase Object
Todas las clases son en realidad subclases de una clase ms amplia: la clase
Object. Esta clase incluye todos los objetos (los lectores de archivos, las tortuga,
los arreglos, los glyphs, etc.). Por lo tanto siempre es posible colocar cualquier
objeto en donde se espera un expresin de tipo Object. Por ejemplo:
Object o1= "Hola";
Object o2= new TextReader("datos.txt");
Object o3= new Nodo(3, "hola", null, null);
Object o4= new Box( ... );
Recuerde que la relacin ser subclase de es transitiva: como Box es subclase de
Glyph y Glyph es subclase de Object entonces Box es subclase de Object
tambin y por eso es vlido realizar la ltima operacin del ejemplo.
El nico problema es que no hay muchas operaciones que se puedan realizar con
una variable de tipo Object. Quizas la ms importante es que se puede obtener un
string que describa el objeto:
println(o1.toString()); // despliega "Hola" en pantalla
println(o3.toString()); // despliega un mensaje no muy til
Este mtodo se puede redefinir en las subclases de modo que se entregue
alguna informacin ms util.
De todas formas algunas de las clases contenedoras que hemos visto durante el
curso aceptan objetos como parmetros. Por ejemplo se puede construir una cola
con objetos:
Queue cola= new Queue();
cola.put(o1);
cola.put(o2);
cola.put(o3);
cola.put(o4);
En realidad este ltimo ejemplo debe ser visto como una humorada porque rara
vez es til juntar elementos tan diferentes en un mismo contenedor. Por otra
parte, para extraer los objetos de la cola se debe usar el mtodo get que retorna
un objeto:
Object stringO= cola.get();
Sera un error tratar de almacenar la referencia del objeto entregada por get en
una variable de tipo String, an cuando se sabe que el primer objeto de la cola
era un string. Para poder ver el objeto como string, se debe aplicar el cast a
String:
String s= (String)stringO;
String starS= "*** "+s+" ***"; // Ahora s se puede trabajar
println(starS); // como un string
Como se sabe que el siguiente objeto de la cola es un lector de archivos, se
puede aplicar directamente el cast al momento de extraerlo:
TextReader lect= (TextReader)cola.get();
String lin= lect.readLine();
...
Por lo tanto la clase Object es importante porque evita tener que programar
contenedores para cada posible tipo de objetos. Por ejemplo, la clase PilaNodos
del captulo anterior no es necesaria, porque se puede usar la clase ms general
Stack para almacenar todo tipo de objetos. El nico cuidado es que al extraer
con la operacin pop, se debe aplicar el cast a Nodo.

Implementacin de contenedores de objetos
Para implementar una cola de objetos con una lista enlazada, basta cambiar los
datos contenidos en los eslabones. En vez de colocar una referencia de un
String, se coloca una referencia de un objeto:
class EslabonCola {
Object o;
EslabonCola prox;
EslabonCola(Objecto o, EslabonCola prox) {
this.o= o;
this.prox= prox;
}
}
La implementacin es la misma slo que cambia el tipo del dato que se
manipula:
class Queue extends Program {
EslabonCola primero;
EslabonCola ultimo;
...
Object get() {
Object o= primero.prox.o;
primero.prox= primero.prox.prox;
return o;
}
void put(Object o) {
ultimo.prox= new EslabonCola(o, null);
ultimo= ultimo.prox;
}
}
El problema de esta implementacin de Queue es que no acepta colocar
enteros o nmeros reales:
cola.put(4); // error en tiempo de compilacin
cola.put(3.14); // idem
cola.put(true); // idem
En todos estos caso el compilador entregar un error diciendo que la operacin
put no recibe enteros, reales o valores de verdad como parmetro. Esto se debe
a que estos tipos de datos no son objetos. En Java se dice que son tipos de datos
primitivos. (En realidad este es un error de diseo del lenguaje: no hay ninguna
razn de fondo para que no sean objetos, como s lo son los Strings.)

Mezclando objetos con tipos primitivos
Para solucionar el problema mencionado ms arriba Java ofrece la clase Integer
que s es una subclase de Object. La operaciones que acepta esta clase son las
siguientes:
Ejemplo Significado Declaracin
Integer objent= new Integer(1); Construye un objeto Integer que almacena el 1 Integer(int i)
int i= objent.intValue() Entrega el valor entero almacenado en objent int intValue()
El objeto objent es un objeto y por lo tanto puede ser colocado en una cola:
q.put(objent); // Correcto
Sin embargo, al ser un objeto pierde sus operaciones como entero y no se
puede operar como tal:
objent= objent+1; // Error de tipos en tiempo de compilacin
Con la clase Integer ahora es posible almacenar cualquier entero en una cola.
Supongamos que necesitamos agregar el entero e en una cola y posteriormente
extraerlo. Entonces el siguiente cdigo es una receta para lograr esto:
q.put(new Integer(e)); // Coloca el entero e en la cola
...
int f= ((Integer)q.get()).intValue(); // Recupera el valor e de la
cola
Java tambin ofrece las clases estndares Double y Boolean para trabajar con
los reales y los valores de verdad como objetos. Observe que se diferencian
nicamente de los tipos de datos primitivos double y boolean porque Double y
Boolean comienzan con una letra mayscula y porque Double y Boolean son
subclases de Object.
De todas formas, esta solucin todava no es satisfactoria porque obliga a los
programadores usuarios de la clase Queue a crear estos objetos adicionales. La
siguiente implementacin de Queue acepta enteros primitivos en la operacin
put:
class Queue extends Program {
EslabonCola primero;
EslabonCola ultimo;
...
void put(int i) {
put(new Integer(i)); // (*)
}
int getInt() {
return ((Integer)get()).intValue();
}
}
Ahora cuando se invoque put con un entero, el compilador determina que el
parmetro es entero y que hay una versin especial de put que recibe un
entero. Observe que la definicin de put no es recursiva en ningn caso puesto
que en (*) se invoca la otra versin de put: aquella que recibe un objeto como
parmetro.
En el caso general, en Java se puede definir varios mtodos con el mismo
nombre, siempre y cuando los tipos de los parmetros sean diferentes. Durante la
compilacin, Java determina el mtodo que mejor se aproxima a los argumentos
especificados en la invocacin del mtodo.
Ejercicio: modifique esta implementacin para que acepte nmeros reales y
valores booleanos en el put.

Potrebbero piacerti anche