Descrição da sintaxe
Uma expressão de lambda consiste nas seguintes partes:
1. Lista separada por vírgula de parâmetros formais entre parênteses. O método checkPerson.test contém um parâmetro P, que representa uma instância da classe Pessoa. Nota: Os tipos de parâmetros nas expressões lambda podem ser omitidos; Além disso, se houver apenas um parâmetro, os suportes também poderão ser omitidos. Por exemplo, o código mencionado na seção anterior:
p -> p.getGender () == Person.sex.male && p.getage ()> = 18 && p.getage () <= 25
2. Símbolo de seta: ->. Usado para separar parâmetros e corpos de função.
3. Corpo de função. Consistindo em uma expressão ou bloco de código. No exemplo na seção anterior, essa expressão foi usada:
P.GetGender () == PERSON.SEX.MALE && p.getage ()> = 18 && p.getage () <= 25
Se você estiver usando uma expressão, o tempo de execução do Java calculará e retornará o valor da expressão. Além disso, você também pode optar por usar a declaração de retorno no bloco de código:
p -> {return p.getGender () == Person.sex.male && p.getage ()> = 18 && p.getage () <= 25; }No entanto, a declaração de retorno não é uma expressão. Nas expressões lambda, a declaração precisa ser fechada em aparelhos encaracolados, mas não há necessidade de envolver as declarações em aparelhos encaracolados ao apenas chamar um método com valor de retorno vazio, portanto o método de escrita a seguir também está correto:
Email -> System.out.println (email)
Existem muitas semelhanças na declaração de expressões e métodos lambda. Portanto, as expressões lambda também podem ser consideradas métodos anônimos, ou seja, métodos sem uma definição de nome.
As expressões Lambda mencionadas acima são todas as expressões que usam apenas um parâmetro como um parâmetro formal. A classe de exemplo a seguir, Caulator, demonstra como usar vários parâmetros como parâmetros formais:
pacote com.zhyea.zytools; calculadora de classe pública {interface integermath {int operação (int a, int b); } public int OperaçãoBinary (int a, int b, integermath op) {return op.operation (a, b); } public static void main (string ... args) {calculadora myApp = new calculator (); Integermath adição = (a, b) -> a + b; System.out.println ("40 + 2 =" + myApp.operatebinary (40, 2, adição)); System.out.println ("20 - 10 =" + myApp.operatebinary (20, 10, subtração)); }}O método OperatingBinary no código usa dois parâmetros inteiros para executar operações aritméticas. A operação aritmética aqui é um exemplo da interface integermath. No programa acima, duas operações aritméticas são definidas usando expressões Lambda: adição e subtração. A execução do programa imprimirá o seguinte conteúdo:
40 + 2 = 4220 - 10 = 10
Acesse variáveis locais de classes externas
Semelhante às aulas locais ou aulas anônimas, as expressões Lambda também podem acessar variáveis locais de classes externas. A diferença é que não há necessidade de considerar questões como superar ao usar expressões Lambda. A expressão de Lambda é apenas um conceito lexical, o que significa que não precisa herdar nenhum nome da superclasse, nem introduz novos escopos. Ou seja, a declaração em uma expressão de lambda tem o mesmo significado que a declaração em seu ambiente externo. Isso é demonstrado no exemplo a seguir:
pacote com.zhyea.zytools; importar java.util.function.consumer; classe pública lambdascopetest {public int x = 0; classe primeiro nível {public int x = 1; Void MethodInfirstLevel (int x) {// A seguinte declaração fará com que o compilador relate um erro "as variáveis locais referenciadas de uma expressão lambda devem ser finais ou efetivamente final" // x = 99; Consumidor <Teger> myconsumer = (y) -> {System.out.println ("x =" + x); // declaração A System.out.println ("y =" + y); System.out.println ("this.x =" + this.x); System.out.println ("lambdascopetest.this.x =" + lambdascopetest.this.x); }; myConsumer.Acept (x); }} public static void main (string ... args) {lambdascopetest st = new lambdascopetest (); Lambdascopetest.FirstLevel fl = St.New FirstLevel (); fl.methodinfirstlevel (23); }}Este código será lançado o seguinte:
x = 23y = 23This.x = 1lambdascopetest.This.x = 0
Se você substituir o parâmetro y na expressão lambda myconsumer no exemplo com x, o compilador relatará um erro:
Consumidor <Teger> myconsumer = (x) -> {// ....};A mensagem de erro do compilador é: "A variável x já está definida no método MethodInfirstLevel (int)", o que significa que a variável x foi definida no Method MethodInfirstLevel. Um erro é relatado porque a expressão de Lambda não introduz um novo escopo. Portanto, você pode acessar diretamente os campos de domínio, métodos e parâmetros formais da classe externa na expressão lambda. Neste exemplo, a expressão Lambda MyConsumer acessa diretamente o parâmetro x do método MethodInfirstLevel. Ao acessar os membros de classes externas, essa palavra -chave também é usada diretamente. Neste exemplo, this.x refere -se a primeirovel.x.
No entanto, como as aulas locais ou anônimas, as expressões Lambda podem acessar apenas variáveis locais ou membros externos declarados como final (ou equivalentes ao final). Por exemplo, removemos o comentário antes de "x = 99" no método de código de amostra MethodInfirstlevel:
// A declaração a seguir fará com que o compilador relate um erro "as variáveis locais referenciadas de uma expressão lambda deve ser final ou efetivamente final" x = 99; Consumidor <Teger> myconsumer = (y) -> {System.out.println ("x =" + x); // declaração A System.out.println ("y =" + y); System.out.println ("this.x =" + this.x); System.out.println ("lambdascopetest.this.x =" + lambdascopetest.this.x); };Como o valor do parâmetro x é modificado nesta instrução, o parâmetro x do MethodInfirstLevel não pode mais ser considerado como final. Portanto, o compilador Java relatará um erro como "variáveis locais referenciadas de uma expressão de lambda devem ser finais ou efetivamente final", onde a expressão lambda acessa a variável local x.
Tipo de destino
Como determinar o tipo de expressão de lambda? Vamos dar uma olhada no código para filtrar o pessoal militar da idade apropriada:
p -> p.getGender () == Person.sex.male && p.getage ()> = 18 && p.getage () <= 25
Este código foi usado em dois lugares:
public static void PrintPersons (lista de <Person>, testador de checkperson) - Solução 3
public void PrintPerSonsWithPredicate (lista de <Person>
Ao chamar o método PrintPersons, esse método espera um parâmetro de tipo de checkperson. Neste momento, a expressão acima é uma expressão do tipo CheckPerson. Ao chamar o método PrintPerSonsWithPredicate, é esperado um parâmetro do Type Predicado <SONED>. Neste momento, a mesma expressão é do Type Prediced <Pesso>. Assim, o tipo determinado pelo tipo esperado pelo método é chamado de tipo de destino (na verdade, acho que a inferência do tipo em scala é mais apropriada aqui). O compilador Java determina o tipo de expressão de uma lambda através do contexto do tipo de destino ou da posição ao descobrir a expressão de Lambda. Isso significa que as expressões lambda só podem ser usadas onde o compilador Java pode inferir o tipo:
Tipo de alvo e parâmetros de método
Para parâmetros de método, o compilador Java também precisa confiar em dois recursos de idioma para determinar o tipo de destino: análise de sobrecarga e inferência de parâmetro de tipo.
Dê uma olhada nas duas interfaces funcionais a seguir (java.lang.runnable e java.util.concurrent.callable <V>):
interface pública runnable {void run (); } interface pública Callable <V> {v Calling (); }O método runnable.run () não retorna um valor, enquanto o método Callable.Call () o possui.
Suponha que sobrecarregamos o método de invocar como o seguinte:
void Invoke (Runnable r) {r.run (); } <t> t Invoke (Callable <T> C) {return c.call (); }Então, qual método será chamado na seguinte declaração:
String s = invoke(() -> "done");
Invoke (Callable <T>) é chamado porque esse método tem um valor de retorno, enquanto o Invoke (Runnable <T>) não retorna o valor. Nesse caso, o tipo de expressão lambda (() -> "done" é chamável <T>.
Serialização
Se o tipo de alvo de uma expressão lambda e os tipos de parâmetros que ele chama forem serializáveis, a expressão de Lambda também será serializável. No entanto, como as classes internas, a serialização das expressões lambda não é fortemente recomendada.