Tem um tutorial da Oracle sobre o assunto. Inclusive, lá tem alguns motivos para usar. São explicações meio genéricas, é verdade, e reconheço que pra quem não conhece fica meio vago, mas enfim, segue uma tradução bem livre:
- é uma maneira de de agrupar logicamente classes que são usadas apenas em um lugar: se a classe é útil apenas para uma outra classe, então faz sentido colocá-al dentro desta outra classe e mantê-las juntas. Desta forma, seu pacote fica mais simplificado.
- aumenta o encapsulamento: suponha duas classes A e B, sendo que B precisa acessar membros privados de A. Escondendo a classe B dentro de A, os membros de A podem ser privados e B pode acessá-los. Além disso, B pode ficar escondida do mundo externo (é visível apenas dentro de A)
- pode deixar o código mais legível e fácil de manter: deixar classes pequenas dentro de outras maiores coloca o código mais próximo de onde é usado
Como já disse, é meio vago se não tiver exemplos práticos, então sugiro ler o tutorial.
Mas só porque vc nunca viu, não quer dizer que não existe ou é pouco usado. O próprio JDK usa bastante. Por exemplo, na classe java.util.ArrayList
, o método iterator
retorna uma instância de Itr
:
public Iterator<E> iterator() {
return new Itr();
}
Aqui tem o código fonte, e logo abaixo do método vemos que Itr
é uma inner class (ou seja, está declarada dentro da classe ArrayList
). Segue um trecho relevante do código (ou seja, várias linhas foram omitidas):
public class ArrayList<E> extends AbstractList<E> implements List<E>, RandomAccess, Cloneable, java.io.Serializable {
//... um monte de código ...
public Iterator<E> iterator() {
return new Itr(); // retorna instância de Itr, que é uma inner class
}
private class Itr implements Iterator<E> {
// e mais código...
public E next() {
// ...
// *** Está acessando um campo privado de ArrayList ***
Object[] elementData = ArrayList.this.elementData;
// etc...
}
}
}
Provavelmente decidiram fazer assim porque o iterator é um objeto que vai iterar pelos elementos do ArrayList
, ou seja, ele precisa ter acesso a esses elementos. Ele está fortemente acoplado à lista, tanto que ele acessa um dos campos desta diretamente (em ArrayList.this.elementData
, sendo que elementData
não é público). Além disso, como é uma implementação que só serve para ArrayList
, não teria porque poluir o pacote com outra classe, e em vez disso criou-se uma inner class que só faz sentido ali, e ninguém mais precisa saber desse detalhe de implementação.
E no caso de classe anônima, eu poderia não criar a classe Itr
e criá-la sem nome:
// implementação alternativa, usando classe anônima
public Iterator<E> iterator() {
return new Iterator<E>() {
// um monte de código...
public E next() {
// etc...
Object[] elementData = ArrayList.this.elementData;
// etc...
}
};
}
Ou seja, é uma classe anônima (não dei um nome), criada ali na hora, e que também só faz sentido existir ali.
Neste caso, optaram por não ser anônima, porque a classe Itr
é usada em outros pontos do código (mas todos dentro de ArrayList
). Mas caso fosse usada somente em um único lugar, poderia ser trocada por uma classe anônima.
E isso é só um exemplo. O código do JDK (e de muitas outras bibliotecas famosas) está cheio de inner classes. Eu também uso às vezes, por exemplo quando preciso de uma classe utilitária pequena que só vai ser usada em um lugar específico (muitas vezes uso uma anônima mesmo).
Outro caso é quando preciso ordenar uma lista usando um critério bem específico que só faz sentido naquele lugar:
lista.sort(new Comparator<SomeClass>() {
public int compare(SomeClass o1, SomeClass o2) {
// lógica de comparação...
}
});
Se esse Comparator
fosse usado em mais lugares, ou fosse mais complexo (a ponto de deixar o código acima mais confuso), aí valeria a pena separar em outra classe.
Enfim, é um recurso que tem vários usos, e muitos vc só vai se deparar com o tempo.