La programación orientada a objetos

 

 

La programación orientada a objetos

La programación es un actividad humana que se ha desarrollado casi enteramente durante la segunda mitad del siglo XX.

Por tanto podemos suponer que aún está en sus orígenes y que el futuro traerá todavía grandes adelantos técnicos y teóricos que mejorarán sus resultados.

En su corta historia, la programación ha sufrido ya dos pequeñas revoluciones. La primera, protagonizada principalmente por Wirth, Dijstra y de forma menos teórica pero quizás con más impacto por Kernighan y Ritchie. Es lo que se denomina la programación estructurada.

Los primeros lenguajes de programación eran simplemente intrucciones que se le podían dar a un autómata como una computadora, para que realizara ciertas operaciones. Así un programa no era sino una lista de instrucciones encaminadas a realizar algún cálculo.

A medida que las computadoras fueron haciéndose más sofisticadas, sus lenguajes propios o lenguajes de máquina iban cambiando y surgió la necesidad de crear unos lenguajes intermedios que cualquier usuario pudiera aprender y que no dependieran de la máquina concreta en la que se iban a ejecutar los programas. Así surgieron varios lenguajes que se hicieron famosos y también surgieron los primeros compiladores. Un compilador es un programa que traduce las instrucciones de un lenguaje más o menos humano, a las de una máquina.

La aparición de estos lenguajes intermedios y sus compiladores marca el comienzo de la programación como una nueva ciencia.

El tremendo éxito que las computadoras fueron teniendo a lo largo de los años 60 fue llevando a la creación de programas cada vez más complicados que llegaban a tener cientos de miles de líneas de código.

Hacer correcciones a este tipo de programas y agregarles mejoras se fue convirtiendo en una labor imposible. Ante este problema que amenazaba con convertir las computadoras en máquinas estériles, surgió un grupo de científicos de la computación, de los cuales Dijstra y Wirth son dos de los más destacados, que fueron proponiendo una serie de ideas que llevaron a la creación de un nuevo concepto: la programación estructurada.

En pocas palabras, la programación estructurada propone dos ideas básicas: no repetir código y proteger las variables que una parte del programa usa de que sean modificadas accidentalmente por otras partes del programa.

Una sola repetición de código es fuente probable de errores y dificulta el mantenimiento de un programa. Para no repetir código hay que escribir funciones o procedimientos que se encarguen de realizar siempre lo que las diferentes repeticiones realizarían.

Para proteger las variables hay que desarrollar lenguajes que permitan definir variables locales, es decir, que sólo pueden ser utilizadas dentro de una función o procedimiento. De esta manera ninguna otra función del programa puede cambiarla.

Como consecuencia de estas ideas disminuye considerablemente el tamaño de los programas y éstos se hacen más confiables y más fáciles de corregir o mejorar.

Para facilitar la programación estructurada, aparecen nuevos lenguajes, notoriamente el Pascal y el C, que son los dos lenguajes de programación estructurada más conocidos.

Wirth, creador del Pascal, nunca quedó satisfecho con este lenguaje. En su constante análisis de los hábitos de programación encontraba que todavía había muchas cosas que mejorar. Una década después del Pascal publicó otro lenguaje llamado Modula2 en el que presentaban algunas de sus ideas más recientes. Los módulos del Modula2 pretendían representar objetos o clases. Dentro de un módulo se definían variables que representaban el estado del objeto y se definían procedimientos que describían su comportamiento. Las variables y los procedimientos de un módulo podían ser de uso público o privado, exclusivo de la clase, según lo decidiera el creador del módulo. Con todo esto se mejoraba notoriamente sobre el Pascal donde el concepto de procedimientos o funciones privadas no existía y se daba un paso importante hacia la programación de clases o programación orientada a objetos.

Otros científicos experimentaron ideas similares creando diversos lenguajes de programación orientada a objetos como Smalltalk. En ellos se experimentó con otras ideas útiles como la definición de subclases que heredan las propiedades de su superclase o sea de la clase de la que se derivan, pero agregando variables y funciones nuevas. También surgió una idea que ayudaría a evitar los difíciles problemas que surgían en el manejo de la memoria dinámica: los constructores y destructores de objetos.

A principios de los 90 se popularizó un nuevo lenguaje orientado a objetos que se parecía mucho a C. Se trata del C++ creado por Stroustroup. La idea de Stroustrup fue crear un lenguaje orientado a objetos que heredara prácticamente toda la sintaxis y posibilidades del lenguaje que en ese momento era más popular entre los programadores, el C. Este truco ayudó a popularizar la programación orientada a objetos y preparó el camino para la aparición del Java.

Los creadores de Java aprendieron bien la lección de Stroustroup. Un nuevo lenguaje para tener éxito debía ser muy parecido al que en el momento de su lanzamiento fuese el más popular. Así, Java se creó con un gran parecido al C++. Pero Java es un lenguaje más puro de programación orientada a objetos, conserva un poco del C original, pero se parece más al C++. Puede verse al C++ como un paso intermedio en la transición de la programación estructurada del C a la programación orientada a objetos más pura del Java. En 2001 apareció un nuevo lenguaje de Microsoft llamado C# (C sharp) que prometía ser aún más puro como lenguaje orientado a objetos, adopta prácticamente todas las mejoras de Java y agrega algunas nuevas. Habrá que estar pendientes de su evolución y de sus posibles ventajas y probables desventajas (en computación nunca se gana algo sin perder otra cosa, lo importante es que en promedio las cosas vayan mejorando).

Java tiene otras virtudes aparte de ser un excelente lenguaje de programación orientada a objetos, virtudes que lo hacen el lenguaje de programación preferido para internet. Pero también tiene la peculiaridad de ser el lenguaje orientado a objetos más popular del momento (2002, antes del lanzamiento de C#).

Las siguientes secciones de esta unidad explican el conceptos de clase, cómo se representa este concepto en Java y las demás características que hacen de Java un lenguaje orientado a objetos.

 

Clases y objetos

El concepto de clase es el más importante del lenguaje Java y el que lo convierte en un lenguaje de programación orientada a objetos.

Todo lo que se programa en Java debe estar dentro de una clase. Un programa es una clase. Por ejemplo, el sencillo programa HelloWorld que vimos en la unidad 2 tiene este código java:

Si su navegador reconociese la etiqueta de applet, aquí vería un applet

en el que puede verse que todo el programa está dentro de una clase llamada "HelloWorld". Dentro de la clase hay un único método llamado "main" que es el que se ejecuta cuando el programa arranca. Esta forma de programar con clases que contienen métodos y variables (HelloWorld no usa variables, pero podría usarlas) es a lo que se ha llegado después de varios años de experiencia en la programación orientada a objetos.

La palabra clase viene del concepto "clase de objetos". Una clase consta de (a) un conjunto de variables que representa el estado de un objeto y (b) un conjunto de métodos que definen el funcionamiento y las operaciones que pueden realizarse sobre los objetos de esa clase.

Como ejemplo consideremos el caso de un punto en el plano cartesiano que puede desplazarse a la izquierda, a la derecha, hacia arriba y hacia abajo.

El estado de este punto queda determinado por dos variables:

X, la abscisa al origen
Y, la ordenada al origen

Además de las variables X e Y hay que incluir los métodos que definen el "funcionamiento" de la clase y además conviene agregar otros métodos auxiliares.

He aquí una definición de la clase punto.

Si su navegador reconociese la etiqueta de applet, aquí vería un applet

A continuación explicamos detalladamente el significado de cada una de las variables y los métodos de esta clase.

X e Y aparecen definidas como variables. En la unidad 3 se estudian todos los tipos de variables del lenguaje Java. Los métodos setX y setY sirven para fijar los valores de X e Y, mientras que getX y getY sirven para obtener los valores de estas variables. Los métodos moveLeft, moveRight, moveUp y moveDown definen el "funcionamiento" de la clase punto, es decir, las operaciones que se pueden realizar sobre un punto.

Los métodos setX y setY admiten un párametro cada uno. El valor de ese parámetro se utiliza para fijar el valor de la abscisa del punto y el valor de la ordenada, respectivamente. Los métodos getX y getY no aceptan parámetros. Los métodos moveLeft, moveRight, moveUp y moveDown aceptan un solo parámetro que se interpreta como la magnitud del desplazamiento que se realiza.

Todos los métodos, excepto getX y getY, tienen valor de devolución "void" (vacío), lo que quiere decir que no devuelven valor alguno. Los métodos getX y getY devuelven un valor de tipo "int", es decir, un número entero.

El último "método" que tiene el mismo nombre que la clase y no tiene valor de devolución, ni siquiera "void", es un constructor. Los constuctores se estudian en la siguiente sección.

 

Creación de objetos

Para crear un objeto nuevo en Java se antepone la palabra new a un constructor de la clase del objeto que se desea crear.

Por ejemplo, para crear un punto con coordenadas (2,3) basta escribirlas dos líneas:

punto p;
p=new punto(2,3);

La primera línea simplemente declara que p es una variable de tipo punto. La segunda línea es la que en realidad "crea" el nuevo objeto p. Los objetos de una clase se llaman también instancias de la clase.

Toda clase tiene al menos un constuctor. Los constructores pueden tener uno o varios parámetros, y pueden no tener parámetros. Si en una clase no se define explícitamente un constructor, entonces tiene sólo un constructor sin parámetros.

Cuando se crea un objeto en realidad se aparta espacio de memoria para contener las variables del objeto. Un constructor suele además inicializar los valores de las variables del objeto y al hacerlo puede crear otros objetos. Cuando un objeto deja de usarse en un programa porque ya no se hace referencia a él, entonces el espacio de memoria apartado para sus variables queda disponible para que el controlador de memoria RAM de Java lo utilice nuevamente. En Java el programador no tiene que preocuparse de destruir los objetos para liberar memoria (aunque si lo desea puede intervenir acelerando el proceso de recuperación de la memoria liberada usando System.gc() que ejecuta el recolector de basura).

Para hacer referencia a las variables de un objeto, se escribe el nombre del objeto, un punto y el nombre de la variable correspondiente. Por ejemplo:

p.X=7;

asigna el valor 7 a la variable X del objeto p.

 

Métodos

Los métodos de un objeto son funciones que pueden operar sobre las variables del objeto.

Para hacer referencia a un método de un objeto, se escribe el nombre del objeto, un punto y el nombre del método agregando entre paréntesis los parámetros del método o bien unos paréntesis sin nada dentro si el método no acepta parámetros.

Por ejemplo, si p es un punto, entonces para llamar al método que lo mueve 3 unidades a la derecha, basta escribir:

p.moveRight(3);

Éste es el código de un pequeño programa que utiliza la clase punto. El programa crea un punto llamado p con coordenadas (10,10). Luego pregunta el valor de la abscisa y la ordenada (les asigna valor 10 si hay algún error), a continuación escribe el valor de la abscisa, llama al método moveLeft de p con parámetro 5, con lo cual desplaza al punto cinco unidades a la izquierda, y escribe el nuevo valor de la abscisa de p.

Si su navegador reconociese la etiqueta de applet, aquí vería un applet

Como ejercicio el alumno debe ir al DOS, compilar punto01.java y ejecutarlo. Podrá comprobar por ejemplo que si antes de la llamada a p.moveLeft(5) la abscisa vale 8, después vale 3.

Observe cómo se usa el constructor para crear un objeto p, es decir una instancia de la clase, que es lo que utiliza en el programa. Observe que el programa no manipula directamente las variables de p sino que lo hace a través del método moveLeft.

Una observación que no tiene que ver con el contenido de esta unidad pero que viene al caso por el uso que se ha hecho en este ejemplo es la siguiente: la función System.out.println permite que se le pase un parámetro "mixto" de una cadena (String) y un número unidos por un + que actúa concatenando la primera cadena con la que resultaría de convertir el número a una cadena. Este funcionamiento "inteligente" de println es muy útil pero es atípico de Java. El alumno no debe esperar que otros métodos funcionen así. En general estas conversiones deberá hacerlas el programador explícitamente.

 

Herencia y extensión de clases (subclases)

Además de contener las variables y los métodos, las clases tienen otra función muy importante en la programación orientada a objetos: la herencia.

Una clase puede ser subclase o extensión de otra. En tal caso se dice que hereda las variables y los métodos de la otra.

Para aclarar este concepto crearemos una subclase de punto llamada circulo.

Si su navegador reconociese la etiqueta de applet, aquí vería un applet

La nueva clase circulo tiene una nueva variable R que interpretaremos como el radio. Pero hereda las variables X e Y de la clase punto. Interpretaremos las variables heredadas como las coordenadas del centro del círculo. La clase circulo también hereda los métodos de punto con los cuales se determina el "funcionamiento" del centro, pero tiene unos métodos nuevos para fijar el valor del radio, para informar dicho valor y para aumentarlo y disminuirlo.

El hecho de que circulo sea una subclase de punto se determina en la línea que define la nueva clase indicando que "extiende" a punto:

public class circulo extends punto

Cuando una clase B es extensión de otra clase A, se dice que B es subclase de A y que A es la superclase de B.

Como se verá en el ejemplo de la última sección de esta unidad, al utilizar la clase circulo no se hace ninguna distinción entre las variables y métodos propios y los heredados de la superclase punto.

 

Modificadores

Las palabras public y static que el alumno habrá visto en las declaraciones de algunas clases, variables y métodos, se llaman modificadores. Su papel es especificar algunas propiedades para dichas clases, variables o métodos.

La siguiente tabla presenta todos los modificadores y una descripción de su función en el lenguaje cuando se aplica a clases, variables, métodos o interfaces. El significado y la importancia de los modificadores se irán aclarando a lo largo de esta unidad y más adelante con las aplicaciones más avanzadas. Esta tabla debe servir como referencia.

modificador
clases
variables
métodos
interfaces
public visible en todas partes visible donde su clase lo sea visible donde su clase lo sea visible en todas partes
protected no se aplica a clases visible en el mismo paquete y en todas las subclases visible en el mismo paquete y en todas las subclases no se aplica a interfaces
ninguno (default) visible sólo en su paquete visible en el paquete de su clase visible en el paquete de su clase visible sólo en su paquete
private no se aplica visible sólo en su clase visible sólo en su clase no se aplica
abstract la clase tiene métodos no implementados no se aplica no hay implementación, sólo se da el encabezado y se termina con ; todas las interfaces son abstractas (aunque no se declaren así)
final no pueden crearse subclases su valor no se puede cambiar (es constante y el compilador puede pre-calcular el valor) no se puede sobreescribir no se aplica
native no se aplica no se aplica. está implementado en un lenguaje dependiente de plataforma, sólo se presenta el encabezado y se termina con ; no se aplica
static no se aplica es una variable de la clase, sólo hay una para todos los objetos de la clase y por tanto en todos los objetos tiene el mismo valor. Se puede invocar con el nombre de la clase. es un método de la clase, sólo puede usar las variables de clase. Se puede invocar con el nombre de la clase. no se aplica
synchronized no se aplica no se aplica. si es estático se bloquea al ejecutarse, y si no lo es, se bloquea su objeto. El bloqueo impide que se ejecuten otros hilos durante la ejecución del método. Este modificador se requiere cuando el método hace cambios no atómicos a la clase o al objeto no se aplica
transient no se aplica no es parte del estado del objeto (actualmente no se usa) no se aplica. no se aplica
volatile no se aplica se modifica asincrónicamente, puede ser un registro de hardware, siempre que se usa se verifica su valor por si ha cambiado. no se aplica no se aplica

 

Sobrecarga y sobreescritura de métodos

En Java varios métodos de una misma clase pueden tener el mismo nombre. Por ejemplo:

public static String valueOf(int i)

y

public static String valueOf(double d)

son dos métodos estáticos de la clase String que tienen el mismo nombre (e incluso los mismos modificadores: public y static), pero en cambio tienen diferentes parámetros. La posiblidad de definir varios métodos con el mismo nombre que sólo se distinguen por recibir diferentes parámetos, se llama sobrecarga. Se trata de algo muy sencillo, en realidad lo que ocurre es que el compilador de Java utiliza todo el encabezado como nombre de un método, así es que si dos métodos tienen diferente número de parámetros o alguno de ellos es de diferente tipo, eso basta para que sean diferentes para el compilador. Sin embargo al compilador no se le puede engañar tratando de definir dos métodos que sólo difieren en el nombre de sus parámetros...

La sobrecarga es muy útil en casos donde se necesitan varios métodos para realizar alguna función sobre objetos de diferente tipo como en el ejemplo indicado en donde se trata de una función que devuelve la cadena de dígitos (y punto decimal) que expresa la representación decimal de un número. Pero en un caso el número es de tipo int y en el otro de tipo double. Las implementaciones de ambos métodos son diferentes, el segundo debe hacer más trabajo pues debe distinguir dónde poner el punto decimal.

La sobrecarga se utiliza mucho y es muy cómoda para el programador pues le permite usar la misma nomenclatura para métodos análogos, con lo cual se ahorra tener que recordar nombres diferentes.

Ahora estudiaremos otra posibilidad que ofrece Java relacionada con los métodos que se llama la sobreescritura y que no debemos confundir con la sobrecarga.

Los métodos de una clase pueden sobreescribirse en sus clases derivadas. Consideremos los ejemplos siguientes:


holaApplet es una extensión de helloApplet, y lo único que hace es sobreescribir el método public void start() de su superclase helloApplet. La sobreescritura consiste en crear un método con el mismo nombre de alguno de la superclase. En la ejecución de la subclase se ejecuta el método sobreescrito y no el de la superclase.

Si una clase B es extensión de otra clase A y sobreescribe un método de A, entonces tiene la posibilidad de utilizar ambos métodos. El suyo propio lo utiliza llamándolo por su nombre, el de la superclase lo utiliza anteponiendo super. al nombre del método. Por ejemplo, si en holaApplet se quisiera utilizar el método public void start() de helloApplet en lugar del suyo propio, tendría que llamarlo así:

super.start();

La sobreescritura es muy útil pues permite aprovechar todo el código que nos interese de una clase que ya existe y solamente sobreescribir las partes que nos interesa modificar. Esto se hace extendiendo la clase original para conservarla intacta y realizando sólo el trabajo necesario en la subclase, como se ilustra en el sencillo ejemplo anterior donde se aprovecha el trabajo hecho en los métodos public void init() y public void paint(Graphics g) de helloApplet en los que se definen los colores, la fuente y se centra el mensaje, y solamente se sobreescribe public void start() substituyendo el mensaje.

 

Clases abstractas

Se dice que una clase es abstracta si tiene métodos no implementados. Los métodos no implementados constan sólo de un encabezado con los modificadores, el nombre y los parámetros y en lugar de la implementación entre llaves, se pone punto y coma. Por ejemplo

abstract public void miMétodo(int x,String s);

podría ser la declaración de un método no implementado de una clase abstracta. El modificador abstract es necesario, sin él el compilador marcaría un error al encontrar  ;  en lugar de la llave { .

Una clase que tiene métodos no implementados, debe declararse abstract. Por ejemplo:

Aquí vería un applet si su navegador reconociese la etiqueta de APPLET.

es una clase abstracta. A partir de esta clase hemos creado tres subclases llamadas triangulo, cuadrado y circunferencia, que implementan el método void dibujar(Graphics g) como corresponde al nombre de cada una. El applet que se muestra a continuación utiliza un objeto cd de la clase abstracta cosaDibujable para presentar la figuras. Al pulsarse uno de los botones se crea un objeto de la subclase correspondiente, se asigna a la variable cd y se dibuja. El método public void paint(Graphics g) simplemente llama al método void dibujar(Graphics g) de cd sin hacer distinción de la realización concreta de cd en ese momento. Esto es posible porque las clases triangulo, cuadrado y circunferencia son extensiones de la clase abstracta cosaDibujable y esto garantiza que cada una implementa su propio método void dibujar(graphics g).

Este es el código del applet:

Aquí vería un applet si su navegador reconociese la etiqueta de APPLET.

y éstos los códigos de las tres clases derivadas :

Aquí vería un applet si su navegador reconociese la etiqueta de APPLET.

Aquí vería un applet si su navegador reconociese la etiqueta de APPLET.

Aquí vería un applet si su navegador reconociese la etiqueta de APPLET.

Podrían crearse muchas otras extensiones de cosaDibujable y el mismo applet dibujador podría encargarse de dibujarlas.

Las clases abstractas son muy útiles para crear familias de clases que comparten cierta funcionalidad pero la implementación concreta de algunos de sus métodos pueden ser diferentes. El siguiente ejemplo ilustra esta aplicación de las clases abstractas. Primero se define la clase abstract public insercionBinaria, que contiene la implementación del método de ordenación por inserción binaria, uno de los más rápidos y útiles. Se define como una clase abstracta porque todo depende del método:

abstract public boolean menor_o_igual(Object o1,Object o2) ;

que compara dos objetos arbitrarios. Si hubiésemos implementado este método habríamos limitado la aplicación de la clase a un solo método de comparación entre objetos de algún tipo. Dejando este método como abstracto abrimos la posibilidad de extender la clase a otras que ordenen objetos concretos. Este es el código de la clase abstacta insercionBinaria:

Aquí vería un applet si su navegador reconociese la etiqueta de APPLET.

Como ejemplos de extensiones concretas de insercionBinaria    presentamos las clases ejecutables:

public class ordenaCadenas y public class ordenaNumeros.

La primera obtiene de los parámetros de ejecución una matriz de cadenas, la ordena y escribe el resultado. Aquí está su código:

Aquí vería un applet si su navegador reconociese la etiqueta de APPLET.

La segunda genera una matriz de veinte números enteros entre 0 y 1000, la escribe, la ordena y la vuelve a escribir:

Aquí vería un applet si su navegador reconociese la etiqueta de APPLET.

El alumno debe estudiar cuidadosamente las tres clases anteriores y hacer un análisis crítico detallado de ellas. En particular debe reflexionar sobre el ahorro de código que se logra con esta organización de los programas de ordenación y sobre la protección que se logra al haber declarado como privados ciertos métodos.

 

Interfaces

Una interfaz es como una clase abstracta que no puede tener ninguna variable y ningún método implementado. Es decir, las interfaces constan exclusivamente de métodos abstractos y constantes (variables declaradas como static final). Si las interfaces son algo así como clases abstractas con restricciones, ¿para qué sirven?

La respuesta está en el hecho de que mientras una clase sólo puede extender a una clase, en cambio puede implementar tantas interfaces como quiera.

Las interfaces son contratos de implementación a los que una clase se compromete.

Como primer ejemplo de uso de una interfaz presentamos otra manera de organizar la ordenación binaria usando una sencilla interfaz y sin usar clases abstractas. Primero creamos una interfaz llamada ordenable con el siguiente código:

Aquí vería un applet si su navegador reconociese la etiqueta de APPLET.

Luego creamos una clase insercionBinariaBis que realiza la ordenación utilizando un objeto ordenable:

Aquí vería un applet si su navegador reconociese la etiqueta de APPLET.

Finalmente creamos el programa para ordenar cadenas como una clase que implementa la interfaz ordenable y crea una instancia de insercionBinariaBis pasándole como parámetro el objeto ordenable en el que se ha implementado el método menor_o_igual.

Aquí vería un applet si su navegador reconociese la etiqueta de APPLET.

Conviene observar algunos detalles. Por ejemplo, el método menor_o_igual debe aparecer como public    porque todos los métodos de una interfaz son implícitamente abstractos y públicos. En segundo lugar, la clase final ordenaCadenasBis no es extensión de nada, por lo que la posibilidad de convertirla en extensión de cualquier otra clase queda libre gracias a que el ser ordenable se lo hemos dado diciendo que implementa una interfaz y no que extiende una clase abstracta. Por tal motivo, en este caso al menos parece más cómoda la organización de los programas de ordenamiento usando la interfaz ordenable que la clase abstracta de la sección anterior.

En muchos casos el programador puede elegir entre estas dos opciones pues muchas veces una clase abstracta se puede sustituir por una clase concreta que opera sobre un objeto de una clase que implementa una interfaz que contiene todos los métodos abstractos de la clase abstracta, como ha ocurrido en este caso. Queda a criterio del programador cual de las opciones le resulta más cómoda. La ventaja principal de usar interfaz en lugar de clase abstracta suele radicar en la posiblidad de múltiples implementaciones contra la unicidad de la extensión.

 

Análisis de un applet

Ahora presentamos un ejemplo de un applet en el que se utiliza la clase circulo.

Este applet utiliza la clase circulo y permite al usuario controlar un objeto de esa clase:
Si su navegador reconociese la etiqueta de applet, aquí vería un applet

Estudie el código de circApplet. Encuentre las líneas donde se crea el objeto c y donde se hacen llamadas a los métodos del objeto.

No se preocupe si no entiende algunos detalles del ejemplo. Es natural pues aún no se ha explicado todo lo que se utiliza en él. Ahora explicaremos todas las partes del applet. En los próximos capítulos se estudiará detalladamente cada uno de los conceptos que aquí sólo se mencionan de manera introductoria.

Es importante que el alumno preste atención a esta explicación para adquirir una visión global de lo que estudiará en las siguientes lecciones. A lo largo de la explicación se introducen por primera vez los conceptos más importantes de la programación en Java y en particular los de la programación de un applet.

La clase circApplet es un Applet, es decir, es una subclase de la clase Applet. Esto se especifica en la línea donde se define circApplet que dice:

public class circApplet extends Applet implements ActionListener

En esta línea se está diciendo (además de otras cosas que se explican más adelante) que la clase que estamos construyendo o definiendo es una extensión de la clase Applet. Esto quiere decir que circApplet posee todas las variables y todos los métodos de la clase Applet, además de los que a continuación se definen explícitamente. Pero que contenga las variables y métodos de la clase Applet significa mucho más de lo que podría parecer en un principio. La clase Applet por definición es una extensión de la clase Panel, que es a su vez extensión de Container, que es extensión de Component que es extensión de Object. Object es la clase básica de la que todas las demás son extensión. Cuando se define una clase y no se dice que es extensión de alguna otra, como se hizo en el caso de circulo, de todas formas es extensión de Object. Por tal motivo, un applet tiene todos los métodos y variables de todas las clases de las que se deriva directa o indirectamente.

A continuación de la declaración de que circApplet es una extensión (o subclase) de Applet, se definen dos variables, una de tipo circulo que se llama c, y otra que es una matriz de 6 botones y se llama b.

Luego aparecen seis métodos cuya función es ofrecer al usuario las operaciones concretas de mover el círculo 10 unidades en cada dirección y aumentar o disminuir el radio en 5 unidades. Hay otro método llamado redraw que se encarga de ofrecer al usuario la opción de dibujar el círculo después de cada movimiento (para entender cómo funciona redraw debe leer la última parte de esta unidad donde se explica el método paint). Estos siete métodos se declaran como públicos para que se puedan usar desde otro programa. En particular se usarán desde la página donde se insertará el applet y se usarán a través de JavaScript. En los ejercicios se aprovecha el hecho de que las funciones son públicas. Dentro del applet se usan también las funciones pero para ello no haría falta que fuesen públicas.

A continuación aparece el método

public void init()

Éste es uno de los métodos de la clase Applet. Cuando un navegador abre un applet lo primero que hace es ejecutar el método init() y por tanto es allí donde se debe realizar la construcción del applet. El hecho de definir otra vez este método significa que deseamos "sobreescribirlo", es decir, deseamos modificar su contenido. El contenido original de init() es vacío, por lo tanto si no lo sobreescribimos, no hace nada.

En nuestra implementación de init() creamos los seis botones que servirán para que el usuario pueda mover el círculo. Para crear los botones usamos el constructor de la clase Button y aprovechamos a darle un título a cada botón que nos indique lo que ocurrirá al pulsarlo.

Después aparece el método

public void start()

Éste es otro de los métodos de la clase Applet y el navegador lo ejecuta automáticamente cada vez que se carga el applet y cuando ya está definido su tamaño. Este hecho es muy importante porque en el método init no pueden saberse todavía el ancho y alto del applet. En el ejemplo sobreescribimos el método start para crear en él el objeto circulo que va a utilizarse en el programa.   Al crear el círculo c usamos el constructor de la clase y allí decidimos que su centro tenga inicialmente las coordenadas del centro del panel en donde se dibuja y que su radio sea 40.

La llamada a un constructor siempre se hace poniendo el nombre de una variable de la clase que se va a crear, un signo = y la palabra "new" precediendo al constructor con sus parámetros. La palabra "new" nos indica que se creará un objeto. Esta creación va acompañada de un reclamo de memoria del sistema para acomodar las variables del objeto. Sin embargo el programador de Java no tiene que preocuparse de pedir al sistema esa cantidad de memoria ni de liberarla cuando ya no la utilice. Ambas operaciones las realiza el intérprete de Java automáticamente.

Las últimas seis líneas del método init() realizan las siguientes operaciones:
- Se escoge que el color del fondo del applet sea negro;
- Se escoge que la manera de acomodar los componentes en el applet será con el despliegue
"BorderLayout" ;
- Luego se crea un Panel auxiliar que se coloca al "Sur" del applet aprovechando el "BorderLayout"
- En el panel auxiliar se colocan los seis botones tras asignarle al Panel un modelo para acomodar las componentes del tipo "GridLayout" con una fila y seis columnas y se les agrega a los botones el applet como "escucha".

Nota: Del tema de los despliegues o "Layouts" se habla un poco más en la unidad 8 y se explica detalladamente en el segundo curso de Java, lo mismo el tema de los escuchas o "Listeners".

Como hemos dicho anteriormente, los métodos init() y start() son dos de los métodos predefinidos de la clase Applet y nosotros los hemos sobreescrito haciendo que se encarguen de crear el círculo y los botones para moverlo y de acomodar esos botones de alguna manera. Como ya dijimos, init() es el primer método que se llama cuando el navegador crea un applet.  init() sólo se llama una vez en la vida del applet y por ello conviene utilizarlo para construir el applet. En la unidad 6 estudiaremos todos los métodos de Applet.

En nuestro circApplet hay dos métodos más que explicar: actionPerformed y paint.

public boolean actionPerformed(ActionEvent ev)

es un método de la interfaz ActionListener que nuestro Applet se ha comprometido a implementar en la última parte de la línea de definición de la clase:

public class circApplet extends Applet implements ActionListener

La interfaz ActionListener consta de un solo método:

public void actionActionListener (ActionEvent ev)

Este método se ejecuta cada vez que se produce un evento del tipo ActionEvent, en particular, cada vez que se pulsa un botón, siempre y cuando el botón haya sido agregado a la lista de "escuchas" de ActionEvent del applet, lo cual se realiza en la línea del método init() que dice:

b[i].addActionListener(this);

El control de eventos es un tema muy importante que se estudia detalladamente en el segundo curso de Java. Por ahora será suficiente explicar que cuando se pulsa un botón se llama al método actionPerformed pasándole en el parámetro ev información necesaria para interpretar el evento y actuar. El parámetro ActionEvent ev lleva la información de a cual componente (en este caso un botón) se le hizo un click y dentro del método actionPerformed se determina la acción que se va a realizar según el botón pulsado. Para ello se hace un recorrido por todos los botones usando un "for" (esta instrucción y otras que se usan para crear ciclos en la programación, se estudian en la unidad 4). Para separar los seis posibles casos se utiliza un "switch" que es la manera que en Java se hace una disyunción condicional de opción múltiple. Los "switches" y las condicionales binarias, que se hacen con "if" y "else", se estudian también en la unidad 4.

Finalmente explicaremos el método

public void paint(Graphics g)

que es un método heredado de la clase Component. El método paint por defecto es vacío. Hay tres métodos de Component que se encargan de "pintar" en varios pasos las componentes y en particular los applets. Estos son repaint(), update(Graphics g) y paint(Graphics g). El primero es el que se llama cada vez que el sistema decide que se tiene que pintar la componente o parte de ella. repaint() obtiene el ambiente gráfico g de la componente, que es un objeto de la clase Graphics y llama a update(g). update(g) pinta la componente con el color de fondo y llama a paint(g) que por defecto es vacío. Pero el nuevo método paint sí hace algo pues lo hemos sobreescrito, de hecho dibuja el círculo utilizando la función adecuada de la clase Graphics que se llama fillOval y sirve para dibujar círculos y elipses rellenas. A fillOval se le deben pasar como parámetros las coordenadas del punto superior izquierdo del mínimo rectángulo que contiene al "óvalo" y el ancho y alto de dicho rectángulo. Tal vez el alumno piense que sería más cómodo pasarle las coordenadas del centro y los semiejes de la elipse, pero por razones de eficiencia ésta es la función que nos ofrece la clase Graphics.