Classes Internas e Acesso à Classe Externa
Use classes internas não estáticas que mantenham uma referência à instância da classe externa.
Classes Internas e Acesso à Classe Externa é uma aula grátis de Java Academy no CoddyKit. Esta é a aula 2 de 4. Você pode ler a aula completa abaixo gratuitamente — depois pratica ao vivo no navegador com um editor de código integrado e um tutor de IA 24/7. Faz parte do caminho de aprendizado de Java Academy, e seu progresso é sincronizado entre a web e o app CoddyKit. O curso de Java Academy inclui 4 aulas no total.
Classes Inner
Uma classe interna não estática mantém uma referência implícita à instância da classe externa (outer). Ela pode acessar todos os membros da classe externa, inclusive os privados.
Declarando e usando uma classe interna
Crie uma instância de classe interna somente por meio de uma instância da classe externa, usando 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.0Acessando membros da classe externa
Classes internas podem acessar todos os membros da classe externa — até mesmo campos e 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);
}
}
}Ocultação de nomes com this
Quando uma classe interna tem um campo com o mesmo nome que a classe externa, use OuterClass.this.field para fazer referência ao 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: 10Padrão de iterador com classe interna
Um caso de uso clássico: implementar um iterador como uma classe interna que acessa os dados privados da coleção externa.
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 5Listeners de eventos como classes internas
A programação com GUI e orientada a eventos costuma usar classes internas para implementar listeners de eventos com acesso ao estado do componente de interface externo.
// 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: aliceClasse interna em coleções
Algumas implementações de coleções usam classes internas para armazenar cursores ou visões baseadas na coleção externa.
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; }
}Aviso sobre vazamento de memória
Instâncias de classes Inner mantêm uma referência à instância externa (outer). Se a classe interna escapar da classe externa (por exemplo, ao ser armazenada em uma coleção de longa duração), a instância externa não poderá ser coletada pelo coletor de lixo.
// 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
}Comparando classe interna e expressão lambda
As expressões lambda modernas de Java frequentemente substituem classes internas anônimas para interfaces de método único. As expressões lambda são mais concisas, mas não têm seu próprio this (DO NOT).
// 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 itselfQuando usar classes internas
Use classes internas não estáticas quando:
- A classe precisar acessar o estado privado da instância externa
- Cada instância interna estiver logicamente vinculada a uma instância externa específica
- O padrão de iterador for usado para encapsular a lógica de travessia
Prática: Paginator
Um Paginator prático no qual a classe interna mantém o estado da página e acessa a lista de dados 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]Verificação rápida
O que uma classe interna (não estática) mantém que uma classe aninhada estática não mantém?
Recapitulação: classes internas e acesso à classe externa
Principais conclusões:
- As classes internas não estáticas mantêm uma referência implícita à instância externa que as contém
- Crie instâncias internas somente por meio de uma instância externa: outer.new Inner()
- As classes internas podem acessar todos os membros da classe externa, incluindo campos privados
- Use Outer.this.field para desambiguar campos ocultos
- Melhores casos de uso: Iterator, ouvintes de eventos e estruturas de dados internas
- Risco: as instâncias de classes internas impedem que as instâncias externas sejam coletadas pelo coletor de lixo
Perguntas Frequentes
A aula “Classes Internas e Acesso à Classe Externa” é grátis?
Sim — o texto completo de “Classes Internas e Acesso à Classe Externa” é grátis para ler aqui na web. Para praticá-la interativamente (um editor de código integrado e um tutor de IA 24/7) e desbloquear o restante do curso de Java Academy, atualize para CoddyKit PRO. O curso de Java Academy inclui 4 aulas no total.
O que vou aprender em “Classes Internas e Acesso à Classe Externa”?
Use classes internas não estáticas que mantenham uma referência à instância da classe externa. Você pratica Java Academy com código prático que executa diretamente no navegador, e um tutor de IA 24/7 responde suas dúvidas enquanto trabalha na aula.
Preciso ter experiência prévia para começar Java Academy?
Nenhuma experiência prévia é necessária. Java Academy no CoddyKit é estruturado para alunos iniciantes até avançados, então você pode começar aqui ou desde o início e aprender no seu ritmo. Esta é a aula 2 de 4.
Quanto tempo leva a aula “Classes Internas e Acesso à Classe Externa”?
A maioria das aulas CoddyKit leva cerca de 5–10 minutos. Cada uma é compacta e interativa, então você faz progresso constante e retoma exatamente de onde parou entre web e app.
Posso escrever e executar código nesta aula de Java Academy?
Sim. Cada aula de Java Academy inclui um editor de código integrado, então você escreve e executa código real direto no navegador e recebe feedback de IA instantaneamente — nenhuma configuração local necessária.
Todas as aulas deste curso
- Classes Aninhadas Estáticas
- Classes Internas e Acesso à Classe Externa
- Classes Locais e Anônimas
- Escolha da Estratégia de Aninhamento