0Pricing
Java Academy · Lección

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.0

Acceso 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: 10

Patró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 5

Detectores 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: alice

Clases 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 itself

Cuá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

  1. Clases anidadas estáticas
  2. Clases internas y acceso a la clase externa
  3. Clases locales y anónimas
  4. Elección de la estrategia de anidamiento adecuada
← Volver a Java Academy