Classes Internas, Aninhadas e Anônimas
Parte da seção Programação Orientada a Objetos do Journey de Java da Coddy — lição 49 de 87.
O Java permite definir classes dentro de outras classes. Essas classes internas ajudam a organizar o código ao agrupar funcionalidades relacionadas e podem acessar os membros da classe externa diretamente.
Uma classe interna aninhada é definida como um membro de outra classe. Ela tem acesso a todos os campos e métodos da classe externa, incluindo os privados:
class Outer {
private String message = "Hello";
class Inner {
void display() {
System.out.println(message); // Pode acessar o campo privado
}
}
}
// Uso
Outer outer = new Outer();
Outer.Inner inner = outer.new Inner();
inner.display(); // Imprime: HelloUma classe interna local é definida dentro de um método. Ela só pode ser usada dentro desse método e pode acessar variáveis locais que são efetivamente finais:
void process() {
String prefix = "Result: ";
class LocalHelper {
void print(int value) {
System.out.println(prefix + value);
}
}
new LocalHelper().print(42);
}Uma classe interna anônima é uma classe sem nome, criada e instanciada em uma única expressão. É comumente usada para implementar interfaces ou estender classes de forma imediata (on the fly):
Comparator<String> comp = new Comparator<String>() {
@Override
public int compare(String a, String b) {
return a.length() - b.length();
}
};Classes internas são particularmente úteis quando uma classe é relevante apenas para uma outra classe, mantendo seu código organizado e encapsulado.
Desafio
FácilVamos construir um sistema de carrinho de compras que demonstra os três tipos de classes internas. Você criará um ShoppingCart que usa uma classe interna aninhada para representar itens, uma classe interna local para calcular descontos e uma classe interna anônima para formatar o recibo final.
Você organizará seu código em dois arquivos:
ShoppingCart.java: Crie a classe externa que gerencia a experiência de compra. Seu ShoppingCart deve ter um campo privadocustomerName(String).Dentro de ShoppingCart, defina uma classe interna aninhada chamada
Itemcom dois campos privados:name(String) eprice(double). Dê ao Item um construtor para inicializar ambos os campos e um métodogetDetails()que retorna:[name]: $[price]Seu ShoppingCart precisa de um construtor que receba o nome do cliente e um método chamado
checkout()que aceite um Item e uma porcentagem de desconto (double). Dentro decheckout():- Crie uma classe interna local chamada
DiscountCalculatorcom um métodoapplyDiscount(double originalPrice)que retorna o preço após aplicar a porcentagem de desconto. - Use a classe local para calcular o preço final.
- Crie uma classe interna anônima que implemente a interface
Formatter(que você definirá) para formatar o recibo. A classe anônima deve sobrescrever um métodoformat()que retorna uma string de recibo de várias linhas.
O método
checkout()deve retornar a string do recibo formatada com este formato exato:--- Receipt --- Customer: [customerName] Item: [item details from getDetails()] Discount: [discount]% Final Price: $[calculated price]- Crie uma classe interna local chamada
Formatter.java: Defina uma interface simples com um único método:String format(). Esta interface será implementada pela sua classe interna anônima dentro do método checkout.Main.java: Dê vida ao seu carrinho de compras! Você receberá três entradas: nome do cliente (String), nome do item (String) e preço do item (double). A porcentagem de desconto é fixada em10.0.Crie um ShoppingCart com o nome do cliente. Em seguida, crie um Item usando a sintaxe de referência da classe externa:
cart.new Item(name, price). Chamecheckout()com o item e o desconto, e imprima o recibo retornado.
Você receberá três entradas em ordem: nome do cliente, nome do item e preço do item.
Por exemplo, se as entradas forem Alice, Laptop e 1000.0, o preço final após um desconto de 10% seria 900.0.
Observe como cada tipo de classe interna serve a um propósito diferente: a classe aninhada agrupa dados relacionados com a classe externa, a classe local encapsula a lógica necessária apenas dentro de um método, e a classe anônima fornece uma implementação rápida e única de uma interface!
Folha de consulta
O Java permite definir classes dentro de outras classes. Essas inner classes (classes internas) ajudam a organizar o código agrupando funcionalidades relacionadas e podem acessar os membros da classe externa diretamente.
Classe Interna Aninhada
Uma classe interna aninhada é definida como um membro de outra classe. Ela tem acesso a todos os campos e métodos da classe externa, incluindo os privados:
class Outer {
private String message = "Hello";
class Inner {
void display() {
System.out.println(message); // Pode acessar o campo privado
}
}
}
// Uso
Outer outer = new Outer();
Outer.Inner inner = outer.new Inner();
inner.display(); // Imprime: HelloClasse Interna Local
Uma classe interna local é definida dentro de um método. Ela só pode ser usada dentro desse método e pode acessar variáveis locais que sejam efetivamente finais (effectively final):
void process() {
String prefix = "Result: ";
class LocalHelper {
void print(int value) {
System.out.println(prefix + value);
}
}
new LocalHelper().print(42);
}Classe Interna Anônima
Uma classe interna anônima é uma classe sem nome, criada e instanciada em uma única expressão. É comumente usada para implementar interfaces ou estender classes de forma imediata:
Comparator<String> comp = new Comparator<String>() {
@Override
public int compare(String a, String b) {
return a.length() - b.length();
}
};As classes internas são particularmente úteis quando uma classe é relevante apenas para uma outra classe, mantendo seu código organizado e encapsulado.
Experimente você mesmo
import java.util.Scanner;
class Main {
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
// Ler entradas
String customerName = scanner.nextLine();
String itemName = scanner.nextLine();
double itemPrice = scanner.nextDouble();
double discount = 10.0;
// TODO: Criar um ShoppingCart com o nome do cliente
// TODO: Criar um Item usando: cart.new Item(itemName, itemPrice)
// TODO: Chamar checkout() com o item e o desconto, depois imprimir o resultado
}
}
Esta lição inclui um quiz rápido. Comece a lição para respondê-lo e acompanhar seu progresso.
Todas as lições de Programação Orientada a Objetos
1Fundamentos de POO
Arquivos ExternosIntrodução à POOClasses vs ObjetosA Palavra-chave thisMétodosCampos (Atributos)Método ConstrutorSobrecarga de ConstrutorRecapitulação - Calculadora Simples4Herança
Herança Básica (extends)A Palavra-chave superSobrescrita de Método (@Override)Encadeamento de ConstrutoresA Classe ObjectHerança Simples e MultinívelPor que não há Herança Múltipla de ClassesRecapitulação - Hierarquia de Funcionários7Métodos Especiais e a Classe Object
Método toString()equals() e hashCode()Método clone()compareTo() e ComparableInterface ComparatorRecapitulação - Ordenação Personalizada2Modificadores de Acesso e Encapsulamento
Visão Geral dos Níveis de AcessoMétodos Getter e SetterOcultação de InformaçõesA Palavra-chave finalRecapitulação - Gerenciador de Conta Bancária5Polimorfismo
Fundamentos de Sobrecarga de MétodosSobrescrita de Métodos (Tempo de Execução)Upcasting e DowncastingO Operador instanceofClasses e Métodos AbstratosRecapitulação - Calculadora de Formas8Conceitos Avançados de OOP
Composição vs HerançaAgregação vs ComposiçãoClasses Internas, Aninhadas e AnônimasEnums e Métodos de EnumRecords (Java 16+)Classes Seladas (Java 17+)11Padrões de Projeto Parte 1
Introdução aos Padrões de ProjetoPadrão SingletonPadrão FactoryPadrão BuilderPadrão ObserverPadrão Strategy3Propriedades de Classe e Membros Estáticos
Variáveis de Instância vs EstáticasMétodos EstáticosBlocos EstáticosConstantes (static final)Recapitulação - Contador e Utilitário6Interfaces e Classes Abstratas
Introdução às InterfacesImplementando InterfacesImplementação de Múltiplas InterfacesDefault e Static em InterfacesClasses Abstratas vs InterfacesInterfaces FuncionaisRecapitulação - Sistema de Pagamento9Generics
Introdução a GenericsClasses GenéricasMétodos GenéricosParâmetros de Tipos DelimitadosWildcards (?, extends, super)Recapitulação - Container Genérico12Padrões de Projeto Parte 2
Padrão CommandPadrão AdapterPadrão DecoratorPadrão Template MethodPadrão StatePadrão CompositePadrão Iterator