Temas:
Recuerde que la relación ser subclase de es transitiva: como Box
es subclase de Glyph y Glyph es subclase de Object entonces Box
es subclase de Object también y por eso es válido realizar la última
operación del ejemplo.
Object o1= "Hola";
Object o2= new TextReader("datos.txt");
Object o3= new Nodo(3, "hola", null, null);
Object o4= new Box( ... );
El único problema es que no hay muchas operaciones que se puedan realizar con una variable de tipo Object. Quizas la más importante es que se puede obtener un string que describa el objeto:
Este método se puede redefinir en las subclases de modo que se
entregue alguna información más util.
println(o1.toString()); // despliega "Hola" en pantalla
println(o3.toString()); // despliega un mensaje no muy útil
De todas formas algunas de las clases contenedoras que hemos visto durante el curso aceptan objetos como parámetros. Por ejemplo se puede construir una cola con objetos:
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 método get que retorna un objeto:
Queue cola= new Queue();
cola.put(o1);
cola.put(o2);
cola.put(o3);
cola.put(o4);
Sería un error tratar de almacenar la referencia del objeto entregada
por get en una variable de tipo String, aún 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:
Object stringO= cola.get();
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:
String s= (String)stringO;
String starS= "*** "+s+" ***"; // Ahora sí se puede trabajar
println(starS); // como un string
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 capítulo anterior no es necesaria, porque se
puede usar la clase más general Stack para almacenar todo tipo de objetos.
El único cuidado es que al extraer con la operación pop, se debe aplicar
el cast a Nodo.
TextReader lect= (TextReader)cola.get();
String lin= lect.readLine();
...
La implementación es la misma sólo que cambia el tipo del dato
que se manipula:
class EslabonCola {
Object o;
EslabonCola prox;
EslabonCola(Objecto o, EslabonCola prox) {
this.o= o;
this.prox= prox;
}
}
El problema de esta implementación de Queue es que no acepta
colocar enteros o números reales:
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;
}
}
En todos estos caso el compilador entregará un error diciendo
que la operación put no recibe enteros, reales o valores de verdad
como parámetro. 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 diseño del lenguaje: no hay ninguna
razón de fondo para que no sean objetos, como sí lo son los Strings.)
cola.put(4); // error en tiempo de compilación
cola.put(3.14); // idem
cola.put(true); // idem
Ejemplo | Significado | Declaración |
---|---|---|
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:
Sin embargo, al ser un objeto pierde sus operaciones como entero
y no se puede operar como tal:
q.put(objent); // Correcto
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
código es una receta para lograr esto:
objent= objent+1; // Error de tipos en tiempo de compilación
Java también ofrece las clases estándares 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 mayúscula
y porque Double y Boolean son subclases de Object.
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
De todas formas, esta solución todavía no es satisfactoria porque obliga a los programadores usuarios de la clase Queue a crear estos objetos adicionales. La siguiente implementación de Queue acepta enteros primitivos en la operación put:
Ahora cuando se invoque put con un entero, el compilador determina que
el parámetro es entero y que hay una versión especial de put que recibe
un entero. Observe que la definición de put no es recursiva en ningún
caso puesto que en (*) se invoca la otra versión de put: aquella que
recibe un objeto como parámetro.
class Queue extends Program {
EslabonCola primero;
EslabonCola ultimo;
...
void put(int i) {
put(new Integer(i)); // (*)
}
int getInt() {
return ((Integer)get()).intValue();
}
}
En el caso general, en Java se puede definir varios métodos con el mismo nombre, siempre y cuando los tipos de los parámetros sean diferentes. Durante la compilación, Java determina el método que mejor se aproxima a los argumentos especificados en la invocación del método.
Ejercicio: modifique esta implementación para que acepte números reales y valores booleanos en el put.