1. 程式人生 > >函式式介面@FunctionalInterface學習(函式程式設計思想)------------與匿名內部類對比

函式式介面@FunctionalInterface學習(函式程式設計思想)------------與匿名內部類對比

 在java8中

1、新推出了一個介面概念:函式式介面。

2、允許介面也可以有default方法,default方法可以方法體。

他滿足以下規範:

  1. 介面有且只能有個一個抽象方法(抽象方法只有方法定義,沒有方法體)
  2. 不能在介面中覆寫Object類中的public方法(寫了編譯器也會報錯)
  3. 允許有default實現方法。

如下例子是函式式介面:

package zzu.zwl.main;

/**
 * Created by zwl on 2018/11/30.
 * May god bless me
 */
@FunctionalInterface
public interface GreetingService {
    String sayMessage(String message);
    default void doSomeMoreWork1()
    {
        // Method body
    }

    default void doSomeMoreWork2()
    {
        // Method body
    }
    static void printHello(){
        System.out.println("Hello");
    }
    @Override
    String toString();  //Object中的方法,但不重寫
}

而@FunctionalInterface是幹什麼的?

   當你新增上這個註解後,僅僅是檢查是否符合函式式介面規範,而不具有其他作用,因此jdk8之前常用的匿名內部類監聽器類也可以使用lambda程式設計,如下

//ActionListener的Java原始碼如下,他沒有使用@FunctionalInterface註解

/*
 * Copyright (c) 1996, 2013, Oracle and/or its affiliates. All rights reserved.
 * ORACLE PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
 */

package java.awt.event;

import java.util.EventListener;

/**
 * The listener interface for receiving action events.
 * The class that is interested in processing an action event
 * implements this interface, and the object created with that
 * class is registered with a component, using the component's
 * {@code addActionListener} method. When the action event
 * occurs, that object's {@code actionPerformed} method is
 * invoked.
 *
 * @see ActionEvent
 * @see <a href="http://docs.oracle.com/javase/tutorial/uiswing/events/actionlistener.html">How to Write an Action Listener</a>
 *
 * @author Carl Quinn
 * @since 1.1
 */
public interface ActionListener extends EventListener {

    /**
     * Invoked when an action occurs.
     * 只有一個未定義方法,符合函式式介面規範。
     * @param e the event to be processed
     */
    public void actionPerformed(ActionEvent e);  


}
/**
 *下面是例子
 *  
 **/

package zzu.zwl.main;


import javax.swing.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;


/**
 * Created by zwl on 2018/11/30.
 * May god bless me
 */
public class Main {
    private static JButton jButton;

    public static void main(String[] args) {

      jButton.addActionListener(new ActionListener() {
          @Override
          public void actionPerformed(ActionEvent e) {
              System.out.println(e.paramString());
          }
      });
        //也可以用lambda表示式程式設計,只關注輸入和輸出,雖然很 actionPerformed的輸出為void型別。
        jButton.addActionListener(e -> System.out.println(e.paramString()));
  }
}

明白了以上程式碼,接下來是函數語言程式設計與匿名內部類的對比程式設計。

package zzu.zwl.main;


import javax.swing.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;


/**
 * Created by zwl on 2018/11/30.
 * May god bless me
 */
public class Main {
    private static JButton jButton;

    public static void main(String[] args) {

      jButton.addActionListener(new ActionListener() {
          @Override
          public void actionPerformed(ActionEvent e) {
              System.out.println(e.paramString());
          }
      });
        jButton.addActionListener(e -> System.out.println(e.paramString()));

        /**
         * 為了更好理解FunctionInterface,暫且拋開lambda不看
         * 這裡不過是傳統的兩個步驟
         * 1、定義了一個方法,兩個引數,傳入一個字串,呼叫介面的方法
         *
         *  這裡相當於,先呼叫joinStr方法,傳入'你好'和一個匿名內部類(匿名內部類實現了getMessage方法)
         *  程式碼執行順序為先進入joinStr方法,然後執行方法體
         *  方法體是呼叫介面的sayMessage方法(傳入的是這裡的str '你好'),
         *  因此程式碼執行順序為
         *  jdk8之前---1
         *  jdk8之前---2
         *  jdk8之前---3
         *  jdk8之前---4
         *  jdk8之前---5
         */
       String  msg= joinStr("你好", new GreetingService() {   //  jdk8之前---1
            @Override
            public String sayMessage(String message) {  //實現介面的方法。
                System.out.println("匿名介面"); //  jdk8之前---3
                return  message;   //  jdk8之前---4
            }
        });
        /**
         * 列印
         */
        System.out.println(msg);   //  jdk8之前---7


        /**
         * 1.接收一個message,執行一部分動作,然後如果FunctionInterface有返回值,則返回一個對應的返回值。
         *  這裡用jdk8以前理解方式是 定義一個匿名內部類,相當於如下形式,所以程式碼不會執行

         GreetingService greetingService = new GreetingService() {
                @Override
                public String sayMessage(String message) {
                System.out.println(message);
                return message + "i";
        }
        };
         *
         */
        GreetingService greetingService1 = message -> {
            System.out.println(message);
            return message + "i";
        };


        /**
         *  2.本質上與1相似,但是此處是方法引用 ,相當於JDK8之前的如下形式
         *     GreetingService greetingService2 = new GreetingService() {
                @Override
                public String sayMessage(String message) {
                return Main.getInstance(message);
                }
        };
         *
         */

        GreetingService greetingService2 = Main::getInstance;
        /**
         * 這裡傳來傳去,實際上就是"你好"-->getInstance:item了,只不過中間規範化的定義一個介面,不同的是以前用匿名內部類實現,
         * 現在不關注類,而只關注抽象方法,介面相當於約束了傳入引數和返回引數,從而使用lambda表示式來書寫,只關注計算過程的輸入和輸出,有一點點函式程式設計的思想。
         */
        String msg1= joinStr("你好",greetingService2);
        System.out.println(msg1);

        //3與上述過程2一致。
        GreetingService greetingService2_1 = message -> Main.getInstance(message);
        //4與上述過程2、3一致。
        GreetingService greetingService2_2 = message -> {
            return Main.getInstance(message);
        };


        String msg1_1  = joinStr("你好",greetingService2_1);
        System.out.println(msg1_1);
        String msg1_2  = joinStr("你好",greetingService2_2);
        System.out.println(msg1_2);

        System.out.println("****************************華麗分割線**********************");
        /**
         * 上面看著是不是很雞肋,下面的例子實踐後會很方便。
         */
        //這樣是不是簡便很多,不用定義這個匿名內部類,直接關注輸入(傳入引數),輸出(返回值)
        String msg3 = joinStr("這個值傳給旁邊的message:",message -> message+"Hello,World");
        //實現具體方法的時候用多行程式碼。 為了複習,下面的程式碼再用JDK8之前的重寫一遍。
        String msg4 = joinStr("這個值傳給旁邊的message",message -> {
            System.out.println("你好呀,函數語言程式設計。");
            return message+"Hello,World";
        });
        String BeforeJdk8_msg4=joinStr("這個值傳給旁邊的message:", new GreetingService() {
            @Override
            public String sayMessage(String message) {
                System.out.println("你好呀,函數語言程式設計。");
                return message+"Hello,World";
            }
        });
        System.out.println(msg3);
        System.out.println(msg4);
        System.out.println(BeforeJdk8_msg4);

    }

    /**
     * 簡單方法
     * @param item
     * @return
     */
    public static String getInstance(String item) {
        return item + "!世界";
    }

    /**
     * 簡單方法
     * @param massage
     * @return
     */
    public static String getMessage(String massage){
        return "世界,"+ massage+"!";
    }

    /**
     * 定義一個方法,兩個引數
     * @param str  字串型別
     * @param greetingService  一個傳介面,呼叫接口裡的一個方法sayMessage,很常見的方法引數型別
     * @return
     */
    public  static String joinStr(String str,GreetingService greetingService) {

        String s = greetingService.sayMessage(str); //  jdk8之前--2(進入sayMessage方法)   jdk8之前--5(sayMessage方法返回)
        return  s;  //jdk8之前---6  joinStr方法返回返回值
    }
}

OK,特別是使用SpringBootFlux程式設計時,集合流配上lambda外加函數語言程式設計思想,好用的不得了。