Publicado el 3 de Noviembre del 2020
653 visualizaciones desde el 3 de Noviembre del 2020
1,0 MB
32 paginas
Creado hace 19a (21/11/2004)
6: Reutilizando clases
Una de las características más atractivas de Java es la reutilización de código.
Pero para ser revolucionario, es necesario poder hacer muchísimo más que co-
piar código y cambiarlo.
Este es el enfoque que se utiliza en los lenguajes procedurales como C, pero no ha funcionado muy
bien. Como todo en Java, la solución está relacionada con la clase. Se reutiliza código creando nue-
vas clases, pero en vez de crearlas de la nada, se utilizan clases ya existentes que otra persona ya
ha construido y depurado.
El truco es usar clases sin manchar el código existente. En este capítulo se verán dos formas de lo-
grarlo. La primera es bastante directa: simplemente se crean objetos de las clase existente dentro
de la nueva clase. A esto se le llama composición, porque la clase nueva está compuesta de objetos
de clases existentes. Simplemente se está reutilizando la funcionalidad del código, no su forma.
El segundo enfoque es más sutil. Crea una nueva clase como un tipo de una clase ya existente.
Literalmente se toma la forma de la clase existente y se le añade código sin modificar a la clase ya
existente. Este acto mágico se denomina herencia, y el compilador hace la mayoría del trabajo. La
herencia es una de las clases angulares de la programación orientada a objetos y tiene implicacio-
nes adicionales como se verá en el Capítulo 7.
Resulta que mucha de la sintaxis y comportamiento son similares, tanto para la herencia, como para
la composición (lo cual tiene sentido porque ambas son formas de construir nuevos tipos a partir de
tipos existentes). En este capítulo, se aprenderá sobre estos mecanismos de reutilización de código.
Sintaxis de la composición
Hasta ahora, la composición se usaba con bastante frecuencia. Simplemente se ubican referencias a
objetos dentro de nuevas clases. Por ejemplo, suponga que se desea tener un objeto que albergue
varios objetos de tipo cadena de caracteres, un par de datos primitivos, y un objeto de otra clase.
En el caso de los objetos no primitivos, se ponen referencias dentro de la nueva clase, pero se defi-
nen los datos primitivos directamente:
/ / : c06:Aspersor.java
/ / Composición para la reutiliación de código.
class FuenteAgua {
private String S;
FuenteAgua ( )
{
System.out .println ("FuenteAgua ( ) 11) ;
s = new String ("Construida") ;
1
}
public String toString() { return S; }
192
Piensa en Java
public class Aspersor {
private String valvulal, valvula2, valvula3, valvula4;
FuenteAgua fuente;
int i;
float f;
void escribir() {
System. out .println ("valvulal = " + valvulal) ;
System. out .println ("valvula2 = " + valvula2) ;
System. out .println ("valvula3 = " + valvula3) ;
System.out.println("valvula4 = " t valvula4);
System.out.println("i = " + i);
System.out.println("f = " t f);
System. out .println ("fuente = " + fuente) ;
1
public static void main(String[] args) t
Aspersor x = new Aspersor ( ) ;
x. escribir ( ) ;
1
1 111:-
Uno de los métodos definidos en FuenteAgua( ) es especial: toString( ). Se aprenderá más adelante
que todo objeto no primitivo tiene un método toStnng( ), y es invocado en situaciones especiales
cuando el compilador desea obtener un objeto como cadena de caracteres. Por tanto, en la expresión:
System. out .println ("fuente = " + fuente) ;
el compilador ve que se está intentando añadir un objeto String ("fuente =") a un objeto
FuenteAgua. Esto no tiene sentido porque sólo se puede "añadir" un String a otro String, por lo
que dice: "jconvertiré fuente en un Stnng invocando a toString( )! Después de hacer esto se pue-
den combinar los dos objetos de tipo String y pasar el String resultante a Sytem.out.println( ).
Siempre que se desee este comportamiento con una clase creada, sólo habrá que escribir un méto-
do toString( ).
A primera vista, uno podría asumir -siendo Java tan seguro y cuidadoso como es- que el compi-
lador podría construir automáticamente objetos para cada una de las referencias en el código de
arriba; por ejemplo, invocando al constructor por defecto para FuenteAgua para inicializar fuente.
La salida de la sentencia de impresión es de hecho:
valvulal = null
valvula2 = null
valvula3 = null
valvula4 = null
i = o
f = 0.0
fuente = null
6: Reutilización de clases
193
Los datos primitivos son campos de una clase que se inicializan automáticamente a cero, como se
indicó en el Capítulo 2. Pero las referencias a objetos se inicializan a null, y si se intenta invocar a
métodos de cualquiera de ellos, se sigue obteniendo una excepción. De hecho, es bastante bueno
(Y útil) poder seguir imprimiéndolos sin lanzar excepciones.
Tiene sentido que el compilador no sólo cree un objeto por defecto para cada referencia, porque eso
conllevaría una sobrecarga innecesaria en la mayoría de los casos. Si se quieren referencias inicia-
lizadas, se puede hacer:
1. En el punto en que se definen los objetos. Esto significa que siempre serán inicializados antes
de invocar al constructor.
2. En el constructor de esa clase.
3.
Justo antes de que, de hecho, se necesite el objeto. A esto se le llama inicialización perezosa.
Puede reducir la sobrecarga en situaciones en las que no es necesario crear siempre el objeto.
A continuación, se muestran los tres enfoques:
/ / : c06:Banio. java
/ / Inicialización de constructores con composición.
class Jabon {
private String S;
Jabon ( )
{
System.out .println ("Jabon ( ) " ) ;
s = new String ("Construido") ;
1
public String toString0 { return S ;
}
1
public class Banio {
private String
/ / Inicializando en el momento de la definición:
sl = new String ("Contento") ,
s2 = "Contento",
s 3 , s4;
Jabon pastilla;
int i;
float juguete;
Banio ( )
{
System. out .println ("Dentro del banio ( ) " ) ;
s 3 = new String("Gozo");
i = 47;
juguete = 3.14f;
pastilla = new J a b o n o ;
1
194
Piensa en Java
void escribir() {
/ / Inicialización tardía:
if (s4 == null)
s4 = new String ("Temporal") ;
System.out.println("s1 = " + sl) ;
System.out.println("s2 = " + s2);
System.out.println("s3 = " + s3);
System.out.println("s4 = " + s4);
System.out.println("i = " + i);
System. out .println ("juguete = " + juguete) ;
System.out .println ("pastilla = " + pastilla) ;
public static void main (String[] args) {
Banio b = new Banio();
b. escribir ( ) ;
1
1 / / / : -
Fíjese que en el constructor Banio se ejecuta una sentencia antes de que tenga lugar ninguna ini-
cialización. Cuando no se inicializa en el momento de la definición, sigue sin haber garantías de que
se lleve a cabo ningún tipo de inicialización antes de que se envíe un mensaje a una referencia a un
objeto -excepto
la inevitable excepción en tiempo de ejecución.
He aquí la salida del programa:
Dentro del Banio ( )
Jabon ( )
S1 = Contento
S2 = Contento
S3 = Gozo
S4 = Gozo
1 = 47
Juguete = 3.14
pastilla = Construido
Cuando se invoca al método escribir( ) éste rellena s4 para que todos los campos estén inicializa-
dos correctamente cuando se usen.
Sintaxis de la herencia
La herencia es una parte integral de Java (y de todos los lenguajes de PO0 en general). Resulta que
siempre se está haciendo herencia cuando se crea una clase, pero a menos que se herede explíci-
tamente de otra clase, se hereda implícitamente de la clase raíz estándar de Java Object.
La sintaxis para la composición es obvia, pero para llevar a cabo herencia se realiza de distinta for-
ma. Cuando se hereda, se dice: "Esta clase nueva es como esa clase vieja". Se dice esto en el códi-
6: Reutilización de clases
195
go dando el nombre de la clase, como siempre, pero antes de abrir el paréntesis del cuerpo de la
clase, se pone la palabra clave extends seguida del nombre de la clase base. Cuando se hace esto,
automáticamente se tienen todos los datos miembro y métodos de la clase base. He aquí un
ejemplo:
/ / : c06:Detergente.java
/ / Sintaxis y propiedades de la herencia.
class ProductoLimpieza {
private String s = new String("Producto de Limpieza");
public void aniadir(String a) { s += a; }
public void diluir() { aniadir(" diluir() " ) ;
public void aplicar ( )
public void frotar ( )
p u b l i c void e s c r i b i r ( )
public static void rnain(String[] args) {
{ System. out . p r i n t l n ( S ) ; }
{ aniadir ( " aplicar ( ) " ) ; }
{ aniadir ( " fregar ( ) " ) ;
}
}
ProductoLimpieza x = new ProductoLimpieza();
x. diluir ( ) ; x. aplicar ( ) ; x. frotar ( ) ;
x. escribir ( ) ;
1
1
public class Detergente extends ProductoLimpieza {
/ / Cambiar un método:
public void frotar ( )
{
aniadir ( " Detergente. frotar ( ) " ) ;
super.frotar ( ) ; / / Llamar a la versión de la clase base
1
/ / Añadir métodos al interfaz:
public void aclarar ( )
/ / Probar la nueva clase:
public static void main(String[] args) {
{ aniadir ( " aclarar ( ) " ) ; }
Detergente x = new Detergente();
x. diluir ( ) ;
x. aplicar ( ) ;
x. frotar ( ) ;
x. aclarar ( ) ;
x. escribir ( ) ;
Systern. out .println ("Probando la clase base: " ) ;
ProductoLimpicza.main(args);
1
1 / / / : -
Esto demuestra un gran número de aspectos. En primer lugar, en el método aniadir( ) de la cla-
se ProductoLimpieza, se concatenan Cadenas de caracteres a S utilizando el operador +=, que
196
Piensa en Java
es uno de los operadores (junto con '+') que los diseñadores de Java "sobrecargaron" para que fun-
cionara con Cadenas de cara
Comentarios de: Reutilizando clases - Piensa en Java (0)
No hay comentarios