Classes anônimas em Java

Classes anônimas em Java

1. Introdução

Neste tutorial, vamos considerar classes anônimas em Java.

Descreveremos como podemos declarar e criar instâncias deles. Também discutiremos brevemente suas propriedades e limitações.

2. Declaração de classe anônima

Anonymous classes are inner classes with no name. Como eles não têm nome, não podemos usá-los para criar instâncias de classes anônimas. Como resultado, temos que declarar e instanciar classes anônimas em uma única expressão no ponto de uso.

Podemos estender uma classe existente ou implementar uma interface.

2.1. Estender uma classe

Quando instanciamos uma classe anônima de uma existente, usamos a seguinte sintaxe:

image

Entre parênteses, especificamos os parâmetros exigidos pelo construtor da classe que estamos estendendo:

new Book("Design Patterns") {
    @Override
    public String description() {
        return "Famous GoF book.";
    }
}

Naturalmente, se o construtor da classe pai não aceita argumentos, devemos deixar os parênteses vazios.

2.2. Implementar uma interface

Também podemos instanciar uma classe anônima a partir de uma interface:

image

Obviamente, as interfaces de Java não têm construtores, então os parênteses sempre permanecem vazios. Esta é a única maneira que devemos fazer para implementar os métodos da interface:

new Runnable() {
    @Override
    public void run() {
        ...
    }
}

Depois de instanciar uma classe anônima, podemos atribuir essa instância a uma variável para poder referenciá-la em algum lugar mais tarde.

Podemos fazer isso usando a sintaxe padrão para expressões Java:

Runnable action = new Runnable() {
    @Override
    public void run() {
        ...
    }
};

Como já mencionamos,an anonymous class declaration is an expression, hence it must be a part of a statement. Isso explica por que colocamos um ponto-e-vírgula no final da declaração.

Obviamente, podemos evitar atribuir a instância a uma variável se criarmos essa instância em linha:

List actions = new ArrayList();
actions.add(new Runnable() {
    @Override
    public void run() {
        ...
    }
});

Devemos usar essa sintaxe com muito cuidado, pois ela pode facilmente prejudicar a legibilidade do código, especialmente quando a implementação do métodorun() ocupa muito espaço.

3. Propriedades de classe anônima

Existem certas particularidades no uso de classes anônimas em relação às classes usuais de nível superior. Aqui abordamos brevemente as questões mais práticas. Para obter as informações mais precisas e atualizadas, podemos sempre olharthe Java Language Specification.

3.1. Construtor

A sintaxe das classes anônimas não nos permite fazê-las implementar várias interfaces. Durante a construção,there might exist exactly one instance of an anonymous class. Portanto, eles nunca podem ser abstratos. Como eles não têm nome, não podemos estendê-los. Pelo mesmo motivo, classes anônimas não podem ter declarado explicitamente construtores.

Na verdade, a ausência de um construtor não representa nenhum problema para nós pelos seguintes motivos:

  1. criamos instâncias de classe anônimas no mesmo momento em que as declaramos

  2. a partir de instâncias de classe anônimas, podemos acessar variáveis ​​locais e membros da classe envolvente

3.2. Membros estáticos

Classes anônimas não podem ter nenhum membro estático, exceto aqueles que são constantes.

Por exemplo, isso não compilará:

new Runnable() {
    static final int x = 0;
    static int y = 0; // compilation error!

    @Override
    public void run() {...}
};

Em vez disso, obteremos o seguinte erro:

The field y cannot be declared static in a non-static inner type, unless initialized with a constant expression

3.3. Escopo das Variáveis

Classes anônimas capturam variáveis ​​locais que estão no escopo do bloco em que declaramos a classe:

int count = 1;
Runnable action = new Runnable() {
    @Override
    public void run() {
        System.out.println("Runnable with captured variables: " + count);
    }
};

Como podemos ver, as variáveis ​​locaiscounteaction são definidas no mesmo bloco. Por esse motivo, podemos acessarcount de dentro da declaração da classe.

Note that in order to be able to use local variables, they must be effectively final. Desde o JDK 8, não é mais necessário declarar variáveis ​​com a palavra-chavefinal. No entanto, essas variáveis ​​devem serfinal. Caso contrário, obteremos um erro de compilação:

[ERROR] local variables referenced from an inner class must be final or effectively final

Para que o compilador decida que uma variável é, de fato, imutável, no código, deve haver apenas um local no qual atribuímos um valor a ela. Podemos encontrar mais informações sobre variáveis ​​efetivamente finais em nosso artigo “https://www.example.com/java-lambda-effectively-final-local-variables[Por que as variáveis ​​locais usadas em Lambdas devem ser finais ou efetivamente finais? ] ”

Vamos apenas mencionar que, como toda classe interna,an anonymous class can access all members of its enclosing class.

4. Casos de uso de classe anônima

Pode haver uma grande variedade de aplicativos de classes anônimas. Vamos explorar alguns casos de uso possíveis.

4.1. Hierarquia de classes e encapsulamento

Devemos usar classes internas em casos de uso gerais e anônimas em casos muito específicos, a fim de alcançar uma hierarquia mais limpa de classes em nosso aplicativo. Ao usar classes internas, podemos alcançar um encapsulamento mais fino dos dados da classe envolvente. Se definirmos a funcionalidade da classe interna em uma classe de nível superior, a classe envolvente deve ter visibilidadepublic oupackage de alguns de seus membros. Naturalmente, há situações em que isso não é muito apreciado ou aceito.

4.2. Estrutura do projeto mais limpa

Normalmente usamos classes anônimas quando temos que modificaron the fly a implementação de métodos de algumas classes. Nesse caso, podemos evitar adicionar novos arquivos*.java ao projeto para definir as classes de nível superior. Isso é especialmente verdade se essa classe de nível superior for usada apenas uma vez.

4.3. Ouvintes de evento da interface do usuário

Em aplicativos com uma interface gráfica, o caso de uso mais comum de classes anônimas é criar vários ouvintes de eventos. Por exemplo, no seguinte snippet:

button.addActionListener(new ActionListener() {
    public void actionPerformed(ActionEvent e) {
        ...
    }
}

criamos uma instância de uma classe anônima que implementa a interfaceActionListener. Seu métodoactionPerformed é acionado quando um usuário clica no botão.

Since Java 8, lambda expressions parece ser a forma mais preferida.

5. Imagem geral

As classes anônimas que consideramos acima são apenas um caso particular denested classes. Geralmente, a nested class isa class that is declared inside another class or interface:

image

Olhando para o diagrama, vemos que as classes anônimas junto comlocalenonstatic member ones formam os chamadosinner classes. Junto com as classesstatic member, eles formam as classes aninhadas.

6. Conclusão

Neste artigo, consideramos vários aspectos das classes anônimas Java. Descrevemos também uma hierarquia geral de classes aninhadas.

Como sempre, o código completo está disponível emin our GitHub repository.