Classes Internas e Aninhadas em Java (Inner e Nested Classes)
Em Java, é possível definir uma classe dentro do corpo de outra. Essa técnica é chamada de aninhamento de classes (nesting classes). É uma forma poderosa de agrupar logicamente classes que estão fortemente relacionadas, melhorando o encapsulamento e a organização do código.
Existem dois tipos principais de classes aninhadas, e a diferença entre eles é crucial:
- Classes Aninhadas Estáticas (Static Nested Classes): Classes aninhadas declaradas com
static
. - Classes Internas (Inner Classes): Classes aninhadas não estáticas.
1. Classes Aninhadas Estáticas: A Classe "Agrupada"
Uma classe aninhada estática é a forma mais simples de aninhamento. Pense nela como uma classe normal que, por questões de organização, foi colocada dentro de outra classe que atua como um "namespace" ou um agrupador. Sua principal característica é que ela não está ligada a uma instância da classe externa.
Um excelente exemplo é a implementação do padrão de projeto Builder. A classe Builder
está logicamente relacionada à HttpUrl
(ela serve para construir uma HttpUrl
), mas sua função é independente e não precisa de uma instância de HttpUrl
para existir.
// Classe Externa
class HttpUrl {
private final String protocol;
private final String host;
private final int port;
// O construtor é privado, forçando o uso do Builder.
private HttpUrl(Builder builder) {
this.protocol = builder.protocol;
this.host = builder.host;
this.port = builder.port;
}
public void displayUrl() {
System.out.println(protocol + "://" + host + ":" + port);
}
// --- Classe Aninhada Estática ---
public static class Builder {
private String protocol = "https";
private String host = "localhost";
private int port = 443;
public Builder protocol(String protocol) {
this.protocol = protocol;
return this;
}
public Builder host(String host) {
this.host = host;
return this;
}
public Builder port(int port) {
this.port = port;
return this;
}
public HttpUrl build() {
return new HttpUrl(this);
}
}
}
Para usar a classe aninhada estática, você pode instanciá-la diretamente usando o nome da classe externa como prefixo.
public class Program {
public static void main(String[] args) {
HttpUrl myUrl = new HttpUrl.Builder()
.protocol("http")
.host("example.com")
.port(80)
.build();
myUrl.displayUrl(); // Saída: http://example.com:80
}
}
2. Classes Internas (Não Estáticas): A Classe "Dependente"
Uma classe interna, por não ser estática, tem uma característica fundamentalmente diferente: cada instância de uma classe interna está diretamente ligada a uma instância da classe externa.
Pense em um carro (Car
) e seu motor (Engine
). O Engine
(Classe Interna
) é uma parte fundamental do Car
(Classe Externa
) e não faz sentido existir sem ele.
// Classe Externa
class Car {
private int maxSpeed;
public Car(int maxSpeed) {
this.maxSpeed = maxSpeed;
}
// --- Classe Interna ---
// O Engine pertence a uma instância específica de Car
public class Engine {
// A classe interna pode acessar os membros da instância da classe externa,
// inclusive os privados como 'maxSpeed'.
public void start() {
System.out.println("Engine started. Max speed is " + maxSpeed + " km/h.");
}
}
}
Para criar uma instância de uma classe interna, você primeiro precisa de uma instância da classe externa.
public class Program {
public static void main(String[] args) {
// 1. Crie uma instância da classe externa.
Car myCar = new Car(200);
// 2. A partir da instância externa, crie a instância da classe interna.
Car.Engine engine = myCar.new Engine();
engine.start(); // Saída: Engine started. Max speed is 200 km/h.
}
}
Resumo: Quando Usar Cada Uma?
Pergunta | Se a resposta for SIM | Se a resposta for NÃO |
---|---|---|
A classe aninhada precisa acessar membros de instância (não estáticos) da classe externa? | Use uma Classe Interna | Use uma Classe Aninhada Estática |
A classe aninhada representa um conceito independente que está apenas "agrupado" por conveniência? | Use uma Classe Aninhada Estática | Use uma Classe Interna |
Regra geral: Comece sempre declarando sua classe aninhada como static
. Remova o static
apenas se e quando você tiver uma razão clara para que ela precise acessar os membros de instância da classe externa. Isso evita a criação de laços desnecessários entre objetos.
📝 Exercícios
Tarefa
Descrição: Um desenvolvedor precisa criar uma instância da classe Interna
e uma da classe Estatica
. Supondo que Externa externaObj = new Externa();
, qual das seguintes opções de código é a forma correta de instanciar ambas as classes aninhadas?
public class Externa {
// Classe aninhada estática
public static class Estatica { }
// Classe interna (não estática)
public class Interna { }
}
Alternativas:
Externa.Estatica obj1 = new Externa.Estatica(); Externa.Interna obj2 = new Externa.Interna();
Externa.Estatica obj1 = externaObj.new Estatica(); Externa.Interna obj2 = externaObj.new Interna();
Externa.Estatica obj1 = new Externa.Estatica(); Externa.Interna obj2 = externaObj.new Interna();
Externa.Estatica obj1 = new Estatica(); Externa.Interna obj2 = new Interna();
Resposta
Externa.Estatica obj1 = new Externa.Estatica();
Externa.Interna obj2 = externaObj.new Interna();
Estatica
): Por ser static
, ela pertence à classe Externa
como um todo, e não a um objeto específico. Portanto, ela pode ser instanciada diretamente usando o nome da classe externa como um prefixo: new Externa.Estatica()
. Ela não precisa de uma instância externaObj
para existir.Interna
): Por não ser static
, ela está ligada a uma instância específica da classe Externa
. Ela não pode existir sozinha. Portanto, para criá-la, você precisa de um objeto da classe externa (externaObj
) e usar a sintaxe especial externaObj.new Interna()
. Isso estabelece a ligação entre a nova instância de Interna
e a instância externaObj
de Externa
.public class Main {
public static void main(String[] args) {
// Instanciação da classe aninhada estática.
Externa.Estatica objEstatico = new Externa.Estatica();
// Instanciação da classe interna.
Externa objExterno = new Externa();
Externa.Interna objInterno = objExterno.new Interna();
System.out.println("Objetos criados com sucesso.");
}
}
// Classe externa que contém as classes aninhadas.
class Externa {
// Classe aninhada estática
public static class Estatica {
// Construtor padrão implícito
}
// Classe interna (não estática)
public class Interna {
// Construtor padrão implícito
}
}