Rumah Java javaTutorial java中lambda表达式语法说明

java中lambda表达式语法说明

Jan 23, 2017 pm 03:42 PM
ungkapan java lambda

语法说明

一个lambda表达式由如下几个部分组成:

1. 在圆括号中以逗号分隔的形参列表。在CheckPerson.test方法中包含一个参数p,代表了一个Person类的实例。注意:lambda表达式中的参数的类型是可以省略的;此外,如果只有一个参数的话连括号也是可以省略的。比如上一节曾提到的代码:

p -> p.getGender() == Person.Sex.MALE
&& p.getAge() >= 18
    && p.getAge() <= 25
Salin selepas log masuk

2. 箭头符号:->。用来分隔参数和函数体。

3. 函数体。由一个表达式或代码块组成。在上一节的例子中使用了这样的表达式:

   
p.getGender() == Person.Sex.MALE
      && p.getAge() >= 18
      && p.getAge() <= 25
Salin selepas log masuk

如果使用的是表达式,java运行时会计算并返回表达式的值。另外,还可以选择在代码块中使用return语句:

p -> {
  return p.getGender() == Person.Sex.MALE
      && p.getAge() >= 18
      && p.getAge() <= 25;
}
Salin selepas log masuk

不过return语句并不是表达式。在lambda表达式中需要将语句用花括号括起来,然而却没有必要在只是调用一个返回值为空的方法时也用花括号括起来,所以如下的写法也是正确的:

email -> System.out.println(email)
Salin selepas log masuk

lambda表达式和方法的声明看起来有很多类似的地方。所以也可以把lambda表达式视为匿名方法,也就是没有定义名字的方法。

以上提到的lambda表达式都是只使用了一个参数作为形参的表达式。下面的实例类,Caulator,演示了如何使用多个参数作为形参:

package com.zhyea.zytools;
 
public class Calculator {
 
  interface IntegerMath {
    int operation(int a, int b);
  }
 
  public int operateBinary(int a, int b, IntegerMath op) {
    return op.operation(a, b);
  }
 
  public static void main(String... args) {
    Calculator myApp = new Calculator();
    IntegerMath addition = (a, b) -> a + b;
    IntegerMath subtraction = (a, b) -> a - b;
    System.out.println("40 + 2 = " + myApp.operateBinary(40, 2, addition));
    System.out.println("20 - 10 = " + myApp.operateBinary(20, 10, subtraction));
  }
}
Salin selepas log masuk

代码中operateBinary方法使用了两个整型参数执行算数操作。这里的算数操作本身就是IntegerMath接口的一个实例。在上面的程序中使用lambda表达式定义了两个算数操作:addition和subtraction。执行程序会打印如下内容:

40 + 2 = 42
20 - 10 = 10
Salin selepas log masuk

访问外部类的局部变量

类似于局部类或匿名类,lambda表达式也可以访问外部类的局部变量。不同的是,使用lambda表达式时无需考虑覆盖之类的问题。lambda表达式只是一个词法上的概念,这意味着它不需要从超类中继承任何名称,也不会引入新的作用域。也就是说,在lambda表达式中的声明和在它的外部环境中的声明意义是一样的。在下面的例子中对此作了演示:

package com.zhyea.zytools;
 
import java.util.function.Consumer;
 
public class LambdaScopeTest {
 
  public int x = 0;
 
  class FirstLevel {
 
    public int x = 1;
 
    void methodInFirstLevel(int x) {
      //如下的语句会导致编译器在statement A处报错“local variables referenced from a lambda expression must be final or effectively final”
      // x = 99;
      Consumer<integer> myConsumer = (y) ->{
        System.out.println("x = " + x); // Statement A
        System.out.println("y = " + y);
        System.out.println("this.x = " + this.x);
        System.out.println("LambdaScopeTest.this.x = " + LambdaScopeTest.this.x);
      };
 
      myConsumer.accept(x);
    }
  }
 
  public static void main(String... args) {
    LambdaScopeTest st = new LambdaScopeTest();
    LambdaScopeTest.FirstLevel fl = st.new FirstLevel();
    fl.methodInFirstLevel(23);
  }
}
Salin selepas log masuk


这段代码会输出如下内容:

   
x = 23
y = 23
this.x = 1
LambdaScopeTest.this.x = 0
Salin selepas log masuk


如果使用示例中lambda表达式myConsumer中的参数y替换为x,编译器就会报错:

Consumer<integer> myConsumer = (x) ->{
      // ....
    };
Salin selepas log masuk


编译器报错信息是:“variable x is already defined in method methodInFirstLevel(int)”,就是说在方法methodInFirstLevel中已经定义了变量x。报错是因为lambda表达式不会引入新的作用域。也因此呢,可以在lambda表达式中直接访问外部类的域字段、方法以及形参。在这个例子中,lambda表达式myConsumer直接访问了方法methodInFirstLevel的形参x。而访问外部类的成员时也是直接使用this关键字。在这个例子中this.x指的就是FirstLevel.x。

然而,和局部类或匿名类一样,lambda表达式也只能访问局部变量或外部被声明为final(或等同于final)的成员。比如,我们将示例代码methodInFirstLevel方法中“x=99”前面的注释去掉:

//如下的语句会导致编译器在statement A处报错“local variables referenced from a lambda expression must be final or effectively final”
x = 99;
Consumer<integer> myConsumer = (y) ->{
  System.out.println("x = " + x); // Statement A
  System.out.println("y = " + y);
  System.out.println("this.x = " + this.x);
  System.out.println("LambdaScopeTest.this.x = " + LambdaScopeTest.this.x);
};
Salin selepas log masuk


因为在这段语句中修改了参数x的值,使得methodInFirstLevel的参数x不可以再被视为final式的。因此java编译器就会在lambda表达式访问局部变量x的地方报出类似“local variables referenced from a lambda expression must be final or effectively final”这样的错误。

目标类型

该如何判断lambda表达式的类型呢。再来看一下筛选适龄服兵役人员的代码:

   
p -> p.getGender() == Person.Sex.MALE
       && p.getAge() >= 18
       && p.getAge() <= 25
Salin selepas log masuk


这段代码在两处用到过:

public static void printPersons(List roster, CheckPerson tester) —— 方案三
public void printPersonsWithPredicate(List roster, Predicate tester) —— 方案六

在调用printPersons方法时,这个方法期望一个CheckPerson 类型的参数,此时上面的那个表达式就是一个CheckPerson 类型的表达式。在调用printPersonsWithPredicate方法时,期望一个Predicate类型的参数,此时同样的表达式就是Predicate类型的。像这样子的,由方法期望的类型来决定的类型就叫做目标类型(其实我觉得scala中的类型推断用在这里更合适)。java编译器就是通过目标类型的上下文语境或者发现lambda表达式时的位置来判断lambda表达式的类型的。这也就意味着只能在Java编译器可以推断出类型的位置使用Lambda表达式:

变量声明;

赋值;

返回语句;

数组初始化;

方法或者构造器参数;

lambda表达式方法体;

条件表达式(?:);

抛出异常时。

目标类型和方法参数

对于方法参数,Java编译器还需要依赖两个语言特性来决定目标类型:重载解析和类型参数推断。

看一下下面的这两个函数式接口( java.lang.Runnable and java.util.concurrent.Callable):

public interface Runnable {
    void run();
  }
 
  public interface Callable<v> {
    V call();
  }
Salin selepas log masuk


Runnable.run()方法没有返回值,而Callable.call()方法有。

假设我们像下面这样重载了invoke方法:

void invoke(Runnable r) {
   r.run();
 }
 
 <t> T invoke(Callable<t> c) {
   return c.call();
 }
Salin selepas log masuk


那么在下面的语句中将会调用哪个方法呢:

String s = invoke(() -> "done");

调用的是invoke(Callable),因为这个方法有返回值,而invoke(Runnable)没有返回值。在这种情况下lambda表达式(() -> “done”)的类型是Callable

序列化

如果一个lambda表达式的目标类型还有它调用的参数的类型都是可序列化的,那么lambda表达式也是可序列化的。然而就像内部类一样,强烈不建议对lambda表达式进行序列化。

更多java中lambda表达式语法说明相关文章请关注PHP中文网!

Kenyataan Laman Web ini
Kandungan artikel ini disumbangkan secara sukarela oleh netizen, dan hak cipta adalah milik pengarang asal. Laman web ini tidak memikul tanggungjawab undang-undang yang sepadan. Jika anda menemui sebarang kandungan yang disyaki plagiarisme atau pelanggaran, sila hubungi admin@php.cn

Alat AI Hot

Undresser.AI Undress

Undresser.AI Undress

Apl berkuasa AI untuk mencipta foto bogel yang realistik

AI Clothes Remover

AI Clothes Remover

Alat AI dalam talian untuk mengeluarkan pakaian daripada foto.

Undress AI Tool

Undress AI Tool

Gambar buka pakaian secara percuma

Clothoff.io

Clothoff.io

Penyingkiran pakaian AI

Video Face Swap

Video Face Swap

Tukar muka dalam mana-mana video dengan mudah menggunakan alat tukar muka AI percuma kami!

Alat panas

Notepad++7.3.1

Notepad++7.3.1

Editor kod yang mudah digunakan dan percuma

SublimeText3 versi Cina

SublimeText3 versi Cina

Versi Cina, sangat mudah digunakan

Hantar Studio 13.0.1

Hantar Studio 13.0.1

Persekitaran pembangunan bersepadu PHP yang berkuasa

Dreamweaver CS6

Dreamweaver CS6

Alat pembangunan web visual

SublimeText3 versi Mac

SublimeText3 versi Mac

Perisian penyuntingan kod peringkat Tuhan (SublimeText3)

Adakah perisian keselamatan syarikat menyebabkan aplikasi gagal dijalankan? Bagaimana cara menyelesaikan masalah dan menyelesaikannya? Adakah perisian keselamatan syarikat menyebabkan aplikasi gagal dijalankan? Bagaimana cara menyelesaikan masalah dan menyelesaikannya? Apr 19, 2025 pm 04:51 PM

Penyelesaian masalah dan penyelesaian kepada perisian keselamatan syarikat yang menyebabkan beberapa aplikasi tidak berfungsi dengan baik. Banyak syarikat akan menggunakan perisian keselamatan untuk memastikan keselamatan rangkaian dalaman. …

Bagaimana dengan elegan mendapatkan nama pemboleh ubah kelas entiti untuk membina keadaan pertanyaan pangkalan data? Bagaimana dengan elegan mendapatkan nama pemboleh ubah kelas entiti untuk membina keadaan pertanyaan pangkalan data? Apr 19, 2025 pm 11:42 PM

Apabila menggunakan Mybatis-Plus atau Rangka Kerja ORM yang lain untuk operasi pangkalan data, sering diperlukan untuk membina syarat pertanyaan berdasarkan nama atribut kelas entiti. Sekiranya anda secara manual setiap kali ...

Bagaimana untuk memudahkan isu pemetaan medan dalam dok sistem menggunakan mapstruct? Bagaimana untuk memudahkan isu pemetaan medan dalam dok sistem menggunakan mapstruct? Apr 19, 2025 pm 06:21 PM

Pemprosesan pemetaan medan dalam dok sistem sering menemui masalah yang sukar ketika melaksanakan sistem dok: bagaimana untuk memetakan medan antara muka sistem dengan berkesan ...

Bagaimanakah saya menukar nama kepada nombor untuk melaksanakan penyortiran dan mengekalkan konsistensi dalam kumpulan? Bagaimanakah saya menukar nama kepada nombor untuk melaksanakan penyortiran dan mengekalkan konsistensi dalam kumpulan? Apr 19, 2025 pm 11:30 PM

Penyelesaian untuk menukar nama kepada nombor untuk melaksanakan penyortiran dalam banyak senario aplikasi, pengguna mungkin perlu menyusun kumpulan, terutama dalam satu ...

Bagaimanakah Idea IntelliJ mengenal pasti nombor port projek boot musim bunga tanpa mengeluarkan log? Bagaimanakah Idea IntelliJ mengenal pasti nombor port projek boot musim bunga tanpa mengeluarkan log? Apr 19, 2025 pm 11:45 PM

Mula musim bunga menggunakan versi IntelliJideaultimate ...

Bagaimana dengan elegan mendapatkan syarat pertanyaan bangunan pembolehubah kelas entiti apabila menggunakan tkmybatis untuk pertanyaan pangkalan data? Bagaimana dengan elegan mendapatkan syarat pertanyaan bangunan pembolehubah kelas entiti apabila menggunakan tkmybatis untuk pertanyaan pangkalan data? Apr 19, 2025 pm 09:51 PM

Apabila menggunakan tkmybatis untuk pertanyaan pangkalan data, bagaimana dengan anggun mendapatkan nama pembolehubah kelas entiti untuk membina keadaan pertanyaan adalah masalah biasa. Artikel ini akan ...

Bagaimana cara menukar objek Java dengan selamat ke array? Bagaimana cara menukar objek Java dengan selamat ke array? Apr 19, 2025 pm 11:33 PM

Penukaran objek dan tatasusunan Java: Perbincangan mendalam tentang risiko dan kaedah penukaran jenis cast yang betul Banyak pemula Java akan menemui penukaran objek ke dalam array ...

Bagaimana menggunakan penyelesaian cache Redis untuk merealisasikan keperluan senarai kedudukan produk dengan cekap? Bagaimana menggunakan penyelesaian cache Redis untuk merealisasikan keperluan senarai kedudukan produk dengan cekap? Apr 19, 2025 pm 11:36 PM

Bagaimanakah penyelesaian caching Redis menyedari keperluan senarai kedudukan produk? Semasa proses pembangunan, kita sering perlu menangani keperluan kedudukan, seperti memaparkan ...

See all articles