搜尋
首頁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
JVM性能與其他語言JVM性能與其他語言May 14, 2025 am 12:16 AM

JVM'SperformanceIsCompetitiveWithOtherRuntimes,operingabalanceOfspeed,安全性和生產性。 1)JVMUSESJITCOMPILATIONFORDYNAMICOPTIMIZAIZATIONS.2)c提供NativePernativePerformanceButlanceButlactsjvm'ssafetyFeatures.3)

Java平台獨立性:使用示例Java平台獨立性:使用示例May 14, 2025 am 12:14 AM

JavaachievesPlatFormIndependencEthroughTheJavavIrtualMachine(JVM),允許CodeTorunonAnyPlatFormWithAjvm.1)codeisscompiledIntobytecode,notmachine-specificodificcode.2)bytecodeisisteredbytheybytheybytheybythejvm,enablingcross-platerssectectectectectross-eenablingcrossectectectectectection.2)

JVM架構:深入研究Java虛擬機JVM架構:深入研究Java虛擬機May 14, 2025 am 12:12 AM

TheJVMisanabstractcomputingmachinecrucialforrunningJavaprogramsduetoitsplatform-independentarchitecture.Itincludes:1)ClassLoaderforloadingclasses,2)RuntimeDataAreafordatastorage,3)ExecutionEnginewithInterpreter,JITCompiler,andGarbageCollectorforbytec

JVM:JVM與操作系統有關嗎?JVM:JVM與操作系統有關嗎?May 14, 2025 am 12:11 AM

JVMhasacloserelationshipwiththeOSasittranslatesJavabytecodeintomachine-specificinstructions,managesmemory,andhandlesgarbagecollection.ThisrelationshipallowsJavatorunonvariousOSenvironments,butitalsopresentschallengeslikedifferentJVMbehaviorsandOS-spe

Java:寫一次,在任何地方跑步(WORA) - 深入了解平台獨立性Java:寫一次,在任何地方跑步(WORA) - 深入了解平台獨立性May 14, 2025 am 12:05 AM

Java實現“一次編寫,到處運行”通過編譯成字節碼並在Java虛擬機(JVM)上運行。 1)編寫Java代碼並編譯成字節碼。 2)字節碼在任何安裝了JVM的平台上運行。 3)使用Java原生接口(JNI)處理平台特定功能。儘管存在挑戰,如JVM一致性和平台特定庫的使用,但WORA大大提高了開發效率和部署靈活性。

Java平台獨立性:與不同的操作系統的兼容性Java平台獨立性:與不同的操作系統的兼容性May 13, 2025 am 12:11 AM

JavaachievesPlatFormIndependencethroughTheJavavIrtualMachine(JVM),允許Codetorunondifferentoperatingsystemsswithoutmodification.thejvmcompilesjavacodeintoplatform-interploplatform-interpectentbybyteentbytybyteentbybytecode,whatittheninternterninterpretsandectectececutesoneonthepecificos,atrafficteyos,Afferctinginginginginginginginginginginginginginginginginginginginginginginginginginginginginginginginginginginginginginginginginginginging

什麼功能使Java仍然強大什麼功能使Java仍然強大May 13, 2025 am 12:05 AM

JavaispoperfulduetoitsplatFormitiondence,對象與偏見,RichstandardLibrary,PerformanceCapabilities和StrongsecurityFeatures.1)Platform-dimplighandependectionceallowsenceallowsenceallowsenceallowsencationSapplicationStornanyDevicesupportingJava.2)

頂級Java功能:開發人員的綜合指南頂級Java功能:開發人員的綜合指南May 13, 2025 am 12:04 AM

Java的頂級功能包括:1)面向對象編程,支持多態性,提升代碼的靈活性和可維護性;2)異常處理機制,通過try-catch-finally塊提高代碼的魯棒性;3)垃圾回收,簡化內存管理;4)泛型,增強類型安全性;5)ambda表達式和函數式編程,使代碼更簡潔和表達性強;6)豐富的標準庫,提供優化過的數據結構和算法。

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脫衣器

Video Face Swap

Video Face Swap

使用我們完全免費的人工智慧換臉工具,輕鬆在任何影片中換臉!

熱門文章

熱工具

SublimeText3 Mac版

SublimeText3 Mac版

神級程式碼編輯軟體(SublimeText3)

MantisBT

MantisBT

Mantis是一個易於部署的基於Web的缺陷追蹤工具,用於幫助產品缺陷追蹤。它需要PHP、MySQL和一個Web伺服器。請查看我們的演示和託管服務。

Dreamweaver CS6

Dreamweaver CS6

視覺化網頁開發工具

ZendStudio 13.5.1 Mac

ZendStudio 13.5.1 Mac

強大的PHP整合開發環境

SublimeText3漢化版

SublimeText3漢化版

中文版,非常好用