O Javase8 lançado em 2013 incluirá um plano chamado Lambda Project, descrito no Draft do JSR-335 em junho deste ano.
O JSR-335 introduziu o fechamento em Java. O fechamento existe em muitos idiomas populares, como C ++, C#. O fechamento nos permite criar um ponteiro de função e passá -lo como um parâmetro. Neste artigo, examinaremos aproximadamente as características do Java8 e apresentaremos expressões de lambda. E tentarei colocar alguns programas de amostra para explicar alguns conceitos e gramática.
A linguagem de programação Java nos fornece o conceito de interface, e o método abstrato pode ser definido na interface. A interface define a API e espera que usuários ou fornecedores implementem esses métodos. Muitas vezes, não criamos classes de implementação independentes para algumas interfaces.
O uso anônimo é amplamente utilizado. A cena mais comum usada na classe interna anônima é o processador de eventos. Em segundo lugar, as classes internas anônimas são frequentemente usadas em programas multi -thread.
Assim como estamos discutindo, uma classe anônima é a implementação de uma determinada interface de um ninja. Normalmente, passamos o objeto desta classe de implementação como um parâmetro para um método e, em seguida, esse método chama o método da classe de implementação para a classe de implementação internamente. Portanto, essa interface é chamada de interface de retorno de chamada.
Embora as categorias anônimas sejam usadas em todos os lugares, elas ainda têm muitos problemas. O primeiro problema principal é a complexidade. Essas classes fazem com que os níveis do código pareçam confusos e complicados, também conhecidos como Privem Vertical. Segundo, eles não podem acessar os membros não finais da classe de embalagem. A palavra -chave disso se tornará muito confusa. Se uma classe anônima possui o mesmo nome de membro da sua classe de embalagem, as variáveis internas abrangem as variáveis de membro externo. Porque essa palavra -chave é digna do próprio objeto anônimo, em vez de seu objeto de encapsulamento.
Public void anonymousexample () {string não -finalVariable = "não -formal exmple"; Executar a variável do método "; // linha abaixo, fornece erros de compilação. println ("->" + this.variable);}}). A saída é:
-> Executar o método varial-> membro da classe Runnable
Este exemplo explica o problema que mencionei acima, e a expressão de Lambda quase resolve todos os problemas causados por classes internas anônimas. Antes de explorarmos ainda mais a expressão de Lambda, vamos dar uma olhada nas interfaces funcionais.
Interfaces funcionais
As interfaces funcionais são uma interface com apenas um método único, que representa esse contrato de método.
Apenas um na definição acima não é tão simples. Não entendo este parágrafo.
O exemplo a seguir mostra claramente como entender o conceito de interfaces funcionais.
interface runnable {void run ();} // funcionalInterface foo {boolean igual (object obj);} // não funcional; Funcional; não funcional; mesma assinaturaA maioria das interfaces de retorno de chamada são interfaces funcionais. Por exemplo, executável, chamável, comparador, etc. Foi chamado anteriormente SAM (Método Resumo único)
Expressão lambda
Como dissemos, um principal problema da categoria anônima é que o nível do código parece confuso, ou seja, a expressão vertical. A expressão lambda parece um método. Eles têm uma lista formal de parâmetros e bloqueio desses parâmetros.
(String s) -> s.lengh; () -> 43;
O exemplo acima significa que a primeira expressão recebe uma variável de sequência como um parâmetro e, em seguida, retorna o comprimento da string. O segundo sem parâmetros e retornar 43. Finalmente, o terceiro aceitou dois inteiros x e y e retornou à paz.
Depois de ler muitas palavras, finalmente, posso dar um exemplo da primeira expressão de Lambda.
Classe pública FirstLambdaExpression {public String variable = "Class Level Variable"; String não -FinalVariable = "Esta não é uma variável final"; ; A saída é:
-> Método Variável local-> Variável de nível de classe
Você pode comparar a diferença entre usar expressões Lambda e classe interna anônima. Podemos dizer claramente que escrever categorias anônimas usando a expressão Lambda resolve o problema da visibilidade variável. Você pode dar uma olhada nas anotações no código.
A sintaxe da expressão geral de lambda inclui uma lista de parâmetros, a palavra-chave de seta "->" é o corpo principal. O sujeito pode ser uma expressão (instrução de uma linha) ou uma frase de linha múltipla. Se for uma expressão, ele será calculado e retornado. Quebrar e continuar só pode ser usado dentro do ciclo.
Por que escolher esta forma gramatical especial, porque atualmente esse estilo geralmente está em C# e Scala, que também é uma escrita geral da expressão de Lambda. Esse design de gramática resolve basicamente a complexidade do tipo anônimo. Mas, ao mesmo tempo, ele também é muito flexível. O resultado da expressão é como seu próprio valor de retorno. Essa flexibilidade pode manter o código simples.
A expressão de lambda é usada como anônima, para que eles possam ser usados com flexibilidade em outros módulos ou outras expressões lambda (expressões de lambda aninhadas).
// A exposição lambda é anexada ao parâmetro Métodos. (() -> {System.out.println ("Executando em diferentes threads");});
Se você analisar mais de perto a expressão Lambda, verá que o tipo de interface de destino não faz parte de uma expressão. O compilador ajuda a inferir o tipo e o ambiente circundante da expressão de lambda.
A expressão lambda deve ter um tipo de destino e eles podem se adaptar a qualquer tipo de destino possível. Quando o tipo de destino é uma interface, as seguintes condições devem ser atendidas para compilar corretamente:
Como o compilador pode conhecer o tipo de parâmetro e o número através da instrução TIPO DE TARGEN, na expressão lambda, a declaração do tipo de parâmetro pode ser omitida.
Comparador c = (s1, s2) -> s1.comparetoignorecase (s2);
Além disso, se o método declarado no tipo de destino receber apenas um parâmetro (muitas vezes esse é o caso), os pequenos colchetes do parâmetro também podem ser gravados, por exemplo:
ActionListenr listerr = event-> event.getwhen ();
Uma pergunta muito óbvia vem: por que Lambda não expressa um nome de método especificado?
A resposta é: a expressão lambda só pode ser usada para a interface funcional, enquanto a interface funcional possui apenas um método.
Quando determinamos uma interface funcional para criar expressões de lambda, o compilador pode perceber a assinatura da interface funcional da lei chinesa e verificar se a expressão fornecida corresponde.
Essa gramática flexível nos ajuda a evitar o uso de privemas verticais anônimas e não trará privem horizontal (frase de passagem muito longa).
A expressão da expressão de Lambda está relacionada ao contexto, mas não são a primeira vez. Os operadores de diamante adicionados por Java SE 7 também têm esse conceito, que é inferido pelo contexto.
Void Invoke (Runnable r) {r.run ()} void Future Invoke (Callable R) {return c.compute ()} // acima são dois métodos interfacefuture s = Invoke (() -> "done"); / Qual invocar será chamado?
A resposta para a pergunta acima é chamar o método de receber o parâmetro chamável. Nesse caso, o compilador será resolvido através da carga de diferentes tipos de parâmetros. Quando há mais de um método de carregamento aplicável, o compilador também verifica a compatibilidade da expressão de lambda e o tipo de destino correspondente. Simplificando, espera -se que o método de invocado retorne, mas apenas um método de invasão tem um valor de retorno.
As expressões lambda podem ser explicitamente convertidas em tipos de destino especificados, desde que sejam compatíveis com os tipos correspondentes. Olhando para o programa a seguir, implementei três tipos de chamadas e todos eles o converteram em um tipo de classe.
Classe pública FirstWithLambdaExpressions {public static void main (string [] args) {listl = ArraysList (Callial)-> "Callable 1", (Call Alefi) ()-> "Callable 2", (chamável) (chamável))-> "Callable 3"); ) {e1.printStackTrace ();} e.shutdown ();} public void dumplist (lista) lança interruptException, executionExcepti em {for (futuro futuro: list) {System.out.println (futura.get ()); }}}Como discutimos anteriormente, as categorias anônimas não podem acessar as variáveis não finais no ambiente circundante. Mas não existe esse limite na expressão de lambda.
Atualmente, as interfaces funcionais definidas são aplicáveis apenas à interface. Tentei criar uma expressão lambda de apenas um método abstrato, mas um erro de compilação foi cometido. De acordo com o JSR -335, a versão futura da expressão lambda pode suportar classes funcionais.
Citação do método
Métodos referenciados como um método de referência sem chamá -lo.
A expressão lambda nos permite definir um método anônimo e usá -lo como uma instância da interface funcional. Os métodos são muito semelhantes à expressão de Lambda.
System :: GetProperty "ABC" :: PlayString :: SpecticeSuper :: TostringArrayList :: NOVO
A declaração acima mostra a sintaxe geral do método e a referência ao construtor. Aqui vimos um novo personagem operacional ":::: Double Colon). Não sei o nome exato como esse operador, mas JSR se refere a ele como separadores, e a página da Wikipedia se refere a ele como uma operação de análise de escopo .
A referência do alvo ou o receptor é colocado atrás do provedor e dos separadores. Isso forma uma expressão que pode citar um método. Na declaração final, o nome do método é "novo". Esta expressão cita o método da estrutura da classe Arraylist (a referência ao construtor na próxima seção)
Antes de entender isso, quero que você veja a força do método citado.
Importar java.util.arrays; = {New Funcionário ("Nick"), novo funcionário ("Robin"), novo funcionário ("Josh"), novo funcionário ("Andy"), novo funcionário ("Mark")}; Antes de classificar: "); DumpEmployee (funcionários); Arrays.sort (funcionários, funcionários :: myCompare); System.out.println (" Após a classificação: "); mployees);] funcionários) {para (Employee Emp: Arrays. aslist (funcionários)) {System.out.print (emp.name+",");} system.out.println (); (Employee EMP1, Employee Emp2) {return emp1.name.compareto (emp.name);}} A saída é:
Antes de classificar: Nick, Robin, Josh, Andy, Mark, depois de classificar: Andy, Josh, Mark, Nick, Robin,
A saída não é especial. Método estático O MyCompare recebe dois objetos de funcionários e retorna seus nomes para comparar.
No método principal, criei uma matriz diferente de um funcionário e o passei para o método ARRAYS.Sort para referenciá -lo à matriz de expressão (funcionário :: mycompare).
Espere um minuto, se olharmos para o Javadoc, você descobrirá que o segundo parâmetro do método de classificação é o tipo de corarator, mas passamos pela referência do método estático do funcionário. O problema importante está aqui.
Vamos dar uma olhada no porquê. Método Arrays.Sort espera uma instância de um comparador, e esse comparador é uma interface funcional, o que significa que ele tem apenas um método, ou seja, compare. Aqui também passamos maliciosamente em uma expressão de lambda, que fornece a implementação do método Compaare nessa expressão. Mas em nós, nossa aula de funcionários já tem um método de comparação. É apenas que seus nomes são diferentes.
Quando houver vários métodos com o mesmo nome, o compilador escolherá a melhor correspondência de acordo com o tipo de destino. Para entender, veja um exemplo:
Public static int myCompare (Employee EMP1, Empregado Emp2) {return emp1.name.compareto (emp.name);} // Outro método com o mesmo nome que o. {{) {{) {{Return int1.compareto (int2);} Eu criei duas matrizes diferentes para classificar.
Funcionário [] funcionários = {new Funcionário ("Nick"), novo funcionário ("Robin"), novo funcionário ("Josh"), novo funcionário ("Andy"), novo funcionário ("Mark")}; Ins = {1, 4, 8, 2, 3, 8, 6}; Agora, eu executo as duas linhas de código a seguir
Arrays.sort (funcionários, funcionário :: mycompare);
Aqui, o método de referências nas duas linhas de código é o mesmo (funcionário :: mycompare).
Não se deixe enganar pelo método estático, também podemos criar uma referência ao método de exemplo. Para métodos estáticos, usamos nomes de classe :: nome do método para gravar referência do método.
O exemplo acima é muito bom, mas não precisamos escrever um método para a comparação do número inteiro, porque o número inteiro implementou comparável e fornece um método de implementação Compareto. Então, apenas usamos a seguinte linha diretamente:
Arrays.sort (ints, Integer :: Compareto);
Vendo isso, você se sente um pouco confuso? Não? Então, deixe -me confundir você aqui. O método do membro é referenciado :: deve ser um objeto antes, mas por que a frase aqui é realmente legítima.
A resposta é: esse tipo de instrução permite o uso em alguns tipos específicos. O número inteiro é um tipo de dados e, para o tipo de dados, essa instrução é permitida.
Se transformarmos o método do funcionário myCompare em não -estático, use: funcionário :: mycompare, haverá erros de compilação: nenhum método adequado encontrado.
Referência de método construtivo
As referências do construtor são usadas como uma classe que faz referência a um construtor sem institucionalização especificada.
A referência do método construtivo é um novo recurso do Javase 8. Podemos construir uma referência a um método construtivo e passá -lo como um parâmetro para o tipo de destino.
Quando o usamos para fazer referência, citamos um método existente para usá -los. Da mesma forma, ao usar a referência do método construtivo, criamos uma referência aos métodos construtivos existentes.
Na seção anterior, vimos o nome da gramática referenciado pelo construtor :: novo, que parece uma referência de método. A referência a esse método construído pode ser atribuído às instâncias das interfaces funcionais de destino. Pode haver vários construtores em uma classe.
Para mim, é difícil escrever o primeiro método construtivo. No final, passei muito tempo trabalhando duro e, finalmente, "Ah, encontrei ...", olhe para o procedimento a seguir.
Public class construtorReferences {public static void main (string [] ar) {myInterface in = myclass :: new; } A saída é:
-> com.myclass@34e5307e
Isso parece um pouco incrível, certo?
Este exemplo despertou outro problema em meu coração: como instanciar um método construtivo com um parâmetro? Veja o procedimento abaixo:
Classe pública ConstructReferences {public static void main (string [] ar) {emlpoyeproder provedor = funcionário :: new; System.Println ("-> Age do funcionário:" EMP.AGE); idade) {this.name = nome; A saída é:
-> Nome do funcionário: John-> Idade do funcionário: 30
Antes de ler este artigo, vamos dar uma olhada no recurso mais legal do método Javase8-Default
Métodos padrão
O Javase8 introduzirá um conceito chamado método padrão. A versão Java antecipada da interface possui uma interface muito rigorosa. Na próxima versão Java, a implementação padrão do método na interface é permitida. Não há muita bobagem, veja o seguinte:
Classe pública DefaultMethods {public static void main (string [] ar) {NormalInterface Instância = new NorminterfaceImpl (); System.out.println ("-> myDefaultMethod");}} classe NormInterfaceImpl ImplemEleMerInterface {@Override public void myNormalMethod () {) System.out.println ("-> myNormethod");}}}}} A saída é:
-> myDefaultMethod
A interface acima declara dois métodos, mas a classe de implementação dessa interface apenas realiza um deles, porque o MyDefaultMethod usa os modificadores padrão e fornece um bloco de método para a implementação padrão. As regras de carga pesada da GM ainda entram em vigor aqui. Se a classe de implementação implementar o método na interface, será o método na classe de chamada ao ligar, caso contrário, a implementação padrão será chamada.
A interface da interface pai integrada pode aumentar, alterar e remover a implementação padrão da interface pai.
Interface ParentInterface {void inicialmentenomal (); -> inicialmentenomal ");} void inicialmenteDefault (); // agora um método normal} Neste exemplo, o ParentInterface define dois métodos, um é normal e o outro é implementado por padrão.
Imagine uma classe herdou a classe C, percebeu que a interface I e C tinham um método e o método que forneceu o método padrão no qual desde que o método padrão fosse compatível. Nesse caso, o método em C dará prioridade ao método padrão em I, e até o método em C ainda é prioritário quando o método é abstrato.
Classe pública DefaultMethods {public static void main (string [] AR) {interfaxe Impl = new NorminterfaceImpl (); ;}} interface interfaxe {public void defaultMethod () padrão {System.out.println ("-> interfaxe"); A saída é:
-> ParentClass
O segundo exemplo é que minha classe implementou duas interfaces diferentes, mas as duas interfaces fornecem a mesma instrução com o mesmo método de implementação padrão. Nesse caso, o compilador não poderá descobrir o que está acontecendo. Isso pode ser feito das seguintes maneiras.
Classe pública DefaultMethods {public static void main (string [] AR) {FirstInterface Impl = new Norminterfampl (); interface); A saída é:
-> SecondInterface
Agora, lemos a introdução do fechamento do Java. Neste artigo, entramos em contato com interfaces funcionais e fechamento de Java, que entendiam a expressão de Lambda de Java, referências de método e referências de construtor. E também escrevemos um exemplo do Hello World da expressão de Lambda.
Javase8 está chegando em breve.