Classes aninhadas em Java
1. Introdução
Este tutorial é uma introdução rápida e direta às classes aninhadas na linguagem Java.
Simplificando, Java nos permite definir classes dentro de outras classes. Nested classes enable us to logically group classes that are only used in one place, write more readable and maintainable code and increase encapsulation.
Antes de começar, vamos dar uma olhada nos vários tipos de classes aninhadas disponíveis na linguagem:
-
Classes aninhadas estáticas
-
Classes aninhadas não estáticas
-
Classes locais
-
Classes anônimas
Nas próximas seções, discutiremos cada um deles em detalhes.
2. Classes aninhadas estáticas
Aqui estão alguns pontos a serem lembrados sobre as classes aninhadas estáticas:
-
Assim como os membros estáticos, eles pertencem à classe envolvente e não a uma instância da classe
-
Eles podem ter todos os tipos de modificadores de acesso em suas declarações
-
Eles só têm acesso a membros estáticos na classe envolvente
-
Eles podem definir membros estáticos e não estáticos
Vamos ver como podemos declarar uma classe aninhada estática:
public class Enclosing {
private static int x = 1;
public static class StaticNested {
private void run() {
// method implementation
}
}
@Test
public void test() {
Enclosing.StaticNested nested = new Enclosing.StaticNested();
nested.run();
}
}
3. Classes aninhadas não estáticas
A seguir, apresentamos alguns pontos rápidos a serem lembrados sobre classes aninhadas não estáticas:
-
Eles também são chamados de classes internas
-
Eles podem ter todos os tipos de modificadores de acesso em suas declarações
-
Assim como variáveis e métodos de instância, classes internas são associadas a uma instância da classe envolvente
-
Eles têm acesso a todos os membros da classe envolvente, independentemente de serem estáticos ou não estáticos
-
Eles só podem definir membros não estáticos
Veja como podemos declarar uma classe interna:
public class Outer {
public class Inner {
// ...
}
}
Se declararmos uma classe aninhada com um modificadorstatic, então é um membro estático. Caso contrário, é uma classe interna. Mesmo que sintaticamente a diferença seja apenas uma única palavra-chave (ou seja,static), semanticamente há uma grande diferença entre esses tipos de classes aninhadas. As instâncias da classe interna são vinculadas às da classe envolvente e, portanto, elas têm acesso aos seus membros. Devemos estar cientes desse problema ao escolher se uma classe aninhada deve ser interna.
Para instanciar uma classe interna, devemos primeiro instanciar sua classe envolvente.
Vamos ver como podemos fazer isso:
Outer outer = new Outer();
Outer.Inner inner = outer.new Inner();
Nas próximas subseções, vamos mostrar alguns tipos especiais de classes internas.
3.1. Aulas locais
Classes locais são um tipo especial de classes internas - nas quaisthe class is defined inside a method ou bloco de escopo.
Vejamos alguns pontos a serem lembrados sobre esse tipo de aula:
-
Eles não podem ter modificadores de acesso em suas declarações
-
Eles têm acesso a membros estáticos e não estáticos no contexto anexo
-
Eles só podem definir membros da instância
Aqui está um exemplo rápido:
public class NewEnclosing {
void run() {
class Local {
void run() {
// method implementation
}
}
Local local = new Local();
local.run();
}
@Test
public void test() {
NewEnclosing newEnclosing = new NewEnclosing();
newEnclosing.run();
}
}
3.2. Classes anônimas
Classes anônimas podem ser usadas para definir uma implementação de uma interface ou uma classe abstrata sem precisar criar uma implementação reutilizável.
Vamos listar alguns pontos a serem lembrados sobre as classes anônimas:
-
Eles não podem ter modificadores de acesso em suas declarações
-
Eles têm acesso a membros estáticos e não estáticos no contexto anexo
-
Eles só podem definir membros da instância
-
Eles são o único tipo de classes aninhadas que não podem definir construtores ou estender / implementar outras classes ou interfaces
Para definir uma classe anônima, vamos primeiro definir uma classe abstrata simples:
abstract class SimpleAbstractClass {
abstract void run();
}
Agora vamos ver como podemos definir uma classe anônima:
public class AnonymousInnerTest {
@Test
public void whenRunAnonymousClass_thenCorrect() {
SimpleAbstractClass simpleAbstractClass = new SimpleAbstractClass() {
void run() {
// method implementation
}
};
simpleAbstractClass.run();
}
}
Para mais detalhes, podemos achar útil nosso tutorial emAnonymous Classes in Java.
4. Sombreamento
The declaration of the members of an inner class shadow those of the enclosing class se eles tiverem o mesmo nome.
Nesse caso, a palavra-chavethis se refere às instâncias da classe aninhada e os membros da classe externa podem ser referenciados usando o nome da classe externa.
Vamos ver um exemplo rápido:
public class NewOuter {
int a = 1;
static int b = 2;
public class InnerClass {
int a = 3;
static final int b = 4;
public void run() {
System.out.println("a = " + a);
System.out.println("b = " + b);
System.out.println("NewOuterTest.this.a = " + NewOuter.this.a);
System.out.println("NewOuterTest.b = " + NewOuter.b);
System.out.println("NewOuterTest.this.b = " + NewOuter.this.b);
}
}
@Test
public void test() {
NewOuter outer = new NewOuter();
NewOuter.InnerClass inner = outer.new InnerClass();
inner.run();
}
}
5. Serialização
Para evitarjava.io.NotSerializableException ao tentar serializar uma classe aninhada, devemos:
-
Declare a classe aninhada comostatic
-
Faça com que a classe aninhada e a classe envolvente implementemSerializable
6. Conclusão
Neste artigo, vimos o que são classes aninhadas e seus diferentes tipos. Também examinamos como a visibilidade do campo e os modificadores de acesso diferem entre esses tipos.
Como sempre, a implementação completa deste tutorial pode ser encontradaover on GitHub.