Dúvida classe interna de método

7 respostas
L

Estou com uma dúvida sobre classes interna de método.Se alguém pode me ajudar eu agradeço.A dúvida é a seguinte:

O livro que eu estou lendo faz a seguinte afirmação: Um objeto da classe interna não poderá usar as variáveis locais do método onde a classe interna estiver, pois, as variavéis locais do método residem na pilha e só existem conforme a duração do método.A menos que as variavéis locais sejam marcadas como finais.

1 - Eu não entendi porque uma variavel marcada como final pode ser acessada por um objeto da classe interna de método.
2 - O que muda entre uma variavel sem o modificado final e com o modificador final, além, de não permitir alterar o valor?.
3 - Se marcada com final ela deixa de residir na pilha?

7 Respostas

T

Ela pode ser acessada porque internamente o compilador consegue obter o valor dessa variável “final”. Um exemplo:

class Objeto {
    private String nome;
    public Objeto (String pNome) { nome = pNome; }
    public String toString() { return nome; }
    
}

class TesteClasseInternaFinal {
    public static void main(String[] args) {
    
        final Objeto fin = new Objeto ("Obscuridade");
        
        class Interna {
            public String teste() {
                 return fin.toString();
            }
        }
        
        Interna inte = new Interna();
        System.out.println (inte.teste()); // deve imprimir "Obs"
    }
}

O compilador transforma isso em 3 classes (Objeto.class, TesteClasseInternaFinal.class e TesteClasseInternaFinal$1Interna.class). Se descompilarmos TesteClasseInternaFinal$1Interna.class e TesteClasseInternaFinal.class, ela é equivalente ao seguinte código Java:

class TesteClasseInternaFinal$1Interna {
    // gerado pelo compilador
    final Objeto val$fin;
    // gerado pelo compilador
    public TesteClasseInternaFinal$1Interna (Objeto obj) {
        val$fin = obj;
        super();
    }
    
    public String teste() {
        // repare que trocou "fin" por "val$fin", campo gerado pelo compilador
        return val$fin.toString();
    }
}

class TesteClasseInternaFinal {
    public TesteClasseInternaFinal () {
        super();
    }
    public static void main(String[] args) {
    
        final Objeto fin = new Objeto ("Obscuridade");
        // note que trocou "Interna inte = new Interna()" por um outro construtor
        // que inclui o tal objeto "final" 
        TesteClasseInternaFinal$1Interna inte = new TesteClasseInternaFinal$1Interna(fin);
        System.out.println (inte.teste()); // deve imprimir "Obs"
    }
}

Isso foi definido porque só dessa maneira é que dá para passar o valor para esse novo “construtor” gerado pelo compilador. É mais uma definição; o pessoal da Sun não precisaria obrigatoriamente ter feito essa restrição (só que o código gerado seria bem mais complicado que já é)

Não. A única diferença é que é usado o truque de compilação acima.

T

Para o exemplo, eu tive de criar um objeto de uma classe que não é String ou então um tipo primitivo.
Para String ou tipos primitivos, definir uma variável como final ativa algumas otimizações que em resumo acabam não criando esse campo (val$<nome da variável local final>) nem esse construtor mágico com mais um parâmetro.

L

Pow cara, eu entendi +ou- a sua explicação muito boa por sinal, mas foi o suficiente para entender…Lembrou-me muito gabiarra(hahaha) mas tudo bem.

Não vou ficar tentando entender essas implementações da sun. Vou só decorar que uma variavel local marcada como final pode ser recuperada numa classe interna de método.

Obrigado pela explicação.

T

O problema de usar uma variável local que não é “final” em uma classe interna está relacionado com o seguinte problema: se ela não fosse “final” e essa classe interna na verdade fosse uma thread*, você poderia ter o caso em que o valor da variável local é diferente do valor que o código da classe interna está executando. Como eles não quiseram correr o risco, então forçaram a barra e definiram que deveria ser “final”.
É possível gerar código que não impõe que seja “final”, mas nesse caso seria bastante complicado gerar o código (diferentemente da “gambiarra” que é possível ao se usar “final”).

  • Ou seja, se isto fosse possível (sem usar o “final”):
Objeto objeto = new Objeto("isto");
new Thread(new Runnable() { // aqui a "classe interna" aparece "disfarçada"
    public void run () {
        System.out.println (objeto);
    }
}).start();
     objeto = new Objeto ("aquilo");
L

Thigol, obrigado pela explicação.Ficou bem claro o motivo e a solução que a sun escolheu para esta “gambiarra” rsrs

Omeganosferatu

Thingol você descompilou os códigos pra ver isso ou foi de muringa ??? Poots show de bola a explicação, quanto mais eu frequento o GUJ mais eu vejo que tenho que estudar muito ainda

T

É claro que descompilei o código. Não sei se isso está documentado em algum lugar. (Minha cabeça não consegue guardar coisas que toda hora estão mudando; se não tiver acesso à Internet fico imediatamente burro e estúpido.)

Criado 18 de junho de 2007
Ultima resposta 19 de jun. de 2007
Respostas 7
Participantes 3