Por que usar expressões lambda
Vamos dar uma olhada em alguns exemplos:
O primeiro exemplo é executar uma tarefa em um tópico separado, que geralmente implementamos da seguinte maneira:
a classe trabalhadora implementa Runnable {public void run () {for (int i = 0; i <100; i ++) dowork (); } ...} trabalhador w = new Worker (); novo thread (w) .start ();O segundo exemplo é um método de comparação de string personalizado (por comprimento da string), que geralmente é feito:
classe LengthComParator implementa o comparador <String> {public int compare (string primeiro, string segundo) {return integer.compare (primeiro.length (), Second.Length ()); }} Arrays.sort (Strings, new Lengthcomparator ());No terceiro exemplo, em Javafx, adicione um retorno de chamada a um botão:
Button.SetonAction (novo EventHandler <CoctionEvent> () {public void Handle (Evento ActionEvent) {System.out.println ("Obrigado por clicar!");}});Esses exemplos têm uma coisa em comum, que primeiro define um bloco de código, passa para um objeto ou método e depois o execute. Antes das expressões Lambda, o Java não permite a aprovação direta dos blocos de código, porque o Java é orientado a objetos; portanto, um objeto deve ser passado para encapsular o bloco de código a ser executado no objeto.
Sintaxe de expressão de lambda
O Comprimento do comparador no segundo exemplo acima é expresso como uma expressão de lambda:
(String primeiro, String segundo) -> Integer.comPare (primeiro.Length (), Second.Length ());
-> Antes é a lista de parâmetros, seguida pelo corpo da declaração de expressão;
Se o corpo de uma declaração de expressão for superior a uma linha, o corpo da declaração será escrito em {}, assim como uma função comum:
(String primeiro, string segundo) -> {if (primeiro.Length ()> Second.Length ()) {return 1; } else if (primeiro.Length () == Second.Length ()) {return 0; } else {return -1; }};Se não houver parâmetros, () ainda precisar ser trazido com você. Por exemplo, o primeiro exemplo acima pode ser expresso como:
() -> {for (int i = 0; i <1000; i ++) {dowork (); }}Se o tipo do parâmetro puder ser inferido automaticamente do contexto, você poderá omitir:
Comparador <string> comp = (primeiro, segundo) // o mesmo que (string primeiro, string segundo) -> Integer.comPare (primeiro.Length (), Second.Length ());
Se houver apenas um parâmetro e o tipo puder ser inferido automaticamente, os colchetes () também poderão ser omitidos:
// em vez de (Event) -> ou (Evento ActionEvent) -> EventHandler <ctionEvent> ouvinte = event -> System.out.println ("Obrigado por clicar!");O tipo de valor de retorno da expressão lambda é inferido automaticamente, portanto não precisa ser especificado; Na expressão de Lambda, alguns ramos condicionais têm valores de retorno, mas outros ramos não têm valores de retorno, o que não é permitido, como:
(x) -> {if (x> = 0) {return 1; }}Além disso, a diferença entre a expressão lambda e a instrução lambda é que a expressão lambda não precisa escrever a palavra -chave de retorno. O tempo de execução do Java retornará o resultado da expressão como o valor de retorno, enquanto a instrução lambda é uma expressão escrita em {}, e a palavra -chave de retorno precisa ser usada, por exemplo:
// Expression lambdacomparator <string> comp1 = (primeiro, segundo) -> Integer.comPare (primeiro.Length (), Second.Length ()); // Declaração lambdacomparator <string> comp2 = (primeiro, segundo) -> {return Integer.comPare (First.Length (), Second.Lound ();}; Interface funcional
Se uma interface tiver apenas um método abstrato, é chamado
Interface funcional, como Runnable, Comparator, etc.
Em qualquer lugar onde o objeto de interface funcional é necessário, você pode usar expressões Lambda:
Arrays.sort (palavras, (primeiro, segundo) -> Integer.comPare (primeiro.Length (), Second.Length ()));
Aqui, o segundo parâmetro do tipo () requer um objeto comparador, e o comparador é
Interface funcional, para que você possa passar diretamente na expressão Lambda. Ao chamar o método compare () do objeto, é executar o corpo da declaração na expressão de Lambda;
Se a declaração da expressão de Lambda lançar uma exceção, o método abstrato correspondente na interface funcional deve lançar a exceção, caso contrário, é necessário capturar explicitamente a exceção na expressão de Lambda:
Runnable r = ()-> {System.out.println ("-------"); tente {thread.sleep (10); } catch (interruptedException e) {// Catch Exception}}; callable <string> c = ()-> {System.out.println ("----------"); Thread.sleep (10); retornar "";}; Referência do método
Se os parâmetros da expressão de Lambda forem passados como parâmetros para um método e seu efeito de execução for o mesmo, a expressão lambda poderá ser expressa usando a referência do método, e os dois métodos a seguir serão equivalentes:
(x) -> System.out.println (x) System.out :: println
Entre eles, System.out :: println é chamado de referência do método.
A referência do método vem principalmente de três formas:
Para os dois primeiros métodos, os parâmetros de expressão lambda correspondentes e os parâmetros de método são os mesmos, como:
System.out :: println (x) -> system.out.println (x) math :: pow (x, y) -> math.pow (x, y)
Para o terceiro método, no corpo da declaração de expressão lambda correspondente, o primeiro parâmetro é usado como um objeto, o método é chamado e outros parâmetros são usados como parâmetros do método, como:
String :: comparetoignorecase (S1, S2) -> S1.comparetoignorecase (S2) 1.5 Referência do construtor
A referência do construtor é semelhante à referência do método, mas é um método especial: novo. O construtor específico é determinado pelo ambiente de contexto, como:
List <String> RABELS = ...; Stream <butter> stream = rabels.stream (). Map (botão :: novo);
Button :: New é equivalente a (x) -> botão (x), então o construtor chamado é: botão (x);
Além de criar um único objeto, você também pode criar uma variedade de objetos, como os dois equivalentes a seguir:
int [] :: new (x) -> new int [x]
Escopo variável
As expressões Lambd capturam variáveis disponíveis no escopo atual, como:
public void repeatMessage (texto da string, int conting) {runnable r = () -> {for (int i = 0; i <count; i ++) {System.out.println (text); Thread.yield (); }}; novo thread (r) .start ();}Mas essas variáveis devem ser imutáveis, por quê? Veja o seguinte exemplo:
int corresponde = 0; para (caminho p: arquivos) novo thread () -> {if (p tem alguma propriedade) corresponde ++;}). start (); // ilegal para mutaçõesComo as variáveis mutáveis não são seguras de roscas nas expressões de lambda, isso é consistente com os requisitos das classes internas, e apenas variáveis finais definidas externamente podem ser referenciadas nas classes internas;
O escopo da expressão de Lambda é o mesmo do bloco de código aninhado; portanto, o nome do parâmetro ou o nome da variável na expressão do Lambd não pode conflitar com variáveis locais, como:
Caminho primeiro = caminhos // Erro: variável primeiro já definida
Se essa variável for referenciada em uma expressão de lambda, a referência é essa variável do método que cria a expressão de lambda, como:
public class Application () {public void Dowork () {runnable runner = () -> {...; System.out.println (this.toString ()); ...}; }} Então, aqui this.ToString () chama ToString () do objeto de aplicação, não executado
Objeto.
Método padrão
Só pode haver métodos abstratos na interface. Se um novo método for adicionado a uma interface existente, todas as classes de implementação da interface precisam implementar esse método.
O Java 8 apresenta o conceito de método padrão e adiciona um método padrão à interface, que não destruirá as regras de interface existentes. A classe de implementação da interface pode optar por substituir ou herdar diretamente o método padrão, como:
Pessoa da interface {long getId (); String padrão getName () {return "John Q. public"; }}O Java permite herança múltipla. Como lidar com esse conflito se os métodos definidos na classe pai de uma classe são exatamente iguais aos métodos padrão definidos na interface, ou as duas interfaces de uma classe são exatamente as mesmas, como lidar com esse conflito? As regras de processamento são as seguintes:
Se o método entrar em conflito entre a classe pai e a interface: os métodos na classe pai prevalecerão e os métodos na interface serão ignorados;
Se o método padrão nos dois conflitos de interfaces, você precisará substituir o método para resolver o conflito;
Método estático
Antes do Java 8, apenas variáveis estáticas podem ser definidas na interface. A partir de Java 8, métodos estáticos podem ser adicionados à interface, como
A interface do comparador adicionou uma série de métodos estáticos de comparaçãoxxx, como:
public static <t> comparador <T> ComparingInt (ToIntfunction <? Super T> keyExtractor) {Objects.RequirenOnNull (KeyExtractor); Return (comparador <T> & Serializable) (C1, C2) -> INTEGER.COMPARE (KEYEXTRATOR.APPLYASINT (C1), KeyExtractor.Applyasint (C2));}Usando este método estático, os dois métodos a seguir também são equivalentes:
1.
Arrays.sort (cidades, (primeiro, segundo) -> Integer.comPare (primeiro.Length (), Second.Length ()));
2.
Arrays.sort (cidades, comparador.comparingint (string :: length));
Portanto, quando projetamos nossas próprias interfaces no futuro, não precisamos mais definir classes de ferramentas separadas (como coleções/coleta).
Basta usar o método estático na interface.
Classe interna anônima
No mundo Java, as classes internas anônimas podem implementar operações que só podem ser realizadas uma vez em um aplicativo. Por exemplo, em um aplicativo Android, um evento de clique de botão é tratado. Você não precisa escrever uma classe separada para lidar com um evento de clique, você pode fazer isso com uma classe interna anônima:
Botão botão = (botão) FindViewById (r.id.button1); button.setOnClickListener (newClickListener () {@Override public void OnClick (View) {Toast.MakeText (mainActivity.This, "Button Clicked", Toast.LengthEd_Short) .Show (); Lambda Exemplo 1. Lambda Runnable Vamos analisar vários exemplos. Aqui está um exemplo de Runnable: public void runnableTest () {System.out.println ("=== runnableTest ==="); // um runnable runnable anônimo r1 = new Runnable () {@Override public void run () {System.out.println ("Hello World One!"); }}; // Lambda Runnable Runnable r2 = () -> System.out.println ("Hello World Two!"); // executa duas funções de execução r1.run (); r2.run (); } public void runnableTest () {System.out.println ("=== runnableTest ==="); // um runnable runnable anônimo r1 = new Runnable () {@Override public void run () {System.out.println ("Hello World One!"); }}; // Lambda Runnable Runnable r2 = () -> System.out.println ("Hello World Two!"); // executa duas funções de execução r1.run (); r2.run (); } Nem a implementação nem o valor de retorno são retornados. Expressões lambda executáveis usam blocos de código para simplificar o código de cinco elementos em uma instrução. public class Pessoa {String Private String Givenname; Sobrenome Privado String; private Int Age; gênero de gênero privado; e -mail privado de string; telefone de corda privada; endereço de sequência privada;} public class Pessoa {String Private String Givenname; Sobrenome Privado String; private Int Age; gênero de gênero privado; e -mail privado de string; telefone de corda privada; endereço de sequência privada;} A seguir, é como implementar a interface do comparador usando classes internas anônimas e expressões lambda: classe pública ComparatorTest {public static void main (string [] args) {list <sesso> PERSONLIST = PERSON.CREATESHORTLISTLIST (); // Use a classe interna para implementar a classificação de coleções.sort (PERSOLLIST, NOVO COMPARADOR <SONESPEL> () {public Int Compare (Pessoa P1, Pessoa P2) {return p1.getSurname (). Compare (p2.getSurname ());}}); System.out.println ("=== classificado sobre o sobrenome ASC ==="); para (Pessoa P: PersonList) {P.printName (); } // implementação usando a expressão lambda // ascendente do sistema.out.println ("=== classificado sobre o sobrenome ==="); Coleções.sort (PersonList, (Pessoa P1, Pessoa P2) -> P1.getSurname (). Compareto (p2.getSurname ())); para (Pessoa P: PersonList) {P.printName (); } // Desc Subsequencial System.out.println ("=== Sobrenome descrito classificado ==="); Coleções.sort (PersonList, (P1, P2) -> P2.GetSurname (). Compareto (P1.getSurname ())); para (Pessoa P: PersonList) {P.printName (); }}} classe pública ComparatorTest {public static void main (string [] args) {list <sesso> PERSONLIST = PERSON.CREATESHORTLISTLIST (); // Use a classe interna para implementar a classificação de coleções.sort (PERSOLLIST, NOVO COMPARADOR <SONESPEL> () {public Int Compare (Pessoa P1, Pessoa P2) {return p1.getSurname (). Compare (p2.getSurname ());}}); System.out.println ("=== classificado sobre o sobrenome ASC ==="); para (Pessoa P: PersonList) {P.printName (); } // implementação usando a expressão lambda // ascendente do sistema.out.println ("=== classificado sobre o sobrenome ==="); Coleções.sort (PersonList, (Pessoa P1, Pessoa P2) -> P1.getSurname (). Compareto (p2.getSurname ())); para (Pessoa P: PersonList) {P.printName (); } // Desc Subsequencial System.out.println ("=== Sobrenome descrito classificado ==="); Coleções.sort (PersonList, (P1, P2) -> P2.GetSurname (). Compareto (P1.getSurname ())); para (Pessoa P: PersonList) {P.printName (); }}} Você pode ver que as classes internas anônimas podem ser implementadas através de expressões Lambda. Observe que a primeira expressão lambda define o tipo de parâmetro como pessoa; A segunda expressão lambda omite a definição de tipo. As expressões Lambda suportam o knockdown do tipo e, se o tipo necessário puder ser deduzido através do contexto, a definição de tipo poderá ser omitida. Aqui, como usamos expressões Lambda em um comparador que usa definições genéricas, o compilador pode deduzir esses dois parâmetros como pessoa.