天天看點

Java8.0新特性之Lambda表達式

  好程式員Java教程分享Java8.0新特性之Lambda表達式:Java 8 已經釋出很久了,很多報道表明Java 8 是一次重大的版本更新。本篇文章,主要給大家介紹的是lambda表達式。

Lambda表達式

Lambda表達式(也稱為閉包)是Java 8中最大和最令人期待的語言改變。它允許我們将函數當成參數傳遞給某個方法,或者把代碼本身當作資料處理:函數式開發者非常熟悉這些概念。

很多JVM平台上的語言(Groovy、Scala等)從誕生之日就支援Lambda表達式,但是Java開發者沒有選擇,隻能使用匿名内部類代替Lambda表達式。

Lambda的設計耗費了很多時間和很大的社群力量,最終找到一種折中的實作方案,可以實作簡潔而緊湊的語言結構。而lambda表達式的使用需要和函數式接口結合。

1.函數式接口

1.1.概念

函數式接口在Java中是指:有且僅有一個抽象方法的接口。 函數式接口,即适用于函數式程式設計場景的接口。而Java中的函數式程式設計展現就是Lambda,是以函數式接口就是可 以适用于Lambda使用的接口。隻有確定接口中有且僅有一個抽象方法,Java中的Lambda才能順利地進行推導。 備注:“文法糖”是指使用更加友善,但是原理不變的代碼文法。例如在周遊集合時使用的for-each文法,其實 底層的實作原理仍然是疊代器,這便是“文法糖”。從應用層面來講,Java中的Lambda可以被當做是匿名内部 類的“文法糖”,但是二者在原理上是不同的。

1.2,格式

隻要確定接口中有且僅有一個抽象方法即可:

  1. 修飾符 interface 接口名稱 {
  2. public abstract 傳回值類型 方法名稱(可選參數資訊);
  3. }

1.3 @FunctionalInterface注解

與 @Override 注解的作用類似,Java 8中專門為函數式接口引入了一個新的注解: @FunctionalInterface 。該注解可用于一個接口的定義上:

  1. @FunctionalInterface
  2. public interface MyFunctionalInterface {
  3. void myMethod();

一旦使用該注解來定義接口,編譯器将會強制檢查該接口是否确實有且僅有一個抽象方法,否則将會報錯。需要注 意的是,即使不使用該注解,隻要滿足函數式接口的定義,這仍然是一個函數式接口,使用起來都一樣.

2.函數式接口的使用

2.1函數式接口作為參數,方法不帶參數

  1. //定義函數式接口
  2. public interface MyInterface{
  3. public abstract void show();
  4. //使用(匿名内部類對象/函數式)
  5. public class Demo01 {
  6. public static void main(String[] args) {
  7. method01(new MyInterface01() {
  8. @Override
  9. public void show() {
  10. System.out.println("你好,函數式接口");
  11. });
  12. // 函數式
  13. method01(() -> {
  14. // 函數式簡寫(如果方法體中隻有一句代碼)
  15. method01(() -> System.out.println("你好,函數式接口"));
  16. public static void method01(MyInterface01 inter) {
  17. inter.show();

函數式接口的優勢

函數式接口比匿名内部類對象産生更少的位元組碼對象,提升java執行效率.

2.2, 函數式接口作為參數,方法帶參數

  1. public interface MyInterface02 {
  2. public abstract void show(String msg1, String msg2);
  3. //使用函數式接口
  4. //匿名内部類對象
  5. method01(new MyInterface02() {
  6. public void show(String msg1, String msg2) {
  7. System.out.println(msg1 + msg2);
  8. //函數式完整
  9. method01((String msg1, String msg2) -> {
  10. //函數式簡寫
  11. method01((msg1, msg2) -> System.out.println(msg1 + msg2));
  12. public static void method01(MyInterface02 inter) {
  13. inter.show("hello", "函數式");

2.3, 函數式接口作為傳回值,方法不帶參數

  1. getInter1().show("你好", "函數式");
  2. getInter2().show("你好", "函數式");
  3. // 函數式完整
  4. public static MyInterface02 getInter1() {
  5. return (String msg1, String msg2) -> {
  6. };
  7. // 函數式簡寫
  8. public static MyInterface02 getInter2() {
  9. return (msg1, msg2) -> System.out.println(msg1 + msg2);

3.函數式程式設計應用場景

3.1,概念

在兼顧面向對象特性的基礎上,Java語言通過Lambda表達式使用函數式接口,就叫做函數式程式設計

3.2, 使用lambada作為參數

如果抛開實作原理不說,Java中的Lambda表達式可以被當作是匿名内部類的替代品。如果方法的參數是一個函數 式接口類型,那麼就可以使用Lambda表達式進行替代。

  1. public class Demo04Runnable{
  2. private static void startThread(Runnable task){
  3. new Thread(task).start();
  4. startThread(()‐>System.out.println("線程執行"));

3.3, 使用函數式接口作為傳回值

如果一個方法的傳回值類型是一個函數式接口,那麼就可以直接傳回一個Lambda表達式。

  1. public class Demo06Comparator {
  2. private static Comparator getComparator(){
  3. return (num1,num2)‐> num1 - num2;
  4. Integer[] array = {3,2,1};
  5. Arrays.sort(array, getComparator());
  6. //周遊數組

3.4, 函數式接口的方法有傳回值

  1. showMsg(new MyInterface03() {
  2. public String getMsg() {
  3. return "hello functional interface";
  4. // lambada表達式
  5. showMsg(() -> {
  6. return "hello1 functional interface";
  7. // lambda表達式簡寫
  8. showMsg(() -> "hello1 functional interface");
  9. public static void showMsg(MyInterface03 inter) {
  10. String msg = inter.getMsg();
  11. System.out.println(msg);

4.常用函數式接口(Supplier接口)

JDK提供了大量常用的函數式接口以豐富Lambda的典型使用場景,它們主要在 java.util.function 包中被提供。 下面是簡單的幾個接口及使用示例。

4.1,Supplier接口

java.util.function.Supplier 接口僅包含一個無參的方法: T get() 。用來擷取一個泛型參數指定類型的對象資料。由于這是一個函數式接口,這也就意味着對應的Lambda表達式需要“對外提供”一個符合泛型類型的對象資料

4.2,基本使用

  1. private static String getString(Supplier function ){
  2. return function.get();
  3. public static void main(String[] args){
  4. String msgA="Hello";
  5. String msgB="World";
  6. System.out.println(getString(()->msgA+msgB));

4.2,綜合案例

  需求:使用 Supplier 接口作為方法參數類型,通過Lambda表達式求出int數組中的最大值。提示:接口的泛型請使用 java.lang.Integer 類。

  1. Integer max = getMax(()->{
  2. Integer[] nums = {1,2,3,4};
  3. int max2 = nums[0];
  4. for (Integer num : nums) {
  5. if(max2 < num){
  6. max2 = num;
  7. return max2;
  8. System.out.println(max);
  9. public static Integer getMax(Supplier supplier){
  10. return supplier.get();

  

5.常用函數式接口(Consumer接口)

5.1,Consumer接口

java.util.function.Consumer 接口則正好與Supplier接口相反,它不是生産一個資料,而是消費一個資料, 其資料類型由泛型決定

5.2,accept方法

Consumer 接口中包含抽象方法 void accept(T t) ,意為消費一個指定泛型的資料。基本使用如:

  1. consumeString((msg)->System.out.println(msg));
  2. public static void consumeString(Consumer consumer){
  3. consumer.accept("hello");

5.3, andThen方法

如果一個方法的參數和傳回值全都是 Consumer 類型,那麼就可以實作效果:消費資料的時候,首先做一個操作, 然後再做一個操作,實作組合。而這個方法就是 Consumer 接口中的default方法 andThen

  1. default Consumer andThen(Consumer<? super T> after) {
  2. Objects.requireNonNull(after);
  3. return (T t) -> { accept(t); after.accept(t); };

注: java.util.Objects 的 requireNonNull 靜态方法将會在參數為null時主動抛出 NullPointerException 異常。這省去了重複編寫if語句和抛出空指針異常的麻煩。

需求:先列印大寫HELLO,再列印小寫hello

  1. consumeString((msg) -> System.out.println(msg.toUpperCase()),
  2. (msg) -> System.out.println(msg.toLowerCase()));
  3. public static void consumeString(Consumer consumer1, Consumer consumer2) {
  4. consumer1.andThen(consumer2).accept("hello");

6.常用函數式接口(Predicate接口)

有時候我們需要對某種類型的資料進行判斷,進而得到一個boolean值結果。這時可以使用 java.util.function.Predicate 接口

6.1, test方法

Predicate 接口中包含一個抽象方法: boolean test(T t) 。用于條件判斷的場景

  1. public enum SingleClass06 {
  2. INSTANCE;

6.2,基本使用

  1. System.out.println(predicateTest((msg) -> msg.length() > 3, "hello"));
  2. public static boolean predicateTest(Predicate predicate,String msg){
  3. return predicate.test(msg);

7.總結

在本文中,我們學會了使用lambda表達式的不同方式,同時也學習了java8.0開始自帶的一些常用函數式接口。

繼續閱讀