1 Introducción

Las clases internas nos permiten agrupar clases relacionadas y controlar la visibilidad mutua de esas clases. Un objeto de una clase interna conoce todos los detalles de la instancia de su clase contenedora y puede comunicarse con ella. Esto se logra debido a que la instancia de la clase interna dispone de un enlace al objeto contenedor que la ha creado (de este modo se puede acceder a todos los miembros del objeto contenedor). Solo se puede instanciar una clase interna a través de una referencia al objeto de la clase contenedora.

Ejemplo: Main01.java

class ClaseContenedora {
    class ClaseInterna {
	public String toString() {
		return "ClaseContenedora.ClaseInterna";
	}
	}
	public String toString() {
		return "ClaseContenedora";
	}
}

public class Main01 {
	public static void main(String[] args) {
	//ClaseContenedora.ClaseInterna ci = new ClaseContenedora.ClaseInterna();
	/*
	Main01.java:14: an enclosing instance that contains ClaseContenedora.ClaseInterna is required
	ClaseContenedora.ClaseInterna ci = new ClaseContenedora.ClaseInterna();
	1 error
	*/

	ClaseContenedora cc = new ClaseContenedora();
	ClaseContenedora.ClaseInterna ci = cc.new ClaseInterna();
	System.out.println(cc);
	System.out.println(ci);
    }
}
/*
SALIDA:
ClaseContenedora
ClaseContenedora.ClaseInterna
*/

Desde una instancia de una clase interna se puede referenciar al objeto contenedor de la siguiente manera: NombreClaseContenedra.this

Como se puede ver en el ejemplo Main01.java, no es posible crear un objeto de la clase interna a menos que ya se disponga de un objeto de la clase externa (o contenedora). Esto se debe a que el objeto de la clase interna se conecta de manera transparente al de la clase externa que lo haya creado. (Esto resuelve también las cuestiones relativas a los ámbitos de los nombres en la clase interna)

Las clases normales (no internas) no pueden ser privadas o protegidas (solo pueden tener acceso público o de paquete). Las clases internas pueden tener cualquiera de los cuatro tipos de acceso. Esto permite ocultar las implementaciones de las clases internas y evitar las dependencias de la codificación de tipos. Main02.java: Por defecto los métodos de una interface son públicos, a través de la implementación (de dicha interface) de una clase interna privada (o protegida) los mismos pueden ser no visibles y no estar disponibles.

Ejemplo: Main02.java

interface InterfaceEjemplo {
        public void metodoUno();
        public void metodoDos();
}

class ClaseContenedora {
	private class ClaseInterna implements InterfaceEjemplo {
		public void metodoUno() { System.out.println("ClaseContenedora.ClaseInterna.metodoUno()"); }
		public void metodoDos() { System.out.println("ClaseContenedora.ClaseInterna.metodoDos()"); }
	}
	public void metodo() {
		ClaseInterna ci = new ClaseInterna();
		ci.metodoUno();
		ci.metodoDos();
	}
}

public class Main02 {
	public static void main(String[] args) {
		ClaseContenedora cc = new ClaseContenedora();
		//ClaseContenedora.ClaseInterna ci = cc.new ClaseInterna();
		/*
		Main02.java:21: ClaseContenedora.ClaseInterna has private access in ClaseContenedora
			ClaseContenedora.ClaseInterna ci = cc.new ClaseInterna();
								^
		Main02.java:21: ClaseContenedora.ClaseInterna has private access in ClaseContenedora
			ClaseContenedora.ClaseInterna ci = cc.new ClaseInterna();
								^
		2 errors
		*/
		cc.metodo();
	}
}
/*
SALIDA:
ClaseContenedora.ClaseInterna.metodoUno()
ClaseContenedora.ClaseInterna.metodoDos()
*/

2 Clases internas locales

Las clases internas pueden crearse dentro de un método o incluso dentro de un ámbito arbitrario. Estas clases (definidas dentro de métodos) se denominan clases internas locales. Main03.java: Las clases definidas dentro de bloques if – else, se compilan con todo el resto del código. Sin embargo estas clases no están disponibles fuera del ámbito en el que se definen, por lo demás se asemejan a clases normales.

Ejemplo: Main03.java

interface InterfaceEjemplo {
	public void metodoUno();
	public void metodoDos();
}

class ClaseContenedora {
	public InterfaceEjemplo metodo(boolean flag) {
		InterfaceEjemplo i = null;
		if (flag) {
			class ClaseInternaLocalUno implements InterfaceEjemplo {
				public void metodoUno() {
					System.out.println("ClaseContenedora.ClaseInternaLocalUno.metodoUno()"); }
				public void metodoDos() {
					System.out.println("ClaseContenedora.ClaseInternaLocalUno.metodoDos()"); }
			}
			i = new ClaseInternaLocalUno();
		}
		else {
			class ClaseInternaLocalDos implements InterfaceEjemplo {
				public void metodoUno() {
					System.out.println("ClaseContenedora.ClaseInternaLocalDos.metodoUno()"); }
				public void metodoDos() {
					System.out.println("ClaseContenedora.ClaseInternaLocalDos.metodoDos()"); }
			}
			i = new ClaseInternaLocalDos();
		}
		return i;
	}
}

public class Main03 {
	public static void main(String[] args) {
		ClaseContenedora cc = new ClaseContenedora();

		InterfaceEjemplo i = cc.metodo(true);
		i.metodoUno();
		i.metodoDos();

		i = cc.metodo(false);
		i.metodoUno();
		i.metodoDos();
	}
}
/*
SALIDA:
ClaseContenedora.ClaseInternaLocalUno.metodoUno()
ClaseContenedora.ClaseInternaLocalUno.metodoDos()
ClaseContenedora.ClaseInternaLocalDos.metodoUno()
ClaseContenedora.ClaseInternaLocalDos.metodoDos()
*/

3 Clases internas anónimas

Una clase interna anónima, es una clase que no tiene nombre. Generalmente se utilizan para implementar una interface (o extender de otra clase) y devolverse a través de un método. Es decir, se crean solo con el objetivo de darle una implementación a una interface. Este tipo de clases están en cierta forma limitadas si se comparan con el mecanismo normal de herencia, porque pueden extender una clase o implementar solo una interface, pero no pueden hacer ambas cosas al mismo tiempo.

Luego de definir una clase interna anónima se cierra la expresión con un punto y coma. Si se está definiendo una clase interna anónima y se quiere usar un objeto que este definido fuera de la clase interna anónima, el compilador requiere que la referencia al argumento sea FINAL. Si esto no se hace el compilador generará un mensaje de error.

Ejemplo: Main04.java

interface InterfaceEjemplo {
	public void metodoUno();
	public void metodoDos();
}

class Superclase {
	protected Integer valor1;
	protected Integer valor2;
	public Superclase(Integer v1) {
		valor1 = v1;
	}
}

class ClaseContenedora {
	public InterfaceEjemplo metodo() {
		return new InterfaceEjemplo() { // Clase anonima que implementa la interface InterfaceEjemplo.
			public void metodoUno() { System.out.println("ClaseContenedora.metodo().InterfaceEjemplo.metodoUno()"); }
			public void metodoDos() { System.out.println("ClaseContenedora.metodo().InterfaceEjemplo.metodoDos()"); }
		};
	}

	//public Superclase metodo(Integer i1, Integer i2) {
	/*
	Main04.java:25: local variable i2 is accessed from within inner class; needs tobe declared final
		valor2 = i2;
			 ^
	1 error
	*/
	public Superclase metodo(Integer i1, final Integer i2) {
		return new Superclase(i1) { // Clase anonima que extiende de la clase Superclase e invoca un constructor no predeterminado.
			{
				valor2 = i2;
			} // Bloque de inicialización de la clase anonima (simula un constructor).
			public String toString() {
				return "Superclase.ClaseAnonima - valor1: " + valor1 + " - valor2: " + valor2;
			}
		};
	}
}

public class Main04 {
	public static void main(String[] args) {
		ClaseContenedora cc = new ClaseContenedora();

		InterfaceEjemplo i = cc.metodo();
		i.metodoUno();
		i.metodoDos();

		Superclase sp = cc.metodo(new Integer(3), new Integer(8));
		System.out.println(sp);
	}
}
/*
SALIDA:
ClaseContenedora.metodo().InterfaceEjemplo.metodoUno()
ClaseContenedora.metodo().InterfaceEjemplo.metodoDos()
Superclase.ClaseAnonima - valor1: 3 - valor2: 8
*/

4 Clases anidadas (Clases internas estáticas)

Una clase anidada es una clase interna estática. Este tipo de clases internas son útiles cuando no es necesario disponer de una conexión entre el objeto de la clase interna y el objeto de la clase externa. No se puede acceder a un objeto no estático de la clase externa desde un objeto de una clase anidada (debido a que no existe una conexión con la clase externa).
A diferencia de las clases internas normales que no pueden tener miembros estáticos o clases anidadas, las clases anidadas si pueden contener estos elementos.

Ejemplo: Main05.java

class ClaseContenedora {
	public static class ClaseAnidada {
		public ClaseAnidada() {
			System.out.println("Constructor ClaseContenedora.ClaseAnidada");
		}
		public void metodo() {
			System.out.println("ClaseContenedora.ClaseAnidada.metodo()");
		}
		public static void metodoEstatico() {
			System.out.println("ClaseContenedora.ClaseAnidada.metodoEstatico()");
		}
	}

	public class ClaseInterna {
		public ClaseInterna() {
			System.out.println("Constructor ClaseContenedora.ClaseInterna");
		}
		public void metodo() {
			System.out.println("ClaseContenedora.ClaseInterna.metodo()");
		}
		/*
		public static void metodoEstatico() {
			System.out.println("ClaseContenedora.ClaseInterna.metodoEstatico()");
		}
		*/
		/*
		Main05.java:21: inner classes cannot have static declarations
			public static void metodoEstatico() {
							^
		*/
	}
}

public class Main05 {
	public static void main(String[] args) {
		ClaseContenedora.ClaseAnidada.metodoEstatico();
		ClaseContenedora.ClaseAnidada ca = new ClaseContenedora.ClaseAnidada();
		ca.metodo();

		System.out.println();

		//ClaseContenedora.ClaseInterna.metodoEstatico();
		/*
		ClaseContenedora.ClaseInterna ci = new ClaseContenedora.ClaseInterna();
		ci.metodo();
		*/
		/*
		Main05.java:34: an enclosing instance that contains ClaseContenedora.ClaseInterna is required
			ClaseContenedora.ClaseInterna ci = new ClaseContenedora.ClaseInterna();
										  ^
		*/
		ClaseContenedora cc = new ClaseContenedora();
		ClaseContenedora.ClaseInterna ci = cc.new ClaseInterna();
		ci.metodo();
	}
}
/*
SALIDA:
ClaseContenedora.ClaseAnidada.metodoEstatico()
Constructor ClaseContenedora.ClaseAnidada
ClaseContenedora.ClaseAnidada.metodo()

Constructor ClaseContenedora.ClaseInterna
ClaseContenedora.ClaseInterna.metodo()
*/
Uso de cookies

Este sitio web utiliza cookies para que usted tenga la mejor experiencia de usuario. Si continúa navegando está dando su consentimiento para la aceptación de las mencionadas cookies y la aceptación de nuestra política de cookies, pinche el enlace para mayor información.

ACEPTAR
Aviso de cookies