Menu
Coddy logo textTech

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: Hello

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

challenge icon

Desafio

Fácil

Vamos 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 privado customerName (String).

    Dentro de ShoppingCart, defina uma classe interna aninhada chamada Item com dois campos privados: name (String) e price (double). Dê ao Item um construtor para inicializar ambos os campos e um método getDetails() 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 de checkout():

    1. Crie uma classe interna local chamada DiscountCalculator com um método applyDiscount(double originalPrice) que retorna o preço após aplicar a porcentagem de desconto.
    2. Use a classe local para calcular o preço final.
    3. 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étodo format() 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]
  • 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 em 10.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). Chame checkout() 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: Hello

Classe 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
        
    }
}
quiz iconTeste seus conhecimentos

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