(Inner)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:

public class ClaseContenedora {
	private int a;
	public ClaseContenedora(int n){ this.a=n; }
	public int getA(){ return this.a; }

	public static class ClaseInterna{
		public void imprimirClaseContenedora(ClaseContenedora cc){
			//System.out.println(a);
			System.out.println(cc.getA());
		}
	}

	public static void main(String[] args) {
		ClaseContenedora cc = new ClaseContenedora(5);
		ClaseContenedora.ClaseInterna ci = new ClaseContenedora.ClaseInterna();
		ci.imprimirClaseContenedora(cc);
	}
}

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 8 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 a 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.

Clase InternaEn 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:

public class ClaseContenedora {
	private int a;
	public ClaseContenedora(int n){ this.a=n; }
	public void imprimirNumero(){
		InnerClass ic = new InnerClass();
		ic.imprimirClaseContenedora();
	}

	public class InnerClass{
		public void imprimirClaseContenedora(){
			System.out.println(a);
		}
	}

	public static void main(String[] args) {
		ClaseContenedora cc = new ClaseContenedora(5);
		cc.imprimirNumero();
	}
}

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:

public class ClaseContenedora {
	private int a;
	public ClaseContenedora(int n){ this.a=n; }

	public void imprimirNumero(){
		class InnerClass{
			public void imprimirClaseContenedora(){
				System.out.println(a);
			}
		}

		InnerClass ic = new InnerClass();
		ic.imprimirClaseContenedora();
	}

	public void imprimirNumero2(){
		//InnerClass ic = new InnerClass();
	}

	public static void main(String[] args) {
		ClaseContenedora cc = new ClaseContenedora(5);
		cc.imprimirNumero();
		cc.imprimirNumero2();
	}
}

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 se prueba a descomentar la línea 17 se verá como el compilador 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:

public interface Imprimible {
	public void imprimir(int n);
}

public class ClaseContenedora {
	private int a;
	public ClaseContenedora(int n){ this.a=n; }

	public void imprimirNumero(Imprimible imp){
		imp.imprimir(a);
	}
}

public class ClasePrincipal {
	public static void main(String[] args) {
		ClaseContenedora cc = new ClaseContenedora(5);
		cc.imprimirNumero(new Imprimible(){
			public void Imprimir(int n){
				System.out.println(n);
			}
		}.Imprimir(););
	}
}

En el ejemplo (aunque es un tanto absurdo) se ha conseguido no implementar explícitamente la interfaz Imprimible en la clase ClaseContenedora ni en la clase ClasePrincipal, y esconder la implementación de la interfaz dentro de una clase anónima en el momento de la llamada al método imprimirNumero.
Esto pude parecer muy barroco, pero es muy útil para el manejo de eventos en interfaces gráficos. Ya que de otra manera se tendrían diferentes implementaciones de la misma interfaz (ActionListener), una por cada componente gráfico (JButton) que se tenga en el programa.

En conclusión

Las clases internas son una forma de agrupar la lógica de 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.

Acerca de franciscoguemes

Ingeniero en Informática
Esta entrada fue publicada en Java. Guarda el enlace permanente.

Responder

Introduce tus datos o haz clic en un icono para iniciar sesión:

Logo de WordPress.com

Estás comentando usando tu cuenta de WordPress.com. Cerrar sesión / Cambiar )

Imagen de Twitter

Estás comentando usando tu cuenta de Twitter. Cerrar sesión / Cambiar )

Foto de Facebook

Estás comentando usando tu cuenta de Facebook. Cerrar sesión / Cambiar )

Google+ photo

Estás comentando usando tu cuenta de Google+. Cerrar sesión / Cambiar )

Conectando a %s