jueves, 4 de marzo de 2010

Nested Classes, las grandes desconocidas.

¿Qué son las Nested Classes? Básicamente son clases embebidas dentro de otras clases, en castellano sería algo así como clases internas. Se pueden subdividir en 2 categorías:

  • Clases internas estáticas (Static Nested Classes)
  • Clases internas no estáticas (Inner Classes)

Clases internas estáticas

Una clase interna de este tipo puede interactuar con la clase contenedora igual que lo haría cualquier otra clase externa a la clase contenedora. Es decir, una clase interna estática no puede acceder a las variables de instancia o métodos de la clase contenedora de forma directa y viceversa, debe hacerlo a través de una referencia a la clase contenedora (igual que lo harían el resto de clases). Esto se ve mejor con un pequeño ejemplo:

   1: public class ClaseContenedora {



   2:     private int a;



   3:     



   4:     public ClaseContenedora(int n){ this.a=n; }



   5:     



   6:     public int getA(){ return this.a; }



   7:     



   8:     public static class ClaseInterna{    



   9:         public void imprimirClaseContenedora(ClaseContenedora cc){



  10:             //System.out.println(a);



  11:             System.out.println(cc.getA());



  12:         }    



  13:     }



  14:     



  15:     public static void main(String[] args) {



  16:         ClaseContenedora cc = new ClaseContenedora(5);



  17:         ClaseContenedora.ClaseInterna ci = new ClaseContenedora.ClaseInterna();



  18:         ci.imprimirClaseContenedora(cc);



  19:     }



  20: }




En el ejemplo se ve como desde la clase interna no se pueden acceder a los miembros de la clase contendora directamente si no es por medio de una referencia a la clase contenedora. Si se descomenta la línea 10 el compilador dará un error, ya que la clase interna no puede acceder al atributo “a” de la clase contenedora.

Clases internas no estáticas o Inner Classes

Son las mas empleadas, tanto para esconder funcionalidad dentro de una clase, como para el manejo de eventos.

Las Inner Classes si son capaces de acceder directamente a los miembros de la clase contenedora (incluso si estos han sido declarados como privados). Mientras que la clase contenedora accede imagea la clase interna igual que lo haría con cualquier otra clase externa a la misma (a través de una referencia de la clase). Una instancia de una clase interna puede existir solo dentro de una clase contenedora.

En la imagen se aprecia como la clase interna no estática se encapsula dentro de la clase contenedora quedando totalmente oculta al exterior. Es decir no puede haber instancias de una Inner class fuera de la clase contenedora.

Las Inner class se puede subdividir en 3 grupos:

  • Inner class Miembros
  • Inner class Locales
  • Inner class Anónimas

Inner class Miembros


Es una clase interna declarada como un miembro de la clase contenedora. Veamos el ejemplo anterior pero ahora aplicado a un Inner Class miembro:



   1: public class ClaseContenedora {



   2:     private int a;



   3:     



   4:     public ClaseContenedora(int n){ this.a=n; }



   5:     



   6:     public void imprimirNumero(){



   7:         InnerClass ic = new InnerClass();



   8:         ic.imprimirClaseContenedora();



   9:     }



  10:     



  11:     public class InnerClass{    



  12:         public void imprimirClaseContenedora(){



  13:             System.out.println(a);



  14:         }    



  15:     }



  16:     



  17:     public static void main(String[] args) {



  18:         ClaseContenedora cc = new ClaseContenedora(5);



  19:         cc.imprimirNumero();



  20:     }



  21: }


En el ejemplo se puede apreciar como la clase interna accede directamente al atributo “a” de la clase contenedora, sin necesidad de una referencia a la clase contenedora ni de invocar al método “getA” como se hacía en el ejemplo anterior. Este tipo de Inner class junto con las clases anónimas son las mas utilizadas.

Inner class Locales

Es muy similar al tipo anterior, solo que la clase interna se define dentro del cuerpo de un método de la clase contenedora, y por tanto solo estará disponible para ese método. Esto se aprecia mejor en el siguiente ejemplo:



   1: public class ClaseContenedora {



   2:     private int a;



   3:     



   4:     public ClaseContenedora(int n){ this.a=n; }



   5:     



   6:     public void imprimirNumero(){



   7:         class InnerClass{    



   8:             public void imprimirClaseContenedora(){



   9:                 System.out.println(a);



  10:             }    



  11:         }



  12:         InnerClass ic = new InnerClass();



  13:         ic.imprimirClaseContenedora();



  14:     }



  15:     



  16:     public void imprimirNumero2(){



  17:         //InnerClass ic = new InnerClass();



  18:     }



  19:     



  20:     public static void main(String[] args) {



  21:         ClaseContenedora cc = new ClaseContenedora(5);



  22:         cc.imprimirNumero();



  23:         cc.imprimirNumero2();



  24:     }



  25: }


En este ejemplo en el cuerpo del método “imprimirNumero” se define la Inner Class y por tanto solo se podrá usar en el cuerpo de ese método, ya que fuera de ahí para el compilador no existe esa clase. Si pruebas a descomentar la línea 17 verás como el compilador te da un error, ya que en el cuerpo del método “imprimirNumero2” no se puede acceder a la clase interna.

Inner class Anónimas


Se suelen emplear para el manejo de eventos, aunque el ejemplo que he puesto aquí sigue la línea de los anteriores y no tiene nada que ver con ese tema. Este tipo de clases se llaman así porque se definen sin nombre y se usan como cuerpo de un método, generalmente para ahorrarnos implementar alguna interfaz, tal y como muestra el ejemplo:





   1: public interface Imprimible {



   2:     



   3:     public void Imprimir();



   4:  



   5: }






   1: public class ClaseContenedora {



   2:     private int a;



   3:     



   4:     public ClaseContenedora(int n){ this.a=n; }



   5:     



   6:     public void imprimirNumero(){



   7:         new Imprimible(){



   8:             public void Imprimir(){



   9:                 System.out.println(a);



  10:             }



  11:         }.Imprimir();



  12:     }



  13:     



  14:     public static void main(String[] args) {



  15:         ClaseContenedora cc = new ClaseContenedora(5);



  16:         cc.imprimirNumero();



  17:     }



  18: }




En el ejemplo (aunque reconozco que es un tanto absurdo) hemos conseguido no implementar explícitamente la interfaz Imprimible en la clase contenedora, y esconder la implementación de la interfaz dentro de una clase anónima en el cuerpo del método “imprimirNumero”, sé que ahora esto te parecerá una estupidez, pero cuando veas el manejo de eventos le verás la utilidad.

En conclusión


Las clases internas son una forma de agrupar la lógica de tu programa de forma mas compacta o de esconder al exterior cierta funcionalidad dentro de una clase.


El caso mas evidente en el que se deben usar clases internas es cuando una clase solo le es útil a otra clase, entonces esa clase debe ser una clase interna.

No hay comentarios:

Publicar un comentario