Clases internas y acceso a la clase externa
Use clases internas no estáticas que mantengan una referencia a la instancia de la clase externa.
Clases internas y acceso a la clase externa es una lección gratuita de Java Academy en CoddyKit. Esta es la lección 2 de 4. Puedes leer la lección completa abajo gratuitamente — luego la practicas en el navegador con un editor de código integrado y un tutor de IA 24/7. Forma parte de la ruta de aprendizaje de Java Academy, y tu progreso se sincroniza en la web y la app de CoddyKit. El curso de Java Academy incluye 4 lecciones en total.
Clases internas
Una clase interna no estática mantiene una referencia implícita a la instancia de la clase externa contenedora. Puede acceder a todos los miembros de la clase externa, incluidos los privados.
Declaración y uso de una clase interna
Cree una instancia de una clase interna únicamente a través de una instancia de la clase externa, mediante outer.new InnerClass().
class BankAccount {
private double balance;
private String owner;
BankAccount(String owner, double initial) {
this.owner = owner; this.balance = initial;
}
class Transaction {
void deposit(double amount) {
balance += amount; // accesses outer field directly
System.out.println(owner + " deposited " + amount);
}
}
}
BankAccount acct = new BankAccount("Alice", 500);
BankAccount.Transaction tx = acct.new Transaction();
tx.deposit(100); // Alice deposited 100.0Acceso a los miembros externos
Las clases internas pueden acceder a todos los miembros de la clase externa, incluso a sus campos y métodos privados.
class Encryption {
private final String secretKey = "s3cr3t"; // private!
class Encryptor {
String encrypt(String data) {
// Inner class accesses private outer field
return data + "|" + secretKey.hashCode();
}
}
class Decryptor {
String decrypt(String encrypted) {
int idx = encrypted.lastIndexOf('|');
return encrypted.substring(0, idx);
}
}
}Ocultación con this
Cuando una clase interna tiene un campo con el mismo nombre que la clase externa, utilice OuterClass.this.field para hacer referencia al campo externo.
class Outer {
int value = 10;
class Inner {
int value = 20; // shadows outer.value
void printBoth() {
System.out.println("Inner value: " + value); // 20
System.out.println("Outer value: " + Outer.this.value); // 10
}
}
}
new Outer().new Inner().printBoth();
// Inner value: 20
// Outer value: 10Patrón Iterator con una clase interna
Un caso de uso clásico es implementar un iterador como clase interna que acceda a los datos privados de la colección contenedora.
class NumberRange implements Iterable<Integer> {
private final int start;
private final int end;
NumberRange(int start, int end) {
this.start = start; this.end = end;
}
@Override
public java.util.Iterator<Integer> iterator() {
return new RangeIterator(); // inner class instance
}
private class RangeIterator implements java.util.Iterator<Integer> {
private int current = start; // accesses outer start
public boolean hasNext() { return current <= end; }
public Integer next() { return current++; }
}
}
for (int n : new NumberRange(1, 5)) System.out.print(n + " ");
// 1 2 3 4 5Detectores de eventos como clases internas
La programación de interfaces gráficas y la programación dirigida por eventos suelen utilizar clases internas para implementar detectores de eventos con acceso al estado del componente de interfaz contenedor.
// Swing-style example (conceptual)
class LoginPanel {
private String username = "";
class LoginButtonListener {
void onButtonClicked() {
if (username.isEmpty()) {
System.out.println("Please enter username"); // accesses outer field
} else {
System.out.println("Logging in as: " + username);
}
}
}
void setUsername(String name) { this.username = name; }
}
LoginPanel panel = new LoginPanel();
panel.setUsername("alice");
panel.new LoginButtonListener().onButtonClicked();
// Logging in as: aliceClases internas en colecciones
Algunas implementaciones de colecciones utilizan clases internas para contener cursores o vistas respaldadas por la colección contenedora.
class SimpleStack<T> {
private Object[] elements;
private int size = 0;
SimpleStack(int capacity) {
elements = new Object[capacity];
}
void push(T item) {
if (size >= elements.length) throw new StackOverflowError();
elements[size++] = item;
}
T pop() {
if (size == 0) throw new java.util.EmptyStackException();
T item = (T) elements[--size];
elements[size] = null;
return item;
}
int size() { return size; }
}Advertencia sobre fugas de memoria
Las instancias de clases internas mantienen una referencia a la instancia externa. Si la clase interna escapa de la clase externa (por ejemplo, al almacenarse en una colección de larga duración), la instancia externa no puede ser recolectada por el recolector de basura.
// Memory leak: anonymous inner class (implicit outer ref) stored statically
class LeakExample {
byte[] data = new byte[1024 * 1024]; // 1MB
// BAD: this Runnable holds a reference to LeakExample.this
Runnable leak = new Runnable() {
public void run() { System.out.println(data.length); }
};
}
// If 'leak' is stored somewhere, LeakExample (and its 1MB) is never GC'd
// FIX: use a static nested class or a lambda that captures only needed values
static Runnable noLeak(byte[] data) {
return () -> System.out.println(data.length); // captures array, not outer
}Comparación: clase interna frente a lambda
Las lambdas de Java moderno suelen reemplazar a las clases internas anónimas para las interfaces de un solo método. Las lambdas son más concisas, pero NO tienen su propio this.
// Old way: anonymous inner class
Runnable r1 = new Runnable() {
@Override
public void run() {
System.out.println("Running (inner class) — this is the Runnable");
}
};
// Modern: lambda
Runnable r2 = () -> System.out.println("Running (lambda)");
r1.run();
r2.run();
// Lambda this = enclosing class (not the Runnable)
// Inner class this = the Runnable instance itselfCuándo utilizar clases internas
Utilice clases internas no estáticas cuando:
- La clase necesite acceder al estado privado de la instancia contenedora
- Cada instancia interna esté vinculada lógicamente a una instancia externa concreta
- Utilice el patrón Iterator para encapsular la lógica de recorrido
Práctica: Paginador
Un paginador práctico en el que la clase interna mantiene el estado de la página y accede a la lista de datos de la clase externa.
import java.util.*;
class Paginator<T> {
private final List<T> items;
private final int pageSize;
Paginator(List<T> items, int pageSize) {
this.items = items; this.pageSize = pageSize;
}
class Page {
private int pageIndex = 0;
List<T> current() {
int start = pageIndex * pageSize;
int end = Math.min(start + pageSize, items.size()); // outer
return start < items.size() ? items.subList(start, end) : List.of();
}
boolean hasNext() { return (pageIndex + 1) * pageSize < items.size(); }
void next() { if (hasNext()) pageIndex++; }
}
}
Paginator<String> p = new Paginator<>(List.of("a","b","c","d","e"), 2);
Paginator.Page page = p.new Page();
System.out.println(page.current()); // [a, b]
page.next();
System.out.println(page.current()); // [c, d]Comprobación rápida
¿Qué mantiene una clase interna (no estática) que no mantiene una clase anidada estática?
Repaso: clases internas y acceso a la clase externa
Ideas clave:
- Las clases internas no estáticas mantienen una referencia implícita a la instancia externa que las contiene
- Cree instancias internas únicamente a través de una instancia externa: outer.new Inner()
- Las clases internas pueden acceder a todos los miembros de la clase externa, incluidos los campos privados
- Utilice Outer.this.field para distinguir campos ocultos
- Casos de uso recomendados: Iterator, listeners de eventos y estructuras de datos internas
- Riesgo: las instancias de clases internas impiden que las instancias externas sean recolectadas por el recolector de basura
Preguntas frecuentes
¿La lección «Clases internas y acceso a la clase externa» es gratis?
Sí — el texto completo de «Clases internas y acceso a la clase externa» es gratis para leer aquí en la web. Para practicarla de forma interactiva (editor de código integrado y tutor de IA 24/7) y desbloquear el resto del curso de Java Academy, actualiza a CoddyKit PRO. El curso de Java Academy incluye 4 lecciones en total.
¿Qué aprenderé en «Clases internas y acceso a la clase externa»?
Use clases internas no estáticas que mantengan una referencia a la instancia de la clase externa. Practicas Java Academy con código real que ejecutas directamente en el navegador, y un tutor de IA 24/7 responde tus preguntas mientras trabajas en la lección.
¿Necesito experiencia previa para empezar Java Academy?
No se requiere experiencia previa. Java Academy en CoddyKit está estructurado para principiantes hasta estudiantes avanzados, así que puedes empezar aquí o desde el inicio y avanzar a tu ritmo. Esta es la lección 2 de 4.
¿Cuánto tiempo toma la lección «Clases internas y acceso a la clase externa»?
La mayoría de las lecciones de CoddyKit toman alrededor de 5–10 minutos. Cada una es compacta e interactiva, así que avanzas constantemente y retomas exactamente por donde dejaste en la web y la app.
¿Puedo escribir y ejecutar código en esta lección de Java Academy?
Sí. Cada lección de Java Academy incluye un editor de código integrado, así que escribes y ejecutas código real directamente en tu navegador y obtienes retroalimentación instantánea de IA — sin configuración local necesaria.
Todas las lecciones de este curso
- Clases anidadas estáticas
- Clases internas y acceso a la clase externa
- Clases locales y anónimas
- Elección de la estrategia de anidamiento adecuada