Java para programadores(4.4). Interfaces, this, super y otros detalles

Interfaces

En algunos lenguajes de programación orientada a objetos, como C++, las clases tienen la posibilidad de extender dos o mas  superclases. Esto es lo que se llama herencia múltiple. El la ilustración que sigue, por ejemplo, la clase E se representa como proveniente directamente de las superclases A y B, mientras que la clase F tienen tres superclases.

Curso de Java, Herencia e Interfaces

Esta característica de herencia múltiple no esta disponible en Java. Los diseñadores de Java intentaron hacer el lenguaje razonablemente simple,  y consideraron que los beneficios de la herencia múltiple no compensaban el aumento de complejidad. Sin embargo, Java tiene otra característica que puede ser usada para lograr algo semejante a la herencia múltiple:la interface.

Ya hemos encontrado el termino «interface» anteriormente, en relación con las cajas negras en general y las subrutinas en particular. Las interfaces de las subrutinas consisten en el nombre de la subrutina, el tipo que retornan, y el numero y tipo de los parámetros. Es la información que necesita si quiere emplear la subrutina. La subrutina también tiene una implementacion: el bloque de código definido que se ejecuta cuando se llama a la subrutina.

En Java, interface es una palabra reservada que tienen un significado adicional. Una «interface" en Java consiste en un conjunto de interfaces de subrutinas, sin que tenga asociada ninguna implementacion. Una clase puede implementar una interface, facilitando la implementacion de cada una de las subrutinas especificadas por la interface.(Una interface también puede incluir definición de constantes, esto es, variables declaradas como final static. Las constantes pueden ser interesantes para facilitar nombres a determinadas cantidades.) A continuación tiene un ejemplo en una sencilla interface Java:

       public interface Drawable {
          public void draw();
       }

Esto se parece mucho a la definición de una clase, excepto que la implementacion del método draw() se ha omitido. La clase que implemente la interface Drawable tienen que proporcionar la implementacion para este método. Naturalmente que puede incluir otros métodos y variables. Por ejemplo:

        class Line implements Drawable {
            public void draw() {
                . . . // Alguna cosa, supongo que dibujar una linea
            }
            . . . // otros métodos y variables
         }
Mientras que una clase solo puede extender otra clase, en cambio puede implementar cualquier numero de interfaces. De hecho, una clase puede extender otra clase e implementar una o mas interfaces. Podemos hacer cosas como esta:
        class FilledCircle extends Circle 
                implements Drawable, Fillable {
           . . .
        }

La clave de todo esto, es que aunque las interfaces no son clases, son algo muy semejante.Una interface es muy semejante a una clase abstracta, esto es la clase que no puede emplearse para construir objetos, pero que puede ser empleada como base para construir otras clases. En una interface las subrutinas son como los métodos abstractos que deben implementarse en cada una de las subclases de la clase abstracta. Y como clase abstracta,  aunque no pueda construir objetos desde la interface puede declarar variables con el tipo definido por la interface. Por ejemplo, si Drawable es una interface, y si Line y FilledCircle son clases que implementan Drawable, puede decir:

       Drawable figure = new Line();  // variable tipo Drawable que se refiere
                                      //  a un objeto de la clase Line
       . . .    // algo relacionado con figuras
       figure.draw();   // llama al metodo draw() de la clase Line
       figure = new FilledCircle();   // ahora, figure referencia un objeto
                                      //  de la clase FilledCircle
       . . .
       figure.draw();   // llama al metodo draw() de la clase FilledCircle

Una variable del tipo Drawable puede referirse a cualquier objeto de cualquier clase que implemente la interface Drawable. Una instrucción como figure.draw().vista anteriormente, es correcta para cualquier clase que implemente el método draw().

Observe que el tipo es algo que podemos emplear para declarar variables. El tipo también se puede usar para especificar el tipo de parámetros en las subrutinas, o el tipo de retorno de una función. En Java, el tipo puede ser cualquier clase, interface o uno de los ocho tipos primitivos internos. De todos estos, sin embargo, solo las clases pueden emplearse para construir nuevos objetos.

Posiblemente, no necesite escribir su propia interface hasta que llegue el momento de escribir un programa bastante complejo. Sin embargo, hay algunas interfaces que se emplean en el package estándar de Java y que tienen bastante importancia, por lo que seria conveniente que supiera algo sobre ellas.


this y super

Dentro de una clase, cuando quiere referirse a una variable o método perteneciente a la misma clase, simplemente indica el nombre. Esto también vale para las variables y métodos heredades de la superclase de la clase. Para variables o métodos de fuera de la clase, sin embargo, tiene que utilizar nombres compuestos para indicar a que objeto pertenecen (o, en el caso de variables o métodos static a que clase pertenecen). Por ejemplo, puede referirse a la función raíz cuadrada como Math.sqrt , porque pertenece a la clase Math. De forma similar, para llamar al método getln() perteneciente al objeto llamado console de la clase Console, tendrá que referirse a él como console.getln(). Por otra parte, si esta escribiendo la clase Console, se podrá referir a él simplemente como getln().

Hay algunas circunstancias, sin embargo, en las que necesita emplear nombres compuestos aunque el miembro sea de la misma clase. Cuando se ejecuta la instancia de un método, debido a que se ha emitido un mensaje desde otro objeto,  el sistema prepara dos variables especiales para referirse al objeto que recibe el mensaje. Las variables se llaman this y super. Puede usar esas variables para definir cualquier instancia del método.

Use la variable llamada this cuando necesite un nombre para «el objeto al que pertenece el método». Puede necesitar el usar this si quiere asignar el objeto a una variable, o pasarle un parámetro. En realidad, esto es muy común. Considere, por ejemplo, la clase que representa iconos en la pantalla del ordenador. Los iconos pueden «seleccionarse», y querrá saber que icono esta actualmente seleccionado. Podría decir:

      class Icon {
         static Icon selectedIcon = null;  // icono seleccionado
         void select() {  // metodo instanciable para seleccionar este icono
            selectedIcon = this;  // guarda que este es el seleccionado
            . . .  // mas cosas
         }
         . . .  // mas variables y métodos
      }

Otro uso de «this» es eliminar ambigüedades cuando hay dos variables o parámetros o variables locales con el mismo nombre. Por ejemplo, considere

      class Shape {
         Color color;  // el color de la forma
         void setColor(Color color) {
            // cambia forma a nuevo color
            this.color = color;  // cambia el valor de la instancia de la variable
            redraw();
         }
         . . . // mas cosas
      }

Dentro del método setColor(), hay dos cosas con el mismo nombre: El parámetro y la instancia de la variable se llaman ambos «color». La norma para tales situaciones, es que el parámetro oculta la instancia de la variable, por lo tanto, cuando se emplea el nombre «color» por si mismo se esta refiriendo al parámetro. Afortunadamente, la instancia de la variable puede ser accedida utilizando el nombre compuesto «this.color«, que solo puede indicar  que es la instancia de la variable del objeto this. (Se preguntara si es razonable usar el mismo nombre en la instancia de la variable y el el parámetro.Es probable; le ahorra el tener que inventar algún nombre divertido para el parámetro como newColor).

La palabra reservada super, en cambio, se refiere a la clase padre de la actual. Por medio de esta palabra, podemos acceder a metodos y propiedades del padre. Entonces, por ejemplo, super.x   se refiere a la instancia de la variable x en la superclase de la clase actual. Esto puede ser útil por las siguientes razones: Si la clase contiene una instancia variable con el mismo nombre que una instancia variable en la superclase, entonces un objeto de esta clase contendrá dos variables con el mismo nombre: una definida como parte de la misma clase, y otra definida como parte de la superclase. La variable en la subclase no reemplaza la variable con el mismo nombre de la superclase; únicamente la oculta. La variable de la superclase puede ser accedida utilizando super.(De nuevo, se puede preguntar si es razonable el llamar a las variables de esta forma. La respuesta es,probablemente no).

De la misma manera, cuando escribe un método en una subclase que tiene la misma firma que un método de la superclase, el método de esta, se oculta. Decimos que el método de la subclase sustituye el método de la superclase. Otra vez, sin embargo, podemos emplear super para acceder al método de la superclase.

El mayor uso de super es para sustituir un método con un nuevo método que extiende el comportamiento del método heredado, en lugar de cambiar totalmente el comportamiento. El nuevo método puede usar super para llamar al método heredado y puede incluir código adicional para proporcionar un comportamiento adicional. Algo así podemos ver con este tonto ejemplo,

       class FilledRectangle extends Rectangle {
          void draw() {
             super.draw();  // llamar al método draw de la clase Rectangle
             fill();   // y añadirle algo
          }
          .
          .

Como un ejemplo mas real, suponga que la clase TextBox representa una caja en la pantalla donde el usuario puede teclear cualquier cosa. Permítame decirle que TextBox es una instancia de un método llamado key que se llama cada vez que el usuario presiona una tecla en el teclado. El propósito del método es añadir el carácter que el usuario a tecleado a la caja de texto. Ahora, puedo querer una subclase,NumberBox, de TextBox que pueda emplear para teclear un numero. Puedo definir la clase NumberBox de la siguiente manera:

        class NumberBox extends TextBox {
           void key(char ch) {
                 // el usuario ha tecleado el carácter ch; ponerlo en la
                 // caja, solo si es un digito
                 // entre el '0' y el '9'
              if (ch >= '0' && ch <= '9')
                 super.key(ch);
           }
         }

Constructores en Subclases

Los constructores existen únicamente para asegurarse que los nuevos objetos empiezan con unos valores iniciales conocidos. Si un objeto es miembro de una subclase, parte de su estructura es heredada de su superclase, y esa parte debe ser inicializada llamando al constructor de la superclase.

Si la superclase tiene el constructor por defecto, esto es, uno sin parámetros,   el sistema puede llamarlo automáticamente. Esto también pasa si en la superclase no se ha definido constructor, dado que entonces el sistema proporciona el constructor por defecto. Sin embargo, si todos los constructores de la superclase requieren parámetros, entonces cualquier constructor que escriba en la subclase, deberá llamar de forma explícita al constructor de la superclase. La sintaxis para hacerlo, emplea la variable especial super. Aquí tenemos un ejemplo. Asuma que la clase TextBox tiene un constructor que especifica el numero máximo de caracteres que caben en la caja. Entonces, puede definir la subclase como:

        class TextBoxWithDefault extends TextBox {
            TextBoxWithDefault(int maxChars, String defaultContents) {
               super(maxChars);  // llama al constructor de la superclase
               setContents(defaultContents);
            }
            . . . // mas cosas
         }

Para un constructor, también es posible llamar a otro de la misma clase. Esto lo podemos hacer empleando la variable this en lugar de la super. La clase TextBoxWithDefault, por ejemplo, puede tener un constructor

TextBoxWithDefault() {

this(20,»»);

}

 

Este constructor existe únicamente para llamar a otro constructor mas complicado y proporcionarle parámetros con valores razonables.


Control de Acceso

Ya se habrá dado cuenta que tanto las clases como sus miembros pueden declararse como public o private.Estos modificadores, se usan para controlar los accesos. La clase, método o variable que se declara como public puede ser accedida desde cualquier sitio. Los métodos o variables que se declaran como private solo puede ser accedida dentro de la clase donde se define. ( Definir una clase como private, no tiene mucho sentido, no podría usarse nunca!)

También hay un nivel de control de acceso intermedio llamado protected Un método o variable que se declare así, puede ser accedido dentro de la misma clase o por cualquier subclase de la clase.Debe utilizar el acceso protegido si quiere ocultar algún detalle de la implementacion de la clase al «mundo exterior» pero quiere poder sustituir la implementacion en las subclases.

En mis ejemplos, he tenido muy poco cuidado sobre las especificaciones del control de acceso. En la mayoría de casos, me he limitado a omitir el modificador de control de acceso totalmente. Recuerde que cuando no se especifica control de acceso, se permite el acceso desde todas las clases del mismo paquete (package). Y si no especifica paquete, entonces es todo lo que va en el paquete por defecto. En este caso, todo su programa es visible a todo el mundo. Esto no es tan terrible para los pequeños programas de demostración, pero para los programas importantes, el control de acceso es uno de los mejores sistemas de seguridad y muy útil para diseñar herramientas.

Acerca de Miguel Garcia

Programador, Desarrollador web, Formador en distintas areas de informatica y director de equipos multidisciplinares.
Esta entrada fue publicada en Formacion, Java y etiquetada , . Guarda el enlace permanente.

Una respuesta a Java para programadores(4.4). Interfaces, this, super y otros detalles

  1. Pingback: Java - Un resumen organizado....o no.Recursos para formacion

Deja un comentario

Este sitio usa Akismet para reducir el spam. Aprende cómo se procesan los datos de tus comentarios.