Dominando clases anidadas e internas en Java: Tipos, ejemplos y mejores prácticas
Clases anidadas e internas en Java
Descubre cómo las clases anidadas de Java mejoran la encapsulación, mejoran la legibilidad y permiten patrones poderosos de clases internas.
En Java puedes declarar una clase dentro de otra clase —esto se llama una clase anidada. Por ejemplo:
class ClaseExterna { // ... class ClaseAnidada { // ... } }
Hay dos sabores principales de clases anidadas:
- Clase anidada no‑estática (comúnmente conocida como clase interna)
- Clase anidada estática
Lectura recomendada:
- Modificadores de acceso en Java
- Palabra clave static en Java
Clase anidada no‑estática (Clase interna)
Una clase interna reside dentro de una clase externa y puede acceder libremente a los miembros de la clase externa, incluso los privados. Como está vinculada a una instancia de la clase externa, debes instanciar la clase externa primero.
Ejemplo 1: Clase interna
class CPU { double precio; // clase interna class Procesador{ double nucleos; String fabricante; double getCache(){ return 4.3; } } // clase interna protected protected class RAM{ double memoria; String fabricante; double getVelocidadReloj(){ return 5.5; } } } public class Main { public static void main(String[] args) { CPU cpu = new CPU(); CPU.Procesador procesador = cpu.new Procesador(); CPU.RAM ram = cpu.new RAM(); System.out.println("Caché Procesador = " + procesador.getCache()); System.out.println("Velocidad reloj RAM = " + ram.getVelocidadReloj()); } }
Salida:
Caché Procesador = 4.3 Velocidad reloj RAM = 5.5
Aquí, Procesador y RAM están anidadas dentro de CPU. La clase RAM se declara protected, demostrando que las clases internas pueden usar cualquier modificador de acceso de Java.
En Main, primero instanciamos la clase externa CPU, luego creamos instancias de clase interna vía la instancia externa:
CPU.Procesador procesador = cpu.new Procesador(); CPU.RAM ram = cpu.new RAM();
.) se usa para vincular una instancia de clase interna a su instancia externa.Accediendo a miembros de la clase externa desde una clase interna
Las clases internas pueden referenciar miembros de la clase externa a través de la palabra clave this prefijada con el nombre de la clase externa, ej., Coche.this.tipoCoche. Esta sintaxis es necesaria cuando la clase interna tiene su propio contexto this.
Ejemplo 2: Interactuando con la clase externa
class Coche { String nombreCoche; String tipoCoche; public Coche(String nombre, String tipo) { this.nombreCoche = nombre; this.tipoCoche = tipo; } private String getNombreCoche() { return this.nombreCoche; } // clase interna class Motor { String tipoMotor; void setMotor() { if(Coche.this.tipoCoche.equals("4WD")){ if(Coche.this.getNombreCoche().equals("Crysler")) { this.tipoMotor = "Más pequeño"; } else { this.tipoMotor = "Más grande"; } } else { this.tipoMotor = "Más grande"; } } String getTipoMotor(){ return this.tipoMotor; } } } public class Main { public static void main(String[] args) { Coche coche1 = new Coche("Mazda", "8WD"); Coche.Motor motor = coche1.new Motor(); motor.setMotor(); System.out.println("Tipo Motor para 8WD= " + motor.getTipoMotor()); Coche coche2 = new Coche("Crysler", "4WD"); Coche.Motor c2motor = coche2.new Motor(); c2motor.setMotor(); System.out.println("Tipo Motor para 4WD = " + c2motor.getTipoMotor()); } }
Salida:
Tipo Motor para 8WD= Más grande Tipo Motor para 4WD = Más pequeño
Aunque getNombreCoche() es private, la clase interna puede invocarlo porque reside dentro de la misma clase externa.
Clase anidada estática
Una clase anidada estática no requiere una instancia de la clase externa para ser creada. Consecuentemente, no puede acceder directamente a miembros no‑estáticos de la clase externa. Se crea usando el nombre de la clase externa:
ClaseExterna.ClaseAnidada obj = new ClaseExterna.ClaseAnidada();
Ejemplo 3: Clase anidada estática
class PlacaMadre { static class USB{ int usb2 = 2; int usb3 = 1; int getTotalPuertos(){ return usb2 + usb3; } } } public class Main { public static void main(String[] args) { PlacaMadre.USB usb = new PlacaMadre.USB(); System.out.println("Total Puertos = " + usb.getTotalPuertos()); } }
Salida:
Total Puertos = 3
Ejemplo 4: Intentando acceder a miembros externos en una clase anidada estática
class PlacaMadre { String modelo; public PlacaMadre(String modelo) { this.modelo = modelo; } static class USB{ int usb2 = 2; int usb3 = 1; int getTotalPuertos(){ if(PlacaMadre.this.modelo.equals("MSI")) { return 4; } else { return usb2 + usb3; } } } } public class Main { public static void main(String[] args) { PlacaMadre.USB usb = new PlacaMadre.USB(); System.out.println("Total Puertos = " + usb.getTotalPuertos()); } }
Ejecutar este código produce:
error: non-static variable this cannot be referenced from a static context
Este error ocurre porque la clase anidada estática no puede referenciar PlacaMadre.this sin una instancia externa.
Conclusiones clave
- Las clases internas se tratan como miembros regulares de la clase externa y pueden declararse
private,protectedopublic. - Pueden acceder libremente a todos los miembros de la clase externa, incluso los marcados
private. - Las clases anidadas estáticas no requieren una instancia externa y no pueden acceder a miembros no‑estáticos de la externa directamente.
- Usar clases anidadas promueve la encapsulación y mantiene lógica relacionada agrupada.
- Las clases internas son ideales para callbacks, listeners u objetos auxiliares fuertemente acoplados a la clase externa.
Java
- Entendiendo clases anidadas en C#: Definición, uso y herencia
- Variables y Literales en Java: Guía Completa
- Clases y Objetos en Java: Guía Práctica
- Palabra clave final en Java: Variables, métodos y clases
- Operador instanceof en Java: Guía completa
- Clases y métodos abstractos en Java: Guía completa
- Entendiendo las clases anidadas estáticas en Java: Uso, diferencias y ejemplos
- Dominando las clases internas anónimas en Java: Definición, sintaxis y ejemplos prácticos
- Patrón Singleton en Java explicado: Cómo implementarlo y usarlo de forma segura
- Dominando la Reflexión en Java: Inspeccionando clases, métodos y campos en tiempo de ejecución
- Dominando Java Generics: Construyendo Código Reutilizable y Type-Safe