Rumah Java javaTutorial Java编程中使用lambda表达式的奇技淫巧

Java编程中使用lambda表达式的奇技淫巧

Jan 23, 2017 pm 03:03 PM

为什么使用Lambda表达式
先看几个例子:

第一个例子,在一个独立的线程中执行某项任务,我们通常这么实现:

class Worker implements Runnable {
  public void run() {
    for (int i = 0; i < 100; i++)
      doWork();
  }
  ...
}
 
Worker w = new Worker();
new Thread(w).start();
Salin selepas log masuk

第二个例子,自定义字符串比较的方法(通过字符串长度),一般这么做:

class LengthComparator implements Comparator<String> {
  public int compare(String first, String second) {
    return Integer.compare(first.length(), second.length());
  }
}
Arrays.sort(strings, new LengthComparator());
Salin selepas log masuk

第三个例子,在JavaFX中,给一个button添加一个callback:

button.setOnAction(new EventHandler<ActionEvent>() {
  public void handle(ActionEvent event) {
    System.out.println("Thanks for clicking!");
  }
});
Salin selepas log masuk

这些例子有一个共同点,就是:先定义一段代码块,传给某个对象或方法,然后被执行。在Lambda表
达式之前,Java是不允许直接传递代码块的,因为Java是面向对象的,因此必须传递一个对象,将要
执行的代码块封装到对象里。

Lambda表达式的语法
将上面第二个例子中的LengthComparator,用Lambda表达式表示为:

(String first, String second) -> Integer.compare(first.length(),
  second.length());
Salin selepas log masuk

->前为参数列表,其后为表达式语句体;

如果表达式语句体不止一行,则将语句体写在{}中,与普通的函数一样:

(String first, String second) -> {
  if (first.length() > second.length()) {
    return 1;
  } else if (first.length() == second.length()) {
    return 0;
  } else {
    return -1;
  }
};
Salin selepas log masuk

如果没有参数,()还是需要带上,比如上面的第一个例子,可以表示为:

() -> {
  for (int i = 0; i < 1000; i ++) {
    doWork();
  }
}
Salin selepas log masuk

如果参数的类型可以从上下文自动推断,则可以省略:

Comparator<String> comp
  = (first, second) // Same as (String first, String second)
  -> Integer.compare(first.length(), second.length());
Salin selepas log masuk

如果参数只有一个,且类型可以自动推断,则小括号()也可以省略:

// Instead of (event) -> or (ActionEvent event) ->
eventHandler<ActionEvent> listener =
  event -> System.out.println("Thanks for clicking!");
Salin selepas log masuk

lambda表达式的返回值的类型是自动推断的,因此不需要指明;在lambda表达式,某些条件分支中有
返回值,而其它分支没有返回值,是不允许的,如:

(x) -> {
  if (x >= 0) {
    return 1;
  }
}
Salin selepas log masuk

另外,expression lambda和statement lambda的区别是,expression lambda不需要
写return关键字,Java runtime会将表达式的结果作为返回值返回,而statement lambda是
写在{}中的表达式,需要使用return关键字,比如:

// expression lambda
Comparator<String> comp1 =
  (first, second) -> Integer.compare(first.length(), second.length());
 
// statement lambda
Comparator<String> comp2 = (first, second) ->
  { return Integer.compare(first.length(), second.length());};
Salin selepas log masuk

Functional Interface
如果一个接口(interface)仅有一个抽象方法(abstract method),就称为
Functional Interface,比如Runnable、Comparator等。
在任何一个需要Functional Interface对象的地方,都可以使用lambda表达式:

Arrays.sort(words,
  (first, second) -> Integer.compare(first.length(), second.length()));
Salin selepas log masuk

这里,sort()的第二个参数需要的是一个Comparator对象,而Comparator是
Functional Interface,因此可以直接传入lambda表达式,在调用该对象的compare()方法
时,就是执行该lambda表达式中的语句体;

如果lambda表达式的语句体会抛出异常,则对应的Functional Interface中的抽象方法必须抛
出了该异常,否则就需要在lambda表达式中显式捕获异常:

Runnable r = () -> {
  System.out.println("------");
  try {
    Thread.sleep(10);
  } catch (InterruptedException e) {
    // catch exception
  }
};
 
Callable<String> c = () -> {
  System.out.println("--------");
  Thread.sleep(10);
  return "";
};
Salin selepas log masuk

Method Reference
如果将lambda表达式的参数作为参数传递给一个方法,他们的执行效果是相同的,则该lambda表达式
可以使用Method Reference表达,以下两种方式是等价的:

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

其中System.out::println被称为Method Reference。

Method Reference主要有三种形式:

object::instanceMethod

Class::staticMethod

Class::instanceMethod

对于前两种方式,对应的lambda表达式的参数和method的参数是一致的,比如:

System.out::println
(x) -> System.out.println(x)
 
Math::pow
(x, y) -> Math.pow(x, y)
Salin selepas log masuk

对于第三种方式,对应的lambda表达式的语句体中,第一个参数作为对象,调用method,将其它参数
作为method的参数,比如:

String::compareToIgnoreCase
(s1, s2) -> s1.compareToIgnoreCase(s2)
1.5 Constructor Reference
Salin selepas log masuk

Constructor Reference与Method Reference类似,只不过是特殊的method:new,具体调用的是哪个构造函数,由上下文环境决定,比如:

List<String> labels = ...;
Stream<Button> stream = labels.stream().map(Button::new);
Salin selepas log masuk

Button::new等价于(x) -> Button(x),所以调用的构造函数是:Button(x);

除了创建单个对象,也可以创建对象数组,如下面两种方式等价:

int[]::new
(x) -> new int[x]
Salin selepas log masuk

变量作用域
lambd表达式会捕获当前作用域下可用的变量,比如:

public void repeatMessage(String text, int count) {
  Runnable r = () -> {
    for (int i = 0; i < count; i ++) {
      System.out.println(text);
      Thread.yield();
    }
  };
  new Thread(r).start();
}
Salin selepas log masuk

但是这些变量必须是不可变的,为什么呢?看下面这个例子:

int matches = 0;
for (Path p : files)
  new Thread(() -> { if (p has some property) matches++; }).start();
  // Illegal to mutate matches
Salin selepas log masuk

因为可变的变量在lambda表达式中不是线程安全的,这和内部类的要求是一致的,内部类中只能引用
外部定义的final变量;

lambda表达式的作用域与嵌套代码块的作用域是一样的,所以在lambd表达式中的参数名或变量名不
能与局部变量冲突,如:

Path first = Paths.get("/usr/bin");
Comparator<String> comp = (first, second) -> Integer.compare(first.length(),
   second.length()); // Error: Variable first already defined
Salin selepas log masuk

如果在lambda表达式中引用this变量,则引用的是创建该lambda表达式的方法的this变量,如:

public class Application() {
  public void doWork() {
    Runnable runner = () -> {
      ...;
      System.out.println(this.toString());
      ...
    };
  }
}
Salin selepas log masuk

所以这里的this.toString()调用的是Application对象的toString(),而不是Runnable
对象的。

Default Method
接口中只能有抽象方法,如果在已有的接口中新增一个方法,则该接口所有的实现类都需要实现该方法。
Java 8中引入了Default Method的概念,在接口中新增一个default方法,不会破坏已有的接
口规则,接口的实现类可以选择重写或直接继承该default方法,比如:

interface Person {
  long getId();
  default String getName() { return "John Q. Public"; }
}
Salin selepas log masuk

Java是允许多继承的,如果一个类的父类中定义的方法和接口中定义的default方法完全相同,或者
一个类的两个接口中定义了完全相同的方法, 则如何处理这种冲突呢?处理规则如下:

如果是父类和接口的方法冲突:以父类中的方法为准,接口中的方法被忽略;
如果两个接口中的default方法冲突,则需要重写该方法解决冲突;

Static Method
Java 8之前,接口中只能定义static变量,Java 8开始,接口中可以添加static方法,比如
Comparator接口新增了一系列comparingXXX的static方法,比如:

public static <T> Comparator<T> comparingInt(ToIntFunction<? super T>
  keyExtractor) {
  Objects.requireNonNull(keyExtractor);
  return (Comparator<T> & Serializable)
   (c1, c2) -> Integer.compare(keyExtractor.applyAsInt(c1),
       keyExtractor.applyAsInt(c2));
}
Salin selepas log masuk

使用这个static方法,以下两种方式也是等价的:

1、

Arrays.sort(cities, (first, second) -> Integer.compare(first.length(),
  second.length()));
Salin selepas log masuk

2、

Arrays.sort(cities, Comparator.comparingInt(String::length));
Salin selepas log masuk

所以,以后我们在设计自己的接口时,不需要再定义单独的工具类(如Collections/Collection),
在接口中使用static方法就行了。

匿名内部类

在 Java 世界中,匿名内部类 可以实现在应用程序中可能只执行一次的操作。例如,在 Android 应用程序中,一个按钮的点击事件处理。你不需要为了处理一个点击事件单独编写一个独立的类,可以用匿名内部类完成该操作:

Button button = (Button) findViewById(R.id.button1);
button.setOnClickListener(new OnClickListener() {
  
  @Override
  public void onClick(View view) {
    Toast.makeText(MainActivity.this, "Button Clicked", Toast.LENGTH_SHORT).show();
  }
  
});
Salin selepas log masuk

Lambda 示例

1.Runnable Lambda

来看几个示例, 下面是一个 Runnable 的示例:

public void runnableTest() {
    System.out.println("=== RunnableTest ===");
    // 一个匿名的 Runnable
    Runnable r1 = new Runnable() {
      @Override
      public void run() {
        System.out.println("Hello world one!");
      }
    };
    // Lambda Runnable
    Runnable r2 = () -> System.out.println("Hello world two!");
    // 执行两个 run 函数
    r1.run();
    r2.run();
  }
Salin selepas log masuk
public void runnableTest() {
  System.out.println("=== RunnableTest ===");
  // 一个匿名的 Runnable
  Runnable r1 = new Runnable() {
    @Override
    public void run() {
      System.out.println("Hello world one!");
    }
  };
 
  // Lambda Runnable
  Runnable r2 = () -> System.out.println("Hello world two!");
 
  // 执行两个 run 函数
  r1.run();
  r2.run();
}
Salin selepas log masuk

这两个实现方式都没有参数也没有返回值。Runnable lambda 表达式使用代码块的方式把五行代码简化为一个语句。
2.Comparator Lambda

在 Java 中,Comparator 接口用来排序集合。在下面的示例中一个 ArrayList 中包含了一些 Person 对象, 并依据 Person 对象的 surName 来排序。下面是 Person 类中包含的 fields:

public class Person {
  private String givenName;
  private String surName;
  private int age;
  private Gender gender;
  private String eMail;
  private String phone;
  private String address;
}
Salin selepas log masuk
Salin selepas log masuk
public class Person {
  private String givenName;
  private String surName;
  private int age;
  private Gender gender;
  private String eMail;
  private String phone;
  private String address;
}
Salin selepas log masuk
Salin selepas log masuk

下面是分别用匿名内部类和 Lambda 表达式实现 Comparator 接口的方式:

 
public class ComparatorTest {
  public static void main(String[] args) {
    List<Person> personList = Person.createShortList();
    // 使用内部类实现排序
    Collections.sort(personList, new Comparator<Person>() {
      public int compare(Person p1, Person p2) {
        return p1.getSurName().compareTo(p2.getSurName());
      }
    });
    System.out.println("=== Sorted Asc SurName ===");
    for (Person p : personList) {
      p.printName();
    }
    // 使用 Lambda 表达式实现
    // 升序排列
    System.out.println("=== Sorted Asc SurName ===");
    Collections.sort(personList, (Person p1, Person p2) -> p1.getSurName().compareTo(p2.getSurName()));
    for (Person p : personList) {
      p.printName();
    }
    // 降序排列
    System.out.println("=== Sorted Desc SurName ===");
    Collections.sort(personList, (p1, p2) -> p2.getSurName().compareTo(p1.getSurName()));
    for (Person p : personList) {
      p.printName();
    }
  }
}
Salin selepas log masuk
public class ComparatorTest {
  public static void main(String[] args) {
    List<Person> personList = Person.createShortList();
  
    // 使用内部类实现排序
    Collections.sort(personList, new Comparator<Person>() {
      public int compare(Person p1, Person p2) {
        return p1.getSurName().compareTo(p2.getSurName());
      }
    });
  
    System.out.println("=== Sorted Asc SurName ===");
    for (Person p : personList) {
      p.printName();
    }
  
    // 使用 Lambda 表达式实现
  
    // 升序排列
    System.out.println("=== Sorted Asc SurName ===");
    Collections.sort(personList, (Person p1, Person p2) -> p1.getSurName().compareTo(p2.getSurName()));
    for (Person p : personList) {
      p.printName();
    }
  
    // 降序排列
    System.out.println("=== Sorted Desc SurName ===");
    Collections.sort(personList, (p1, p2) -> p2.getSurName().compareTo(p1.getSurName()));
    for (Person p : personList) {
      p.printName();
    }
  }
}
Salin selepas log masuk

   

可以看到 匿名内部类可以通过 Lambda 表达式实现。注意 第一个 Lambda 表达式定义了参数的类型为 Person;而第二个 Lambda 表达式省略了该类型定义。Lambda 表达式支持类型推倒,如果通过上下文可以推倒出所需要的类型,则可以省略类型定义。这里由于 我们把 Lambda 表达式用在一个使用泛型定义的 Comparator 地方,编译器可以推倒出这两个参数类型为 Person 。

更多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

AI Hentai Generator

AI Hentai Generator

Menjana ai hentai secara percuma.

Artikel Panas

R.E.P.O. Kristal tenaga dijelaskan dan apa yang mereka lakukan (kristal kuning)
3 minggu yang lalu By 尊渡假赌尊渡假赌尊渡假赌
R.E.P.O. Tetapan grafik terbaik
3 minggu yang lalu By 尊渡假赌尊渡假赌尊渡假赌
R.E.P.O. Cara Memperbaiki Audio Jika anda tidak dapat mendengar sesiapa
3 minggu yang lalu By 尊渡假赌尊渡假赌尊渡假赌

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)

Bagaimanakah saya melaksanakan caching pelbagai peringkat dalam aplikasi java menggunakan perpustakaan seperti kafein atau cache jambu? Bagaimanakah saya melaksanakan caching pelbagai peringkat dalam aplikasi java menggunakan perpustakaan seperti kafein atau cache jambu? Mar 17, 2025 pm 05:44 PM

Artikel ini membincangkan pelaksanaan caching pelbagai peringkat di Java menggunakan kafein dan cache jambu untuk meningkatkan prestasi aplikasi. Ia meliputi persediaan, integrasi, dan faedah prestasi, bersama -sama dengan Pengurusan Dasar Konfigurasi dan Pengusiran PRA Terbaik

Bagaimanakah mekanisme kelas muatan Java berfungsi, termasuk kelas yang berbeza dan model delegasi mereka? Bagaimanakah mekanisme kelas muatan Java berfungsi, termasuk kelas yang berbeza dan model delegasi mereka? Mar 17, 2025 pm 05:35 PM

Kelas kelas Java melibatkan pemuatan, menghubungkan, dan memulakan kelas menggunakan sistem hierarki dengan bootstrap, lanjutan, dan pemuat kelas aplikasi. Model delegasi induk memastikan kelas teras dimuatkan dahulu, yang mempengaruhi LOA kelas tersuai

Bagaimanakah saya dapat melaksanakan teknik pengaturcaraan berfungsi di Java? Bagaimanakah saya dapat melaksanakan teknik pengaturcaraan berfungsi di Java? Mar 11, 2025 pm 05:51 PM

Artikel ini meneroka mengintegrasikan pengaturcaraan berfungsi ke dalam Java menggunakan ekspresi Lambda, API Streams, rujukan kaedah, dan pilihan. Ia menyoroti faedah seperti kebolehbacaan dan kebolehkerjaan kod yang lebih baik melalui kesimpulan dan kebolehubahan

Bagaimanakah saya boleh menggunakan JPA (Java Constence API) untuk pemetaan objek-objek dengan ciri-ciri canggih seperti caching dan malas malas? Bagaimanakah saya boleh menggunakan JPA (Java Constence API) untuk pemetaan objek-objek dengan ciri-ciri canggih seperti caching dan malas malas? Mar 17, 2025 pm 05:43 PM

Artikel ini membincangkan menggunakan JPA untuk pemetaan objek-relasi dengan ciri-ciri canggih seperti caching dan pemuatan malas. Ia meliputi persediaan, pemetaan entiti, dan amalan terbaik untuk mengoptimumkan prestasi sambil menonjolkan potensi perangkap. [159 aksara]

Bagaimanakah saya menggunakan Maven atau Gradle untuk Pengurusan Projek Java Lanjutan, Membina Automasi, dan Resolusi Ketergantungan? Bagaimanakah saya menggunakan Maven atau Gradle untuk Pengurusan Projek Java Lanjutan, Membina Automasi, dan Resolusi Ketergantungan? Mar 17, 2025 pm 05:46 PM

Artikel ini membincangkan menggunakan Maven dan Gradle untuk Pengurusan Projek Java, membina automasi, dan resolusi pergantungan, membandingkan pendekatan dan strategi pengoptimuman mereka.

Bagaimana saya menggunakan API Java's Nio (Input/Output Baru) untuk I/O yang tidak menyekat? Bagaimana saya menggunakan API Java's Nio (Input/Output Baru) untuk I/O yang tidak menyekat? Mar 11, 2025 pm 05:51 PM

Artikel ini menerangkan NIO API Java untuk I/O yang tidak menyekat, menggunakan pemilih dan saluran untuk mengendalikan pelbagai sambungan dengan cekap dengan satu benang. Ia memperincikan proses, faedah (skalabilitas, prestasi), dan potensi perangkap (kerumitan,

Bagaimanakah saya membuat dan menggunakan perpustakaan Java Custom (fail JAR) dengan pengurusan versi dan pergantungan yang betul? Bagaimanakah saya membuat dan menggunakan perpustakaan Java Custom (fail JAR) dengan pengurusan versi dan pergantungan yang betul? Mar 17, 2025 pm 05:45 PM

Artikel ini membincangkan membuat dan menggunakan perpustakaan Java tersuai (fail balang) dengan pengurusan versi dan pergantungan yang betul, menggunakan alat seperti Maven dan Gradle.

Bagaimana saya menggunakan API Soket Java untuk komunikasi rangkaian? Bagaimana saya menggunakan API Soket Java untuk komunikasi rangkaian? Mar 11, 2025 pm 05:53 PM

Artikel ini memperincikan API soket Java untuk komunikasi rangkaian, yang meliputi persediaan pelanggan-pelayan, pengendalian data, dan pertimbangan penting seperti pengurusan sumber, pengendalian ralat, dan keselamatan. Ia juga meneroka teknik pengoptimuman prestasi, i

See all articles