Clases internas, anidadas y anónimas
Parte de la sección Programación Orientada a Objetos del Journey de Java de Coddy — lección 49 de 87.
Java permite definir clases dentro de otras clases. Estas clases internas ayudan a organizar el código agrupando funcionalidades relacionadas y pueden acceder directamente a los miembros de la clase externa.
Una clase interna anidada se define como un miembro de otra clase. Tiene acceso a todos los campos y métodos de la clase externa, incluidos los privados:
class Outer {
private String message = "Hello";
class Inner {
void display() {
System.out.println(message); // Puede acceder al campo privado
}
}
}
// Uso
Outer outer = new Outer();
Outer.Inner inner = outer.new Inner();
inner.display(); // Imprime: HelloUna clase interna local se define dentro de un método. Solo se puede utilizar dentro de ese método y puede acceder a variables locales que son efectivamente finales:
void process() {
String prefix = "Result: ";
class LocalHelper {
void print(int value) {
System.out.println(prefix + value);
}
}
new LocalHelper().print(42);
}Una clase interna anónima es una clase sin nombre, creada e instanciada en una sola expresión. Se utiliza comúnmente para implementar interfaces o extender clases sobre la marcha:
Comparator<String> comp = new Comparator<String>() {
@Override
public int compare(String a, String b) {
return a.length() - b.length();
}
};Las clases internas son particularmente útiles cuando una clase solo es relevante para otra clase, manteniendo su código organizado y encapsulado.
Desafío
Fácil¡Vamos a construir un sistema de carrito de compras que muestre los tres tipos de clases internas! Crearás un ShoppingCart que utiliza una clase interna anidada para representar artículos, una clase interna local para calcular descuentos y una clase interna anónima para formatear el recibo final.
Organizarás tu código en dos archivos:
ShoppingCart.java: Crea la clase externa que gestiona una experiencia de compra. Tu ShoppingCart debe tener un campo privadocustomerName(String).Dentro de ShoppingCart, define una clase interna anidada llamada
Itemcon dos campos privados:name(String) yprice(double). Dale a Item un constructor para inicializar ambos campos y un métodogetDetails()que devuelva:[name]: $[price]Tu ShoppingCart necesita un constructor que reciba el nombre del cliente, y un método llamado
checkout()que acepte un Item y un porcentaje de descuento (double). Dentro decheckout():- Crea una clase interna local llamada
DiscountCalculatorcon un métodoapplyDiscount(double originalPrice)que devuelva el precio después de aplicar el porcentaje de descuento. - Usa la clase local para calcular el precio final.
- Crea una clase interna anónima que implemente la interfaz
Formatter(la cual definirás) para formatear el recibo. La clase anónima debe sobrescribir un métodoformat()que devuelva una cadena de recibo multilínea.
El método
checkout()debe devolver la cadena del recibo formateada con este formato exacto:--- Receipt --- Customer: [customerName] Item: [item details from getDetails()] Discount: [discount]% Final Price: $[calculated price]- Crea una clase interna local llamada
Formatter.java: Define una interfaz simple con un único método:String format(). Esta interfaz será implementada por tu clase interna anónima dentro del método checkout.Main.java: ¡Dale vida a tu carrito de compras! Recibirás tres entradas: nombre del cliente (String), nombre del artículo (String) y precio del artículo (double). El porcentaje de descuento es fijo en10.0.Crea un ShoppingCart con el nombre del cliente. Luego crea un Item usando la sintaxis de referencia de la clase externa:
cart.new Item(name, price). Llama acheckout()con el artículo y el descuento, e imprime el recibo devuelto.
Recibirás tres entradas en orden: nombre del cliente, nombre del artículo y precio del artículo.
Por ejemplo, si las entradas son Alice, Laptop, y 1000.0, el precio final después de un descuento del 10% sería 900.0.
¡Observa cómo cada tipo de clase interna sirve para un propósito diferente: la clase anidada agrupa datos relacionados con la clase externa, la clase local encapsula la lógica necesaria solo dentro de un método, y la clase anónima proporciona una implementación rápida y de un solo uso de una interfaz!
Hoja de referencia
Java permite definir clases dentro de otras clases. Estas clases internas ayudan a organizar el código agrupando la funcionalidad relacionada y pueden acceder directamente a los miembros de la clase externa.
Clase Interna Anidada
Una clase interna anidada se define como un miembro de otra clase. Tiene acceso a todos los campos y métodos de la clase externa, incluidos los privados:
class Outer {
private String message = "Hello";
class Inner {
void display() {
System.out.println(message); // Can access private field
}
}
}
// Usage
Outer outer = new Outer();
Outer.Inner inner = outer.new Inner();
inner.display(); // Prints: HelloClase Interna Local
Una clase interna local se define dentro de un método. Solo se puede utilizar dentro de ese método y puede acceder a variables locales que sean efectivamente finales:
void process() {
String prefix = "Result: ";
class LocalHelper {
void print(int value) {
System.out.println(prefix + value);
}
}
new LocalHelper().print(42);
}Clase Interna Anónima
Una clase interna anónima es una clase sin nombre, creada e instanciada en una sola expresión. Se utiliza comúnmente para implementar interfaces o extender clases sobre la marcha:
Comparator<String> comp = new Comparator<String>() {
@Override
public int compare(String a, String b) {
return a.length() - b.length();
}
};Las clases internas son particularmente útiles cuando una clase solo es relevante para otra clase, manteniendo su código organizado y encapsulado.
Pruébalo tú mismo
import java.util.Scanner;
class Main {
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
// Leer las entradas
String customerName = scanner.nextLine();
String itemName = scanner.nextLine();
double itemPrice = scanner.nextDouble();
double discount = 10.0;
// TODO: Crear un ShoppingCart con el nombre del cliente
// TODO: Crear un Item usando: cart.new Item(itemName, itemPrice)
// TODO: Llamar a checkout() con el item y discount, luego imprimir el resultado
}
}
Esta lección incluye un breve cuestionario. Empieza la lección para responderlo y registrar tu progreso.
Todas las lecciones de Programación Orientada a Objetos
1Fundamentos de OOP
Archivos externosIntroducción a la OOPClases vs. ObjetosLa palabra clave thisMétodosCampos (Atributos)Método constructorSobrecarga de constructoresResumen - Calculadora simple4Herencia
Herencia básica (extends)La palabra clave superSobrescritura de métodos (@Override)Encadenamiento de constructoresLa clase ObjectHerencia simple y multinivelPor qué no hay herencia múltiple de clasesResumen - Jerarquía de empleados7Métodos especiales y la clase Object
Método toString()equals() y hashCode()Método clone()compareTo() y ComparableInterfaz ComparatorResumen - Ordenamiento personalizado2Modificadores de acceso y encapsulamiento
Visión general de los niveles de accesoMétodos Getter y SetterOcultamiento de informaciónLa palabra clave finalResumen - Gestor de cuentas bancarias5Polimorfismo
Fundamentos de sobrecarga de métodosSobrescritura de métodos (tiempo de ejecución)Upcasting y DowncastingEl operador instanceofClases y métodos abstractosResumen - Calculadora de figuras8Conceptos avanzados de POO
Composición vs. HerenciaAgregación vs. ComposiciónClases internas, anidadas y anónimasEnums y métodos de EnumRecords (Java 16+)Clases selladas (Java 17+)11Patrones de diseño - Parte 1
Introducción a los patrones de diseñoPatrón SingletonPatrón FactoryPatrón BuilderPatrón ObserverPatrón Strategy3Propiedades de clase y miembros estáticos
Variables de instancia vs. estáticasMétodos estáticosBloques estáticosConstantes (static final)Repaso - Contador y utilidad6Interfaces y Clases Abstractas
Introducción a las InterfacesImplementación de InterfacesImplementación de Múltiples InterfacesMétodos Default y Static en InterfacesClases Abstractas vs InterfacesInterfaces FuncionalesResumen - Sistema de Pagos9Genéricos
Introducción a los genéricosClases genéricasMétodos genéricosParámetros de tipo delimitadosComodines (?, extends, super)Resumen - Contenedor genérico12Patrones de diseño Parte 2
Patrón CommandPatrón AdapterPatrón DecoratorPatrón Template MethodPatrón StatePatrón CompositePatrón Iterator