Javafree

Cap. 8 - Classes internas

Publicado por kuesley em 01/03/2010 - 124.988 visualizações

Capítulo 8 - Classes internas

8.1 - Onde deve ser usada e para que serve ?

Antes de qualquer introdução à esse assunto, vamos fazer uma explanação sobre as classes internas, onde e quando devem ser usadas, visto que esse assunto é um tanto contundente no que se refere a reutilização de código, extensabilidade e escalabilidade de um código sob o ótica da OO.

8.1.1 - Um exemplo oficial

Definir uma classe interna ou aninhada é um recurso interessante adicionado na versão 1.1 da Java, porém deve ser bem cauteloso o seu uso visto que se assim não o for, com certeza teremos redundância de código, classes extremamente complicadas de ser entendidas e com seu uso limitado, portanto saiba decidir seu uso. Vejamos um exemplo usado na própria framework de coleção Java, as classes Map herdam de uma superclasse chamada AbstractList, para seu funcionamento ela usa um objeto de uma classe interna chamado Entry e seu escopo está limitado à classe AbstractList, com um funcionamento totalmente peculiar à AbstractList, não teria muito sentido, criar uma classe externa para Entry, visto que sua utilização se limita à AbstractList. Definir classes especilialista é um conhecimento extremamente necessário no tocante à orientação a objeto e nesse caso a classe Entry nada mais é do que um membro (pois é assim que também pode ser chamadas as classes internas) de AbstractList.

8.2 - As classes internas são divididas em:

- Classe estática aninhada (top-level class)
- Classe interna comum
- Classe interna local de método
- Classe interna anônima


8.3 - Classes estáticas

Entenderemos agora o que são as classes estáticas que também podem ser chamadas como classe de nível superior ou top-level class, entretanto no fritar dos ovos é tudo a mesma coisa.
Uma classe estática não tem acesso aos membros da instância da classe encapsulada, somente os membros estáticos, tem a mesma visibilidade de uma classe externa.

Modificadores que podem ser atribuídos à uma classe interna estática:

static - obrigatório é claro
protected
public
private
abstract
final

* nunca abstract e final simultâneamente é claro!



Você NUNCA podería referencia o membro CountNonStatic dentro da classe Inner, uma vez que esse membro não é estático. Isso geraria um erro de compilação como podemos observar a seguir:



O erro acima é básico pra quem já está no capítulo 8 desse guia, mas vou mostrar o erro que o compilador gera:

[color=red:e4cc865dea]Erro de compilação: non-static variable CountNonStatic cannot be referenced from a static context.[/color:e4cc865dea]

Agora analisaremos o código a seguir:



Já o código acima compila sem nenhum problema, visto que CountStatic é um membro estático da classe e pode ser acessado sem que haja uma instância de Outer.
Você deve estar se perguntando: "Oxente, mas porquê então criar uma classe estática (top-level class) se ela se comporta da mesma forma que uma classe externa ?" - Essa questão é natural na cabeça de qualquer um. A resposta nem sempre, vejamos um explicação lógica para tal.

O que nós aprendemos no capítulo 2 sobre o modificador private ? Que um membro com modificador private só pode ser acesso de dentro da própria classe certo ? Mentira era tudo mentira ! Calma é só uma brincadeira. Note que no código anterior o membro CountStatic tem o modificador private, e mesmo assim está sendo acessado de uma classe que se comporta como classe externa porém não deixa de ser interna.
O que uma classe de nível superior têm de diferentes das classes externas no tocante à relacionamento com sua classe encapsulada é um acesso direto aos membros independentes de sua modificador de acesso. A seguir temos um exemplo prático do us desse tipo de classe:



Entender o código acima o fará abrir os horizontes para o uso de classes estáticas.

8.4 - Classe interna comum

O estudo de classes internas não segue uma orientação explícita da Sun, porém é comum se deparar com questões ao longo do exame, e você precisará saber algumas regras para não vacilar (errar mesmo) alguma questão pertinentes à esse assunto.
Você NUNCA poderá ter uma instância de uma classe interna sem que haja uma instância de uma classe externa, Vejamos:



Fixe bem isso: "Qualquer tentativa de instanciação da classe Inner sem que haja um OBJETO do tipo Outer, não funciona e entenderemos isso no que segue".
Podemos inferir a partir dessa asserção que um método estático de Outer nunca poderá instanciar uma classe Inner, visto que um método estático pode ser acesso sem um objeto propriamente dito e isso viola a regra definida anteriormente. Portanto:



O código anterior causa erro de compilação, visto que o método main é estático e pode ser chamado sem que haja uma instância de Outer, portanto erro: "Uma instãncia de Inner só poderá existir a partir sua classe externa" - mudando um pouco o código obteríamos um êxito na compilação:



Compilação executado sem nenhum erro !

8.4.1 - Classes internas e membros externos

Uma classe interna como já foi falado, pode ser chamada de membro de classe da classe externa, portanto se é um membro, ter visibilidade para qualquer membro da classe externa, isso mesmo, qualquer membro da classe externa pode ser acesso pela classe interna, vejamos:



Note que o membro x que é privado foi acesso por Inner sem nenhum erro. O código acima resulta em:

x before 0
x after: 1

8.4.2 - Instanciando um objeto interno fora da classe externa

Por mais que isso possa parecer estranho, mas é possível obter uma instância de uma classe interna fora de sua classe externa, vejamos o código que segue:



O código acima compila e é válido, pois para se obter uma instância Inner se informa sua classe externa - Outer.
Isso é muito interessante pode ser usado tambem para deixar o acoplamento de classes em um nível baixo - mas tome muito cuidado com isso!

8.4.3 - Referenciando a classe externa de dentro da classe interna

Ter uma referência da classe externa de dentro da classe interna pode parecer estranho, porém, às vezes, é necessário. Imaginemos uma situação em que um método da classe interna precise passar como referência a classe externa, viu ? Por mais que a classe interna tem acesso à todos os membros da classe externa, pode ser que em um caso atípico, você precise disso, ou acha que estamos colocando isso aqui por ser notório ?



Nota 1: Observe que a palavra chave this foi usada, nesse caso ele é uma referência ao objeto de Inner !
Nota 2: Note que this foi usado com o nome da classe externa, portanto, esse é o objeto da classe externa

8.4.4 - Modificadores aplicados as classes internas comuns

Como já foi falado mais tenho certeza que sua displicência o fez esqueçer :) - calma, calma é só um teste de paciência ! - uma classe interna é um membro da classe externa, portando os modificadores a seguir podem ser aplicados à uma classe interna, seguindo as mesmas regras do capítulo 2:

final

abstract

public

private

protected

static - com uma exceção que estudaremos mais adiante.

strictfp


8.5 - Classe interna local de método

Até agora vimos como uma classe pode ser criada dentro de outra, com escopo em toda a classe, pois bem, agora vamos reduzir esse escopo, isso mesmo, definir uma classe com escopo de método, vejamos:



Note que a classe Inner foi criada dentro do método see(), apesar de não fazer nada pois nenhuma instância foi criada de Inner dentro do método see.

8.5.1 - Modificador padrão

Uma classe de método tem o modificador private por padrão, visto que não pode ser instancia de nenhum outro local senão o método que encapsula a classe, vejamos um exemplo de uso:



Note que a classe Inner foi criada no meio do método, e isso é perfeitamente aceitável pelo compilador, só não tente instanciá-la antes de criar, pois isso seria um insulto ao compilador. O código acima resulta em:

before 10
after 10

A variável x não foi inicializada com 0 pois o construtor de Inner não foi chamado, vejamos o exemplo a seguir:



Resultado:

before 10
after 0

Não existe forma no mundo Java para instanciar uma classe interna de método fora do próprio método, se conhecerem não esqueçam de me avisar. Tentei mostrar um exemplo de instanciação inválida mas não fui criativo o suficiente, vejamos algo:



ou



8.5.2.1 - Modificadores aplicados à classe interna de método

Os únicos modificadores aplicados a essa classe são:

abstract
final


Nunca os dois ao mesmo tempo é claro! Pesquisamos muito para saber qual seria o sentido de criar uma classe de método abstrata. Se alguém descobrir esse enigma não esqueça de me avisar...

PODE ESPERAR QUE NA PROVA PODE CAIR ALGO ASSIM, MAS NÃO VACILEM !!

8.5.2 - Usando as variáveis automática de métodos

Uma classe interna de método não pode referenciar as variáveis locais de método, por incrível que pareça ! Você deve estar desgrenhando-se, mas não pode ! E existe uma explicação plausível para isso. Imagina que uma classe interna de método chame um método e precise passar como parâmetro a própria classe, como um método da classe interna pode referenciar uma variável automática após a execução do método, uma vez que esse sendo finalizado, suas variáveis são destruídas. Você pode estar se perguntando: "Oxente, mas como a classe interna precisará de uma variável local após a execução do método ?" Calma, imagine que o objeto da classe interna, que está no heap foi passado para um outro método, e esse tem sua execução mesmo após o método ter sido finalizado. Vejamos um exemplo inválido:



Nota 1: Note que a variável local y não pode ser referenciada dentro da classe interna de método, isso causaria um erro de compilação: local variable y is accessed from within inner class; needs to be declared final

Mas para aumentar a complexidade e o número de regras da linguagem Java, existe uma exceção para esse caso: Se a variável for final poderá ser referenciada (sei que você já tinha traduzido a mensagem de erro acima!), vejamos



Resultado:

before 10
after 5

8.5.3 - Modificadores de acesso

As mesmas regras de variáveis locais se aplicam as classes internas de métodos. Com isso podemos lembrar facilmente quais são os únicos modificadores aplicáveis às variáveis locais ?

a) public, private, protected, padrão
b) final, abstract, static
c) static, final, protected
d) abstract, final
e) todos os modificadores

[color=green:e4cc865dea]Resposta no final do capítulo !!![/color:e4cc865dea]

8.6 - Classes internas anônimas

Preste bastante atenção nesse tópico, pois você pode se confundir quando se deparar com questões que envolvem classes anônimas. Vejamos sua sintaxe:



Observe que a classe Car foi criada sem nenhuma anormalidade, porém a classe Vehicle foi criada com uma instância de Car, e se você for uma camarada esperto, notou que não houve um (;) (ponto-e-vírgula) após a declaração do membro car, ao contrário, foi criada uma instância de uma classe anônima (pois não foi definido um nome - que é uma subclasse de Car) com um novo método chamado break. A priori, pode parecer complicado, mas isso tudo nadamais é do que, uma herança em local exclusivo (ou pra complicar mesmo), ou seja, somente nesse ponto preciso de redefinir a classe X. Vejamos um outro exemplo para entender melhor:



Note que estamos realizando uma criação de métodos polimórficos para os objetos peao e mgr onde ambos estendem de Empregado.

Você é capaz de descobrir qual o erro da seguinte listagem ?



Observe que mgr é uma instância de Empregado, o qual define o métodos trabalhar, porém há uma tentativa de execução do método (que realmente foi definido na classe anônima) mgr.demite(), que o compilador acusa erro de ignorância, ou seja, o compilador só irá conhecer os métodos definidos na classe pai - qualquer tentativa de execução ou chamada de um método não existente, causará um erro de compilação: cannot resolve symbol

Não podemos esquecer...



Esse código é perfeitamente compilado pois a classe anônima que está sendo criada dentro do método enclosingMethod referencia a variável local que é definida como final e arg1 que também é um argumento com modificador final. Qualquer tentativa de referencia arg2 ou nonfinal dentro do método acima, causará erro de compilação, se estiver duvidando por tentar.

Se você realmente precisar de referenciar uma variável local dentro de uma classe anônima podemos dar uma dica, provavelmente você nunca precisará mas, só pra não perder a viagem: use array. Vejamos um exemplo:



Se você está lembrado do que estudamos nos capítulos iniciais, vai lembrar que o array é final seus elementos não. Portanto o resultado desse código será:

Before: 0
After: 25


8.6.2 - Implementando uma interface anonimamente

Você também poderá se deparar com o código a seguir:



Você deve estar se peguntando, como pode passar para um método uma instância de uma interface ? Por mais que possa parecer isso, não é o que está acontecendo, na verdade estamos criando uma classe anônima que implementa a interface Evento, tanto é que a nova classe teve que implementar todos os métodos de Eventos (clicar e arrastar), se um desses dois métodos não forem implementados na classe anônima um erro de compilação seria exibido. Pode parecer estranho, mas não é ! O que criamos aqui foi um implementador de classe como algumas literaturas assim o definem.

Mesmo que esse assunto de classes anônimas possa parecer diferentes as regras de herança, polimorfismo se aplicam, ou seja, uma tentativa de criar uma classe anônima que herda de uma classe abstrata deve obrigatóriamente implementar todos os seus métodos, isso porque você não pode definir uma classe anônima abstrata.



Vamos mudar um pouco o código acima:



O código acima gerará um erro de compilação, pois o método arrastar não foi implementado na nova classe (isso seria perfeitamente possível se a nova classe fosse abstrata) porém como é uma classe concreta deve implementá-lo.

8.6.3 - Classes finais não podem ser anônimas

O título anterior já falou tudo o que precisava ser falado, mas vamos ser menos suscinto. Aprendemos algum dia de nossa vida que uma classe marcada com o modificador final nunca poderia ser estendida, consequentemente nunca poderíamos ter uma classe anônimas a partir de uma classe final, pois uma classe anônima nada mais é do que uma herança de uma outra classe onde a subclasse não tem nome. Vejamos isso na prática:



O código acima gerará o seguinte erro de compilação:

[color=red:e4cc865dea]Erro de compilação: cannot inherit from final Boy[/color:e4cc865dea]

Bom não podemos esquecer das demais regras envolvendo classes e herança, como a visibilidade para com classes de outros pacotes, enfim, se tiver alguma dúvida quanto a isso volte ao capítulo 2.

8.6.4 - Um código real para esse caso...

Vamos ver um uso prático desse tipo de classe, inclusive vamos utilizar o mesmo exemplo do início do capítulo quando falávamos de classes estáticas, modificaremos um pouco o código exibido anteriormente:



Note que o código acima não criou uma classe estática como no início do capítulo, nesse caso foi criada uma classe anônima para resolver o mesmo problema, com isso podemos inferior que a utilização de classes internas é um assuntos extremamente relativo, podemos ser utilizado de diversas maneiras.

Esses exemplos foram tirados do guia de certificação da Sun !

Para encerrar esse assunto de classes anônimas, gostaríamos de explanar que o uso de classes anônimas são freqüentemente utilizadas em desenvolvimento de ambientes gráficos quando se usa AWT ou Swing principalmente para tratamento de eventos.

Estamos terminando nosso estudo, só falta mais um capítulo........ :!: :!:



Leia também:
Fundamentos da Linguagem
Modificadores
Operadores e atribuições
Controle de Fluxo
Orientação a Objetos
Java Lang e Wrappers
Objetos e Conjuntos
Classes Internas
Threads (Segmentos)