Download Clases e Interfaces Anidadas
Document related concepts
no text concepts found
Transcript
Java Clases e Interfaces Anidadas H. Tejeda V htejeda@fismat.umich.mx Universidad Michoacana Facultad de Ciencias Fı́sico-Matemáticas Java– p. 1 Introducción • Se pueden declarar clases e interfaces dentro de otras clases e interfaces, como miembros o en bloques de código. • Se definen tipos anidados por lo siguiente: ◦ Las clases e interfaces permiten estructurar los tipos y su alcance en grupos relacionados lógicamente. ◦ Las clases anidadas se pueden usar para conectar objetos relacionados lógicamente de forma simple y efectiva, como en frameworks de eventos (AWT y en la arquitectura de componentes JavaBeans). • Un tipo anidado se considera parte del tipo que lo contiene, y comparten una relación de confianza, donde cada uno puede acceder a los miembros del otro. • Las diferencias entre los tipos anidados dependen de que sea una clase o interfaz. • Los tipos anidados pueden declararse estáticos o no. Con el primero se permite una estructuración simple de los tipos, y el segundo define una relación especial entre un objeto anidado y un objeto de la clase que lo contiene. Java– p. 2 Tipos Anidados Estáticos • Cuando una clase o interfaz anidada se declara como un miembro static de la clase o interfaz que lo contiene, actúa como cualquier clase o interfaz no anidada, o de nivel superior, excepto que el nombre y accesibilidad se definen por el tipo que la incluye. • El nombre de un tipo anidado se expresa como NombreDelQueIncluye.NombreDelAnidado siendo accesible cuando el tipo que lo incluye es accesible. • Sirven como mecanismo de estructuración de gestión del alcance para tipos relacionados lógicamente. • Los tipos anidados estáticos son miembros del tipo que los contiene, por lo cual pueden acceder a todos los miembros del tipo que los contiene. • Como los tipos anidados estáticos son miembros del tipo que los contiene, son aplicables para ellos las mismas reglas de accesibilidad que las de los otros miembros. Java– p. 3 Clases Anidadas Estáticas • Es la forma más simple de clase anidada y se declara precediendo al nombre de la clase con el modificador static. • Si una declaración de clase se anida en una interfaz, la clase es siempre estática. • Una clase anidada se comporta como cualquier clase de nivel superior, pudiendo ser extendida por otra clase y/o implementar cualquier interfaz. • Se puede declarar final o abstract la clase anidada estática. public class CuentaBancaria { private long numero; private long balance; public static class Permisos { public boolean puedeDepositar, // Campos no estáticos puedeRetirar, puedeCerrar; } public Permisos permisosDe ( String nomb ) { .... } // ... } Java– p. 4 Comentarios • La clase Permisos se define en la clase CuentaBancaria siendo un miembro de la clase. • El método permisosDe devuelve un objeto Permisos el cual puede ser llamado sin calificador. • El nombre completo de la clase es CuentaBancaria.Permisos. • El código exterior a la clase CuentaBancaria debe usar el nombre completo. Por ejemplo: CuentaBancaria.Permisos perm = cuenta.permisosDe(propietario); • La clase anidada se ve como parte de la implementación de la clase que la incluye, y por tanto, es de total confianza. • Las clases anidadas estáticas se pueden declarar accesibles de la forma que se desee. • No hay restricciones para extender una clase anidada, pudiendo ser extendida por cualquier clase para la que sea accesible. La clase extendida no heredará el acceso privilegiado que la clase anidada tiene en la clase que lo incluyen. Java– p. 5 Interfaces Anidadas • Las interfaces anidadas son siempre estáticas. • Sirven como un mecanismo de estructuración de tipos relacionados. Java– p. 6 Clases Internas • Las clases anidadas no estáticas se denominan clases internas. • Una clase interna se asocia con una instancia de la clase, es decir, un objeto de una clase interna se asocia siempre con un objeto de la clase que la incluye. • Se muestra en el siguiente código un método para la clase CuentaBancaria que permite ver la última acción realizada en la cuenta. • La clase Accion almacena una sola acción sobre la cuenta. • La relación entre un objeto Acción y su objeto CuentaBancaria se establece cuando se crea el objeto Accion en los métodos deposito y reintegro, asociándose con un objeto de la clase que la incluye. • Por lo general, los objetos de las clases internas se crean dentro de métodos de instancia de la clase, donde el objeto en curso this se asocia por defecto con el objeto interno. Java– p. 7 Ejemplo public class CuentaBancaria { private long numero; private long balance; private Accion ultimaAcc; public class Accion { private String operacion; private long cantidad; Accion(String operacion, long cantidad) { this.operacion = operacion; this.cantidad = cantidad; } public String toString() { return numero + ": " + operacion + " " + cantidad; } } public void deposito(long cantidad) { balance += cantidad; ultimaAcc = new Accion("deposito", cantidad); } ⇓ Java– p. 8 Ejemplo cont. public void retiro(long cantidad) { balance -= cantidad; ultimaAcc = new Accion("retiro", cantidad); } // ... } Java– p. 9 Acceso a los Objetos que Incluyen • Una clase anidada puede acceder a todos los miembros de la clase que la contiene (incluyendo los campos y métodos privados), sin necesidad de calificación, por ser parte de la implementación de la clase que la incluye, basta con nombrar los miembros, por los que los nombres de la clase están dentro del alcance, por ejemplo cuando se usa el campo numero del objeto CuentaBancaria en el método toString() de la clase anidada. • La clase que incluye puede acceder a los miembros privados de la clase interna, usando una referencia explícita a un objeto de dicha clase interna, como UltimaAcc. Java– p. 10 TAREA 1. Poner un método accesor en la clase CuentaBancaria para poner el número de cuenta. Crear una referencia del tipo CuentaBancaria para realizar un depósito por 100 y un retiro por 25 para la cuenta 54321. Java– p. 11 Extensión de Clases Internas • Una clase interna se puede extender como cualquier otra clase anidada estática o clase de nivel superior, siempre y cuando los objetos de la clase extendida se asocien con objetos de la clase original que incluye, o con una subclase. class Externa { class Interna { } } class ExternaExtendida extends Externa { class InternaExtendida extends Interna { } Interna ref = new InternaExtendida(); } Java– p. 12 Extensión de Clases Internas • El campo ref se inicializa al crear un objeto ExternaExtendida. • La creación de la instancia de ExternaExtendida usa el constructor por defecto sin argumentos de InternaExtendida, que a su vez invoca implícitamente al constructor sin argumentos por defecto de Interna usando super. • El constructor de Interna requiere ligarse a un objeto de Externa, que en este caso es implícitamente el objeto actual de ExternaExtendida. Java– p. 13 Herencia, Alcance y Ocultación • Dentro de una clase interna, todos los nombres declarados en la clase que la incluyen están dentro del alcance. • Los campos y métodos propios de la clase interna pueden ocultar a los del objeto que la incluye, sucediendo de las siguientes 2 formas: ◦ Un campo o método se declara en la clase interna. ◦ Un campo o método es heredado por la clase interna. • Para el primer caso, cualquier uso del nombre simple se refiere a la declaración dentro de la clase interna. • Para acceder al campo o método del objeto que la incluye, debe usarse la nomenclatura con this. • En el segundo caso el nombre debe ser calificado explícitamente usando this o super, para referirse al nombre en la clase interna, o NombreExterno.this para referirse a un miembro de la clase que la incluya. Java– p. 14 Ejemplo class Host { int x; class Ayudante extends Desconocida { void incrementar() { x++; } } } class Desconocida { int x; } • Parece que el método incrementar incrementa el campo x de la instancia de Host que incluye a Ayudante. • En Desconocida se declara un campo x, el cual es heredado por Ayudante. • El campo x heredado oculta al campo x de Host. • Los usuarios que lean el código tendrían una impresión equivocada sobre lo que sucede, y más cuando no saben que campos tiene la clase Desconocida, por lo que se recomienda calificar explícitamente la referencia a x para saber a que x se refiere: ◦ this.x ◦ Host.this.x Java– p. 15 TAREA 2. Crear un objeto del tipo Host para incrementar x usando el método incrementar de la clase ayudante. Poner un método que muestre los valores de x, tanto el campo x de la clase host como el de la clase que se extiende. Poner otro método que incremente el campo x de la clase Host dentro de la clase Ayudante. Java– p. 16 Ejemplo • Un método de una clase interna con el mismo nombre que un método de la clase que la incluye, oculta todas las formas sobrecargadas del método de la clase externa, incluso aunque la clase interna no declare esas formas sobrecargadas. class Externa { void imprimir() { } void imprimir(int val) { } class Interna { void imprimir() { } void mostrar() { imprimir(); Externa.this.imprimir(); imprimir(1); // Error: no Interna.imprimir(int) } } } Java– p. 17 Ejemplo cont. • La declaración de Interna.imprimir oculta a todas las formas sobrecargadas de la declaración de Externa.imprimir. • Cuando Interna.mostrar invoca a imprimir(1), el compilador informa de que Interna no tiene ningún método imprimir que tome un parámetro entero. • mostrar debe calificar explícitamente la invocación con Externa.this Java– p. 18 Clases Internas Locales • Se definen en el cuerpo de un método, en un constructor o en un bloque de inicialización. • No son miembros de la clase de la cual es parte el bloque de código, sino que son locales al bloque, al igual que las variables locales. • Son totalmente inaccesibles desde el exterior del bloque de código donde se define, pero las instancias de esas clases son objetos normales que pueden ser pasados como parámetros, ser valores de retorno de métodos, y que existen hasta que ya nadie más se refiere a ellas. • El único modificador que se les puede aplicar es final. • Puede acceder a todas las variables que estén dentro de alcance allí donde dicha clase define, por ejemplo: variables locales, parámetros de métodos, variables de instancia y variables estáticas. • Pueden acceder a una variable o parámetro local de un método si están declarados como final. Si es necesario, se puede copiar una variable no final en una final, que será accedida después por la clase interna local. Java– p. 19 Ejemplo - Clase Interna Local public static Iterator recorridoPor(final Object[] objs) { class Iter implements Iterator { private int pos = 0; public boolean hasNext() { return (pos < objs.length); } public Object next() throws NoSuchElementException { if (pos >= objs.length) throw new NoSuchElementException(); return objs[pos++]; } public void remove() { throw new UnsupportedOperationException(); } } } Java– p. 20 Clase Interna Local – Comentarios • Método que devuelve un Iterator para recorrer un arreglo de objetos. • La clase Iter es local en el método recorridoPor. • Como Iter es local al método, tiene acceso a todas las variable finales del método, por ejemplo objs • Los miembros de las clases locales internas pueden ocultar las variables y parámetros locales del bloque de código donde se declaran, o bien, pueden ocultar campos y métodos de instancia. Java– p. 21 Clases Internas Anónimas • Pueden usarse cuando una clase interna local parece demasiado para los requerimientos del programa. • Las clases se definen en la propia expresión de new, como parte de una sentencia. • El tipo especificado en new es el supertipo de la clase anónima. • Como Iterator es una interfaz, la clase anónima en recorridoPor extiende implícitamente a Object e implementa a Iterator. Java– p. 22 Clases Internas Anónimas – Ejemplo public static Iterator recorridoPor(final Object[] objs) { return new Iterator() { private int pos = 0; public boolean hasNext() { return (pos < objs.length); } public Object next() throws NoSuchElementException { if (pos >= objs.length) throw new NoSuchElementException(); return objs[pos++]; } public void remove() { throw new UnsupportedOperationException(); } }; } Java– p. 23 Herencia de Tipos Anidados • Los tipos anidados, sean clases o interfaces estáticas, o clases internas, se heredan de la misma forma que se heredan los campos. • Una declaración de un tipo anidado con el mismo nombre que el de un tipo anidado heredado oculta la definición del tipo heredado. • El tipo real referido se determina por el tipo de referencia usada. • Dentro de una clase dada, el tipo anidado real referido es el definido en la clase actual o el heredado en la clase actual. Java– p. 24 Anidamiento de Interfaces • Las clases e interfaces anidadas permiten asociar tipos que están fuertemente relacionados con una interfaz dentro de esa interfaz. interface Cambiable { class Grabar { public Object cambiador; public String descCambiador; } Grabar getUltimoCambio (); // ... } Java– p. 25 Anidamiento de Interfaces cont. • El método getUltimoCambio devuelve un objeto Cambiable.Grabar que contiene el objeto que realizó el cambio y una cadena que describe el cambio. • La clase tiene sólo significado para la interfaz Cambiable. por lo que no se justifica que sea una clase de nivel superior. • Otro uso de las clases anidadas dentro de una interfaz es definir una implementación por defecto (parcial o completa) de dicha interfaz, donde una clase que implemente la interfaz puede elegir extender la clase o redirigir llamadas de métodos a una instancia de clase. • Cualquier clase o interfaz anidada dentro de otra interfaz es pública y estática, al igual que los miembros de las interfaces. Java– p. 26 Variables Modificables en Interfaces • Cuando se necesitan datos compartidos que sean modificables en una interfaz, el uso de una clase interna es la forma más simple de conseguirlo. • En la clase interna se declaran los campos que almacenan los datos compartidos y cuyos métodos proporcionen acceso a esos datos, y se mantiene una referencia a una instancia de esa clase. interface DatosCompartidos { class Datos { private int x = 0; public int getX() { return x; } public void setX(int nuevoX) { x = nuevoX; } } Datos datos = new Datos(); } Java– p. 27 Implementación de Tipos Anidados • Los tipos anidados se añadieron como una extensión al lenguaje que tuvo que mantener compatibilidad con anteriores máquinas virtuales de Java, con lo que los tipos anidados se implementaron en términos de transformaciones del código fuente que aplica el compilador. Java– p. 28