好程序員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,格式
只要確保接口中有且僅有一個抽象方法即可:
- 修飾符 interface 接口名稱 {
- public abstract 返回值類型 方法名稱(可選參數信息);
- }
1.3 @FunctionalInterface註解
與 @Override 註解的作用類似,Java 8中專門爲函數式接口引入了一個新的註解: @FunctionalInterface 。該註解可用於一個接口的定義上:
- @FunctionalInterface
- public interface MyFunctionalInterface {
- void myMethod();
- }
一旦使用該註解來定義接口,編譯器將會強制檢查該接口是否確實有且僅有一個抽象方法,否則將會報錯。需要注 意的是,即使不使用該註解,只要滿足函數式接口的定義,這仍然是一個函數式接口,使用起來都一樣.
2.函數式接口的使用
2.1函數式接口作爲參數,方法不帶參數
- //定義函數式接口
- public interface MyInterface{
- public abstract void show();
- }
- //使用(匿名內部類對象/函數式)
- public class Demo01 {
- public static void main(String[] args) {
- method01(new MyInterface01() {
- @Override
- public void show() {
- System.out.println("你好,函數式接口");
- }
- });
- // 函數式
- method01(() -> {
- System.out.println("你好,函數式接口");
- });
- // 函數式簡寫(如果方法體中只有一句代碼)
- method01(() -> System.out.println("你好,函數式接口"));
- }
- public static void method01(MyInterface01 inter) {
- inter.show();
- }
- }
函數式接口的優勢
函數式接口比匿名內部類對象產生更少的字節碼對象,提升java執行效率.
2.2, 函數式接口作爲參數,方法帶參數
- //定義函數式接口
- public interface MyInterface02 {
- public abstract void show(String msg1, String msg2);
- }
- //使用函數式接口
- public static void main(String[] args) {
- //匿名內部類對象
- method01(new MyInterface02() {
- @Override
- public void show(String msg1, String msg2) {
- System.out.println(msg1 + msg2);
- }
- });
- //函數式完整
- method01((String msg1, String msg2) -> {
- System.out.println(msg1 + msg2);
- });
- //函數式簡寫
- method01((msg1, msg2) -> System.out.println(msg1 + msg2));
- }
- public static void method01(MyInterface02 inter) {
- inter.show("hello", "函數式");
- }
2.3, 函數式接口作爲返回值,方法不帶參數
- //定義函數式接口
- public interface MyInterface02 {
- public abstract void show(String msg1, String msg2);
- }
- public static void main(String[] args) {
- getInter1().show("你好", "函數式");
- getInter2().show("你好", "函數式");
- }
- // 函數式完整
- public static MyInterface02 getInter1() {
- return (String msg1, String msg2) -> {
- System.out.println(msg1 + msg2);
- };
- }
- // 函數式簡寫
- public static MyInterface02 getInter2() {
- return (msg1, msg2) -> System.out.println(msg1 + msg2);
- }
3.函數式編程應用場景
3.1,概念
在兼顧面向對象特性的基礎上,Java語言通過Lambda表達式使用函數式接口,就叫做函數式編程
3.2, 使用lambada作爲參數
如果拋開實現原理不說,Java中的Lambda表達式可以被當作是匿名內部類的替代品。如果方法的參數是一個函數 式接口類型,那麼就可以使用Lambda表達式進行替代。
- public class Demo04Runnable{
- private static void startThread(Runnable task){
- new Thread(task).start();
- }
- public static void main(String[] args) {
- startThread(()‐>System.out.println("線程執行"));
- }
- }
3.3, 使用函數式接口作爲返回值
如果一個方法的返回值類型是一個函數式接口,那麼就可以直接返回一個Lambda表達式。
- public class Demo06Comparator {
- private static Comparator getComparator(){
- return (num1,num2)‐> num1 - num2;
- }
- public static void main(String[] args) {
- Integer[] array = {3,2,1};
- Arrays.sort(array, getComparator());
- //遍歷數組
- }
- }
3.4, 函數式接口的方法有返回值
- public static void main(String[] args) {
- showMsg(new MyInterface03() {
- @Override
- public String getMsg() {
- return "hello functional interface";
- }
- });
- // lambada表達式
- showMsg(() -> {
- return "hello1 functional interface";
- });
- // lambda表達式簡寫
- showMsg(() -> "hello1 functional interface");
- }
- public static void showMsg(MyInterface03 inter) {
- String msg = inter.getMsg();
- System.out.println(msg);
- }
4.常用函數式接口(Supplier接口)
JDK提供了大量常用的函數式接口以豐富Lambda的典型使用場景,它們主要在 java.util.function 包中被提供。 下面是簡單的幾個接口及使用示例。
4.1,Supplier接口
java.util.function.Supplier 接口僅包含一個無參的方法: T get() 。用來獲取一個泛型參數指定類型的對象數據。由於這是一個函數式接口,這也就意味着對應的Lambda表達式需要“對外提供”一個符合泛型類型的對象數據
4.2,基本使用
- private static String getString(Supplier function ){
- return function.get();
- }
- public static void main(String[] args){
- String msgA="Hello";
- String msgB="World";
- System.out.println(getString(()->msgA+msgB));
- }
4.2,綜合案例
需求:使用 Supplier 接口作爲方法參數類型,通過Lambda表達式求出int數組中的最大值。提示:接口的泛型請使用 java.lang.Integer 類。
- public static void main(String[] args) {
- Integer max = getMax(()->{
- Integer[] nums = {1,2,3,4};
- int max2 = nums[0];
- for (Integer num : nums) {
- if(max2 < num){
- max2 = num;
- }
- }
- return max2;
- });
- System.out.println(max);
- }
- public static Integer getMax(Supplier supplier){
- return supplier.get();
- }
5.常用函數式接口(Consumer接口)
5.1,Consumer接口
java.util.function.Consumer 接口則正好與Supplier接口相反,它不是生產一個數據,而是消費一個數據, 其數據類型由泛型決定
5.2,accept方法
Consumer 接口中包含抽象方法 void accept(T t) ,意爲消費一個指定泛型的數據。基本使用如:
- public static void main(String[] args) {
- consumeString((msg)->System.out.println(msg));
- }
- public static void consumeString(Consumer consumer){
- consumer.accept("hello");
- }
5.3, andThen方法
如果一個方法的參數和返回值全都是 Consumer 類型,那麼就可以實現效果:消費數據的時候,首先做一個操作, 然後再做一個操作,實現組合。而這個方法就是 Consumer 接口中的default方法 andThen
- default Consumer andThen(Consumer<? super T> after) {
- Objects.requireNonNull(after);
- return (T t) -> { accept(t); after.accept(t); };
- }
注: java.util.Objects 的 requireNonNull 靜態方法將會在參數爲null時主動拋出 NullPointerException 異常。這省去了重複編寫if語句和拋出空指針異常的麻煩。
需求:先打印大寫HELLO,再打印小寫hello
- public static void main(String[] args) {
- consumeString((msg) -> System.out.println(msg.toUpperCase()),
- (msg) -> System.out.println(msg.toLowerCase()));
- }
- public static void consumeString(Consumer consumer1, Consumer consumer2) {
- consumer1.andThen(consumer2).accept("hello");
- }
6.常用函數式接口(Predicate接口)
有時候我們需要對某種類型的數據進行判斷,從而得到一個boolean值結果。這時可以使用 java.util.function.Predicate 接口
6.1, test方法
Predicate 接口中包含一個抽象方法: boolean test(T t) 。用於條件判斷的場景
- public enum SingleClass06 {
- INSTANCE;
- }
6.2,基本使用
- public static void main(String[] args) {
- System.out.println(predicateTest((msg) -> msg.length() > 3, "hello"));
- }
- public static boolean predicateTest(Predicate predicate,String msg){
- return predicate.test(msg);
- }
7.總結
在本文中,我們學會了使用lambda表達式的不同方式,同時也學習了java8.0開始自帶的一些常用函數式接口。