函式式介面@FunctionalInterface學習(函式程式設計思想)------------與匿名內部類對比
阿新 • • 發佈:2018-12-05
在java8中
1、新推出了一個介面概念:函式式介面。
2、允許介面也可以有default方法,default方法可以方法體。
他滿足以下規範:
- 介面有且只能有個一個抽象方法(抽象方法只有方法定義,沒有方法體)
- 不能在介面中覆寫Object類中的public方法(寫了編譯器也會報錯)
- 允許有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方法返回返回值 } }