搜尋
首頁Javajava教程Java8 新特性之 Lambda 表達式
Java8 新特性之 Lambda 表達式Feb 23, 2017 am 10:34 AM
java8lambda新特性

摘要: lambda表達式是Java8帶給我們的幾個重量級新功能之一,借用lambda表達式,可以讓我們的Java程式設計更簡潔。本文是Java8新特性的第一篇,將探討行為參數化、lambda表達式,以及方法參考。

lambda表達式是java8帶給我們的幾個重量級新特性之一,借用lambda表達式,可以讓我們的java程式設計更加簡潔。最近新的專案摒棄了1.6的版本,全面基於java8進行開發,本文是java8新特性的第一篇,將探討行為參數化、lambda表達式,以及方法引用。

一. 行為參數化

行為參數化簡單的說就是函數的主體只包含模板類別通用程式碼,而一些會隨著業務場景而變化的邏輯則以參數的形式傳遞到函數之中,採用行為參數化可以讓程式更加的通用,以因應頻繁變更的需求。

考慮一個業務場景,假設我們需要透過程式對蘋果進行篩選,我們先定義一個蘋果的實體:

/**
 * 苹果实体
 *
 * @author zhenchao.wang 2016-09-17 12:49
 * @version 1.0.0
 */
public class Apple {
    /** 编号 */
    private long id;
    /** 颜色 */
    private Color color;
    /** 重量 */
    private float weight;
    /** 产地 */
    private String origin;
    public Apple() {
    }
    public Apple(long id, Color color, float weight, String origin) {
        this.id = id;
        this.color = color;
        this.weight = weight;
        this.origin = origin;
    }
    // 省略getter和setter
}

用戶最開始的需求可能只是簡單的希望能夠透過程序篩選出綠色的蘋果,於是我們可以很快的通過程序實現:

/**
 * 筛选绿苹果
 *
 * @param apples
 * @return
 */
public static List<Apple> filterGreenApples(List<Apple> apples) {
    List<Apple> filterApples = new ArrayList<>();
    for (final Apple apple : apples) {
        if (Color.GREEN.equals(apple.getColor())) {
            filterApples.add(apple);
        }
    }
    return filterApples;
}

如果過了一段時間用戶提出了新的需求,希望能夠通過程序篩選出紅色的蘋果,於是我們又針對性的添加了篩選紅色蘋果的功能:

/**
 * 筛选红苹果
 *
 * @param apples
 * @return
 */
public static List<Apple> filterRedApples(List<Apple> apples) {
    List<Apple> filterApples = new ArrayList<>();
    for (final Apple apple : apples) {
        if (Color.RED.equals(apple.getColor())) {
            filterApples.add(apple);
        }
    }
    return filterApples;
}

更好的實現是把顏色作為一個參數傳遞到函數中,這樣就可以應付以後用戶提出的各種顏色篩選請求了:

/**
 * 自定义筛选颜色
 *
 * @param apples
 * @param color
 * @return
 */
public static List<Apple> filterApplesByColor(List<Apple> apples, Color color) {
    List<Apple> filterApples = new ArrayList<>();
    for (final Apple apple : apples) {
        if (color.equals(apple.getColor())) {
            filterApples.add(apple);
        }
    }
    return filterApples;
}

這樣設計了之後,再也不用擔心用戶的顏色篩選需求變化了,但是不幸的是,某一天使用者提了一個需求要求能夠選擇重量達到某一標準的蘋果,有了前面的教訓,我們也把重量的標準作為參數傳遞給篩選函數,於是得到:

/**
 * 筛选指定颜色,且重要符合要求
 *
 * @param apples
 * @param color
 * @param weight
 * @return
 */
public static List<Apple> filterApplesByColorAndWeight(List<Apple> apples, Color color, float weight) {
    List<Apple> filterApples = new ArrayList<>();
    for (final Apple apple : apples) {
        if (color.equals(apple.getColor()) && apple.getWeight() >= weight) {
            filterApples.add(apple);
        }
    }
    return filterApples;
}

這樣透過傳遞參數的方式真的好嗎?如果篩選條件越來越多,組合模式越來越複雜,我們是不是需要考慮到所有的情況,並針對每一種情況都有相應的應對策略呢,並且這些函數僅僅是篩選條件的部分不一樣,其餘部分都是相同的模板程式碼(遍歷集合),這時候我們就可以將行為 參數化 ,讓函數只保留模板程式碼,而把篩選條件抽離出來當做參數傳遞進來,在在java8之前,我們透過定義一個過濾器介面來實現:

/**
 * 苹果过滤接口
 *
 * @author zhenchao.wang 2016-09-17 14:21
 * @version 1.0.0
 */
@FunctionalInterface
public interface AppleFilter {
    /**
     * 筛选条件抽象
     *
     * @param apple
     * @return
     */
    boolean accept(Apple apple);
}
/**
 * 将筛选条件封装成接口
 *
 * @param apples
 * @param filter
 * @return
 */
public static List<Apple> filterApplesByAppleFilter(List<Apple> apples, AppleFilter filter) {
    List<Apple> filterApples = new ArrayList<>();
    for (final Apple apple : apples) {
        if (filter.accept(apple)) {
            filterApples.add(apple);
        }
    }
    return filterApples;
}

透過上面行為抽象化之後,我們可以在具體呼叫的地方設定篩選條件,並將條件作為參數傳遞到方法中:

public static void main(String[] args) {
    List<Apple> apples = new ArrayList<>();
    // 筛选苹果
    List<Apple> filterApples = filterApplesByAppleFilter(apples, new AppleFilter() {
        @Override
        public boolean accept(Apple apple) {
            // 筛选重量大于100g的红苹果
            return Color.RED.equals(apple.getColor()) && apple.getWeight() > 100;
        }
    });
}

上面的行為參數化方式採用匿名類別來實現,這樣的設計在jdk內部也經常採用,例如

java.util.Comparator

java.util.concurrent.Callable

等,使用這一類別介面的時候,我們都可以在具體呼叫的地方用過匿名類別來指定函數的具體執行邏輯,不過從上面的程式碼區塊來看,雖然很極客,但是不夠簡潔,在java8中我們可以透過lambda來簡化:

// 筛选苹果
List<Apple> filterApples = filterApplesByAppleFilter(apples,
        (Apple apple) -> Color.RED.equals(apple.getColor()) && apple.getWeight() >= 100);

透過lambda表達式極大的精簡了程式碼,下面來學習java的lambda表達式吧~

二. lambda表達式定義

我們可以將lambda表達式定義為一種 簡潔、可傳遞的匿名函數,首先我們需要明確lambda表達式本質上是一個函數,雖然它不屬於某個特定的類,但具備參數列表、函數主體、返回類型,以及能夠拋出異常;其次它是匿名的,lambda表達式沒有具體的函數名稱;lambda表達式可以像參數一樣進行傳遞,從而極大的簡化程式碼的編寫。格式定義如下:

格式一:參數列表-> 表達式
格式二:參數列表-> {表達式集合}
需要注意的是,lambda表達式隱含了return關鍵字,所以在單一的表達式中,我們不需要明確的寫return關鍵字,但是當表達式是一個語句集合的時候,則需要明確添加return,並用花括號

{ }

將多個表達式包圍起來,下面看幾個例子:

//返回给定字符串的长度,隐含return语句
(String s) -> s.length() 
// 始终返回42的无参方法
() -> 42 
// 包含多行表达式,则用花括号括起来
(int x, int y) -> {
    int z = x * y;
    return x + z;
}

三. 依託於函數式介面使用lambda表達式

lambda表達式的使用需要借助於函數式接口,也就是說只有函數式接口出現地方,我們才可以將其用lambda表達式進行簡化。

自訂函數式介面

函數式介面定義為只具備 一個抽象方法 的介面。 java8在介面定義上的改進就是引入了預設方法,使得我們可以在介面中對方法提供預設的實現,但是不管存在多少個預設方法,只要具備一個且只有一個抽象方法,那麼它就是函數式接口,如下(引用上面的AppleFilter):

/**
 * 苹果过滤接口
 *
 * @author zhenchao.wang 2016-09-17 14:21
 * @version 1.0.0
 */
@FunctionalInterface
public interface AppleFilter {
    /**
     * 筛选条件抽象
     *
     * @param apple
     * @return
     */
    boolean accept(Apple apple);
}
AppleFilter

僅包含一個抽象方法

accept(Apple apple)

,依照定義可以將其視為一個函數式接口,在定義時我們為該介面添加了

@FunctionalInterface

注解,用于标记该接口是函数式接口,不过这个接口是可选的,当添加了该接口之后,编译器就限制了该接口只允许有一个抽象方法,否则报错,所以推荐为函数式接口添加该注解。

jdk自带的函数式接口

jdk为lambda表达式已经内置了丰富的函数式接口,如下表所示(仅列出部分):




函数式接口 函数描述符 原始类型特化
Predicate T -> boolean IntPredicate, LongPredicate, DoublePredicate
Consumer T -> void IntConsumer, LongConsumer, DoubleConsumer
Funcation T -> R IntFuncation, IntToDoubleFunction, IntToLongFunction, LongFuncation…
Supplier () -> T BooleanSupplier, IntSupplier, LongSupplier, DoubleSupplier
UnaryOperator T -> T IntUnaryOperator, LongUnaryOperator, DoubleUnaryOperator
BinaryOperator (T, T) -> T IntBinaryOperator, LongBinaryOperator, DoubleBinaryOperator
BiPredicate (L, R) -> boolean
BiConsumer (T, U) -> void
BiFunction (T, U) -> R

下面分别就

Predicate<T>

Consumer<T>

Function<T, R>

的使用示例说明。

Predicate

@FunctionalInterface
public interface Predicate<T> {
    /**
     * Evaluates this predicate on the given argument.
     *
     * @param t the input argument
     * @return {@code true} if the input argument matches the predicate,
     * otherwise {@code false}
     */
    boolean test(T t);
}

Predicate的功能类似于上面的

AppleFilter

,利用我们在外部设定的条件对于传入的参数进行校验,并返回验证结果

boolean

,下面利用

Predicate

对List集合的元素进行过滤:

/**
 * 按照指定的条件对集合元素进行过滤
 *
 * @param list
 * @param predicate
 * @param 
 * @return
 */
public  List filter(List list, Predicate<T> predicate) {
    List newList = new ArrayList();
    for (final T t : list) {
        if (predicate.test(t)) {
            newList.add(t);
        }
    }
    return newList;
}

利用上面的函数式接口过滤字符串集合中的空字符串:

demo.filter(list, (String str) -> null != str && !str.isEmpty());
Consumer<T>
@FunctionalInterface
public interface Consumer<T> {
    /**
     * Performs this operation on the given argument.
     *
     * @param t the input argument
     */
    void accept(T t);
}

Consumer提供了一个accept抽象函数,该函数接收参数,但不返回值,下面利用

Consumer

遍历集合:

/**
 * 遍历集合,执行自定义行为
 *
 * @param list
 * @param consumer
 * @param 
 */
public  void filter(List list, Consumer<T> consumer) {
    for (final T t : list) {
        consumer.accept(t);
    }
}

利用上面的函数式接口,遍历字符串集合,并打印非空字符串:

demo.filter(list, (String str) -> {
        if (StringUtils.isNotBlank(str)) {
            System.out.println(str);
        }
    });

Function

@FunctionalInterface
public interface Function<T, R> {
    /**
     * Applies this function to the given argument.
     *
     * @param t the function argument
     * @return the function result
     */
    R apply(T t);
}

Funcation执行转换操作,输入是类型T的数据,返回R类型的数据,下面利用

Function

对集合进行转换:

/**
 * 遍历集合,执行自定义转换操作
 *
 * @param list
 * @param function
 * @param 
 * @param 
 * @return
 */
public  List filter(List list, Function<T, R> function) {
    List newList = new ArrayList();
    for (final T t : list) {
        newList.add(function.apply(t));
    }
    return newList;
}

下面利用上面的函数式接口,将一个封装字符串(整型数字的字符串表示)的接口,转换成整型集合:

demo.filter(list, (String str) -> Integer.parseInt(str));
上面这些函数式接口还提供了一些逻辑操作的默认实现,留到后面介绍java8接口的默认方法时再讲吧~

使用过程中需要注意的一些事情

类型推断

在编码过程中,有时候可能会疑惑我们的调用代码会去具体匹配哪个函数式接口,实际上编译器会根据参数、返回类型、异常类型(如果存在)等做正确的判定。

在具体调用时,在一些时候可以省略参数的类型,从而进一步简化代码:

/

/ 筛选苹果
List<Apple> filterApples = filterApplesByAppleFilter(apples,
        (Apple apple) -> Color.RED.equals(apple.getColor()) && apple.getWeight() >= 100);
// 某些情况下我们甚至可以省略参数类型,编译器会根据上下文正确判断
List<Apple> filterApples = filterApplesByAppleFilter(apples,
        apple -> Color.RED.equals(apple.getColor()) && apple.getWeight() >= 100);

局部变量

上面所有例子我们的lambda表达式都是使用其主体参数,我们也可以在lambda中使用局部变量,如下:

int weight = 100;
List<Apple> filterApples = filterApplesByAppleFilter(apples,
        apple -> Color.RED.equals(apple.getColor()) && apple.getWeight() >= weight);

该例子中我们在lambda中使用了局部变量weight,不过在lambda中使用局部变量必须要求该变量 显式声明为final或事实上的final ,这主要是因为局部变量存储在栈上,lambda表达式则在另一个线程中运行,当该线程视图访问该局部变量的时候,该变量存在被更改或回收的可能性,所以用final修饰之后就不会存在线程安全的问题。

四. 方法引用

采用方法引用可以更近一步的简化代码,有时候这种简化让代码看上去更加的直观,先看一个例子:

/* ... 省略apples的初始化操作 */
// 采用lambda表达式
apples.sort((Apple a, Apple b) -> Float.compare(a.getWeight(), b.getWeight()));
// 采用方法引用
apples.sort(Comparator.comparing(Apple::getWeight));

方法引用通过

::

将方法隶属和方法自身连接起来,主要分为三类:

静态方法

(args) -> ClassName.staticMethod(args)


转换成

ClassName::staticMethod


参数的实例方法

(args) -> args.instanceMethod()


转换成

ClassName::instanceMethod  // ClassName是args的类型


外部的实例方法

(args) -> ext.instanceMethod(args)


转换成

ext::instanceMethod(args)

 以上就是Java8 新特性之 Lambda 表达式 的内容,更多相关内容请关注PHP中文网(www.php.cn)!



陳述
本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發現涉嫌抄襲或侵權的內容,請聯絡admin@php.cn
Java 8中如何计算一年前或一年后的日期?Java 8中如何计算一年前或一年后的日期?Apr 26, 2023 am 09:22 AM

Java8计算一年前或一年后的日期利用minus()方法计算一年前的日期packagecom.shxt.demo02;importjava.time.LocalDate;importjava.time.temporal.ChronoUnit;publicclassDemo09{publicstaticvoidmain(String[]args){LocalDatetoday=LocalDate.now();LocalDatepreviousYear=today.minus(1,ChronoUni

如何使用Java 8计算一周后的日期?如何使用Java 8计算一周后的日期?Apr 21, 2023 pm 11:01 PM

Java8如何计算一周后的日期这个例子会计算一周后的日期。LocalDate日期不包含时间信息,它的plus()方法用来增加天、周、月,ChronoUnit类声明了这些时间单位。由于LocalDate也是不变类型,返回后一定要用变量赋值。packagecom.shxt.demo02;importjava.time.LocalDate;importjava.time.temporal.ChronoUnit;publicclassDemo08{publicstaticvoidmain(String[

PHP8.1引入的新的Redis扩展PHP8.1引入的新的Redis扩展Jul 07, 2023 pm 09:41 PM

PHP8.1引入的新的Redis扩展随着互联网的快速发展,大量的数据需要进行存储和处理。为了提高数据处理的效率和性能,缓存成为了一个不可或缺的部分。而在PHP开发中,Redis作为一种高性能的键值对存储系统,被广泛应用于缓存和数据存储的场景。为了进一步提升Redis在PHP中的使用体验,PHP8.1引入了新的Redis扩展,本文将介绍这一扩展的新增功能,并给

CSS3的新特性一览:如何使用CSS3实现过渡效果CSS3的新特性一览:如何使用CSS3实现过渡效果Sep 09, 2023 am 11:27 AM

CSS3的新特性一览:如何使用CSS3实现过渡效果CSS3作为CSS的最新版本,在众多新特性中,最有趣和实用的应该是过渡效果(transition)。过渡效果可以让我们的页面在交互时更加平滑、漂亮,给用户带来良好的视觉体验。本文将介绍CSS3过渡效果的基本用法,并附带相应的代码示例。transition-property属性:指定需要过渡的CSS属性过渡效果

go语言有什么新特性go语言有什么新特性Aug 24, 2023 pm 01:36 PM

go语言的新特性有:1、Go模块,用于管理Go语言项目的依赖关系;2、错误处理,增加了一个新的错误类型error,使得错误处理更加灵活和简洁;3、上下文包,用于在goroutine之间传递请求范围的值;4、嵌入,即一个结构体可以嵌入到另一个结构体中;5、同步包,更好地控制goroutine之间的同步和通信;6、错误值,更好地区分不同类型的错误;7、泛型,让开发者编写更灵活。

CSS3的新特性一览:如何使用CSS3实现水平居中布局CSS3的新特性一览:如何使用CSS3实现水平居中布局Sep 09, 2023 pm 04:09 PM

CSS3的新特性一览:如何使用CSS3实现水平居中布局在网页设计和布局中,水平居中布局是一项常见的需求。过去,我们经常使用复杂的JavaScript或CSS技巧实现此目的。然而,CSS3引入了一些新的特性,使得水平居中布局更加简单和灵活。本文将介绍一些CSS3的新特性,并提供一些代码示例,演示如何使用CSS3实现水平居中布局。一、使用flexbox布局fle

PHP8底层开发原理解密:利用新特性提高代码性能和可靠性PHP8底层开发原理解密:利用新特性提高代码性能和可靠性Sep 10, 2023 pm 04:25 PM

PHP8作为最新版本的PHP编程语言,引入了许多新的特性和改进,在底层开发原理方面做了一些重要的改变。本文将深入探讨PHP8的底层开发原理,并分析如何利用新特性提高代码性能和可靠性。首先,我们来了解一下PHP8的一些重要的底层开发原理。PHP8引入了JIT(Just-In-Time)编译器,这是一个动态编译器,可以将PHP代码即时转换为本地机器码,并在执行时

es6的新特性有哪些es6的新特性有哪些Aug 04, 2023 am 09:54 AM

es6的新特性有:1、块级作用域,可以在块级作用域中声明变量;2、箭头函数,一种新的函数声明方式;3、解构赋值,一种从数组或对象中提取值并赋值给变量的语;4、默认参数,允许在函数定义时为参数提供默认值;5、扩展运算符,可以将数组或对象展开,提取出其中的元素;6、模板字符串;7、类和模块;8、迭代器和生成器;9、Promise对象;10、模块化导入和导出等等。

See all articles

熱AI工具

Undresser.AI Undress

Undresser.AI Undress

人工智慧驅動的應用程序,用於創建逼真的裸體照片

AI Clothes Remover

AI Clothes Remover

用於從照片中去除衣服的線上人工智慧工具。

Undress AI Tool

Undress AI Tool

免費脫衣圖片

Clothoff.io

Clothoff.io

AI脫衣器

AI Hentai Generator

AI Hentai Generator

免費產生 AI 無盡。

熱門文章

R.E.P.O.能量晶體解釋及其做什麼(黃色晶體)
3 週前By尊渡假赌尊渡假赌尊渡假赌
R.E.P.O.最佳圖形設置
3 週前By尊渡假赌尊渡假赌尊渡假赌
R.E.P.O.如果您聽不到任何人,如何修復音頻
3 週前By尊渡假赌尊渡假赌尊渡假赌

熱工具

Atom編輯器mac版下載

Atom編輯器mac版下載

最受歡迎的的開源編輯器

Dreamweaver Mac版

Dreamweaver Mac版

視覺化網頁開發工具

Safe Exam Browser

Safe Exam Browser

Safe Exam Browser是一個安全的瀏覽器環境,安全地進行線上考試。該軟體將任何電腦變成一個安全的工作站。它控制對任何實用工具的訪問,並防止學生使用未經授權的資源。

DVWA

DVWA

Damn Vulnerable Web App (DVWA) 是一個PHP/MySQL的Web應用程序,非常容易受到攻擊。它的主要目標是成為安全專業人員在合法環境中測試自己的技能和工具的輔助工具,幫助Web開發人員更好地理解保護網路應用程式的過程,並幫助教師/學生在課堂環境中教授/學習Web應用程式安全性。 DVWA的目標是透過簡單直接的介面練習一些最常見的Web漏洞,難度各不相同。請注意,該軟體中

mPDF

mPDF

mPDF是一個PHP庫,可以從UTF-8編碼的HTML產生PDF檔案。原作者Ian Back編寫mPDF以從他的網站上「即時」輸出PDF文件,並處理不同的語言。與原始腳本如HTML2FPDF相比,它的速度較慢,並且在使用Unicode字體時產生的檔案較大,但支援CSS樣式等,並進行了大量增強。支援幾乎所有語言,包括RTL(阿拉伯語和希伯來語)和CJK(中日韓)。支援嵌套的區塊級元素(如P、DIV),