Amigos, como você já percebeu, no final de junho, iniciaremos muitos novos grupos, entre eles o próximo fluxo do curso
"Java Developer" , que todos gostaram. No momento, estamos compartilhando com você uma nova tradução preparada para os alunos deste curso.

Ao adicionar novos recursos à linguagem de programação, os desenvolvedores de linguagem geralmente prestam atenção aos conflitos com os recursos atuais da linguagem, às alterações que levam à incompatibilidade com versões anteriores, aos erros e a quaisquer situações que possam levar a um comportamento indefinido ou inesperado.
No entanto, muitas vezes eles não prestam atenção suficiente a pequenas alterações nas novas técnicas práticas para escrever código. Essas alterações geralmente são efeitos colaterais de novos recursos. Alterações desse tipo não são, estritamente falando, novos recursos. Essas são mudanças sutis que apareceram devido a outros recursos ou suas combinações.
Classes internas anônimas
Em Java, classes internas são classes definidas como membros de uma classe. Existem quatro tipos de classes internas:
- estático aninhado
- interno
- local (método local)
- anônimo
Classes internas anônimas são classes sem nome que fornecem uma implementação de uma classe existente. Essas classes são amplamente usadas para manipular eventos na programação orientada a eventos. Normalmente, uma classe interna anônima fornece uma implementação imediata de uma classe abstrata. No entanto, isso não é necessário. Uma classe interna anônima pode ser criada a partir de uma classe não abstrata.
Eu acredito que há um ponto que não é totalmente compreendido em relação às classes internas anônimas. O fato é que o programador realmente
cria uma subclasse da classe de origem . Essa subclasse recebe o nome de
Class$X
, em que
Class
é a classe externa e
X
é o número que representa a ordem na qual as instâncias das classes internas são criadas na classe externa. Por exemplo, o
AnonDemo$3
é a terceira classe interna criada no
AnonDemo
. Você não pode chamar essas classes da maneira usual. E, diferentemente de outros tipos de classes internas, uma classe interna anônima é sempre implicitamente um filho do tipo a partir do qual foi criada (com exceção do uso de
var
, que veremos em breve).
Vejamos um exemplo.
class Anon { }; public class AnonDemo { public static void main (String[] args) { Anon anonInner = new Anon () { public String toString() { return "Overriden"; }; public void doSomething() { System.out.println("Blah"); }; }; System.out.println(anonInner.toString()); anonInner.doSomething();
Neste exemplo, instanciamos uma classe interna anônima com base na classe Anon. Em essência,
criamos uma subclasse sem nome de uma classe específica . Antes do Java 10, as classes internas anônimas eram quase sempre implicitamente polimórficas. Eu digo "quase", porque um código não polimórfico como esse, é claro, será executado.
new Anon() { public void foo() { System.out.println("Woah"); } }.foo();
No entanto, se quisermos atribuir o resultado da criação de uma instância de uma classe interna anônima ao tipo original, essa operação será polimórfica por natureza. Os motivos para isso estão no fato de criarmos implicitamente uma subclasse da classe que especificamos como a origem da classe interna anônima e não poderemos acessar o tipo de objeto específico (
Class$X
) para especificá-lo no código-fonte.
Polimorfismo e classes internas anônimas, consequências práticas
Você notou o código acima? Como usamos uma referência de classe base para um objeto de subclasse, de acordo com as leis do polimorfismo, podemos nos referir apenas a 1) métodos definidos pela classe base ou 2) métodos virtuais substituídos na subclasse.
Portanto, no trecho de código anterior, chamar
toString()
para um objeto de classe interno anônimo nos daria um valor "Substituído" substituído, no entanto, chamar
doSomething()
resultaria em um erro de compilação. Qual o motivo?
Um objeto de subclasse com uma referência ao tipo da classe base não tem acesso aos membros da subclasse por meio dessa referência à classe base. A única exceção a esta regra é se uma subclasse substituir o método da classe base. Nesse caso, o Java, fiel à sua natureza polimórfica, usa o Dynamic Method Dispatch para selecionar a versão do método da subclasse virtual em tempo de execução.
Se você ainda não sabia, um
método virtual é um método que pode ser substituído. Em Java, todos os métodos não finais, não privados e não estáticos são virtuais por padrão. Falo por padrão, e não implicitamente, porque diferentes jvm podem executar otimizações que podem mudar isso.
O que o Java 10 tem a ver com isso?
Um pequeno recurso chamado inferência de tipo. Veja o seguinte exemplo:
class Anon { }; public class AnonDemo { public static void main(String[] args) { var anonInner = new Anon() { public void hello() { System.out.println( "New method here, and you can easily access me in Java 10!\n" + "The class is: " + this.getClass() ); }; }; anonInner.hello();
Funciona, podemos chamar
hello()
! O diabo está nos detalhes. Se você está familiarizado com
var
, então você já entende o que está acontecendo aqui. Usando o nome reservado do tipo
var
, o
Java conseguiu determinar o tipo exato da classe interna anônima. Portanto, não estamos mais limitados a fazer referência à classe base para acessar o objeto da subclasse.O que fizemos antes do Java 10 quando precisamos de uma referência a uma subclasse?
Não é segredo que, no passado distante, mas não esquecido, houve um grande debate sobre as classes internas. E se isso é um segredo, então este é um dos piores segredos do mundo. Sem dúvida, muitos não aprovam o fato de que você precisa de uma referência exata a uma classe interna anônima, pois a idéia é evitar adicionar lixo à classe. Classes anônimas devem ser usadas para executar um contrato rapidamente, para uma operação logicamente relacionada a outra classe, por exemplo, para manipular eventos. No entanto, provavelmente, a curiosa Barbara não foi arrancada do nariz, e aposto que a maioria dos desenvolvedores é muito curiosa. Talvez em detrimento do senso comum!
Antes do Java 10, podemos obter um efeito semelhante usando a reflexão, da seguinte maneira:
Anon anonInner2 = new Anon() { public void hello() { System.out.println("Woah! "); }; }; anonInner2.getClass().getMethod("hello").invoke(anonInner2);
Fonte completa
Pode levar
aqui public class VarAnonInner { public static void main (String[] args) throws Exception { var anonInner = new Anon() { public void hello() { System.out.println("New method here, and you can easily access me in Java 10!\n" + "The class is: " + this.getClass() ); }; }; anonInner.hello(); Anon anonInner2 = new Anon() { public void hello() { System.out.println("Woah! "); }; }; anonInner2.getClass().getMethod("hello").invoke(anonInner2); new Anon() { public void hello() { System.out.println("Woah!!! "); }; }.hello();
Estamos aguardando seus comentários e lembre-se de que um
webinar gratuito sobre o curso ocorrerá hoje às 20h00.