1. 程式人生 > >[書]java8函數語言程式設計(1)

[書]java8函數語言程式設計(1)

一:Learning

package testLambda;

import java.awt.Button;
import java.awt.event.ActionEvent;
import java.util.EventListener;
import java.util.function.BinaryOperator;
import org.junit.Test;

/**
 * @author zhangdi
 * @description Lambda
 */
public class LambdaChapter1and2 {
    public static void main(String[] args) {

    }

    /**
     * 2.1 辨別Lambda表示式
     */
@SuppressWarnings("unused") @Test public static void recognizeLambda() { // 1.不包含引數,使用()表示,沒有引數,該Lamdba表示式實現了Runnable介面,該介面也只有一個run方法,沒有引數,返回型別為void Runnable NoArguments = () -> System.out.println("hello world"); // 2.該Lamdba表示式包含且只包含一個引數可省略引數的括號 ActionListener oneArgument = (event) -> System.out.println("button clicked"
); ActionListener oneArgument2 = event -> System.out.println("button clicked"); // 3.可以使用{}將lamdba表示式的主體括起來;只有一行程式碼的lambda的表示式也可以使用大括號;用來明確表示式的開始與結束 Runnable multistatement = () -> { System.out.println("hello"); System.out.println("world"); }; // 4.lambda表示式也可以是包含多個引數的方法;
BinaryOperator<Long> add = (x, y) -> x + y; BinaryOperator<Long> addExplicit = (Long x, Long y) -> x + y; } /** * 2.2 函式介面是隻有一個抽象方法的介面,用作lambda表示式的型別 */ public static void 函式介面() { } /** * 2.3 引用值,而不是變數 * 既成事實上的 final 是指只能給該變數賦值一次。 換句話說, Lambda 表示式引用的是值,而不是變數 * Lambda 表示式中引用既成事實上的 final 變數 -->Lambda 表示式都是靜態型別 */ public static void valueReference() { Button button = new Button(); String name = getUserName(); button.addActionListener(event -> System.out.println("hi " + name)); } private static String getUserName() { // TODO Auto-generated method stub return "test"; } /** * 2.4 ActionListener 介面: 接受 ActionEvent 型別的引數, 返回空 * * ActionListener 只有一個抽象方法: actionPerformed, 被用來表示行為: 接受一個引數, 返回空。 * 記住, 由於 actionPerformed 定義在一個接口裡, 因此 abstract 關鍵字不是必需 的。 * 該介面也繼承自一個不具有任何方法的父介面: EventListener。 */ public interface ActionListener extends EventListener { public void actionPerformed(ActionEvent event); } /** *java中重要的函式介面 * 介面 引數 返回型別 示例 * Predicate<T> T boolean 這張唱片已經發行了嗎 * Consumer<T> T void 輸出一個值 * Function<T,R> T R 獲得 Artist 物件的名字 * Supplier<T> None T 工廠方法 * UnaryOperator<T> T T 邏輯非( !) * BinaryOperator<T> (T, T) T 求兩個數的乘積( *) * * 2.5型別推斷 * Predicate 用來判斷真假的函式介面 */ public static void 型別推斷() { Predicate<Integer> atLeast5 = x -> x > 5; } //Predicate 介面的原始碼, 接受一個物件, 返回一個布林值 public interface Predicate<T> { boolean test(T t); } //略顯複雜的型別推斷 :型別推斷系統相當智慧, 但若資訊不夠, 型別推斷系統也無能為力。 型別系統不會漫無邊 //際地瞎猜, 而會中止操作並報告編譯錯誤, 尋求幫助 ,如去掉Long,程式碼不會通過編譯 BinaryOperator<Long> addLongs = (x, y) -> x + y; //沒有泛型, 程式碼則通不過編譯 }

java中重要的函式介面:
這裡寫圖片描述
總結:
* Lambda 表示式是一個匿名方法, 將行為像資料一樣進行傳遞。
* Lambda 表示式的常見結構: BinaryOperator add = (x, y) → x + y。
* Lambda表示式裡引用的到的變數是final的,本質是值,不是變數。
* Lambda表示式的型別是個函式介面,即僅有一個抽象方法的介面。( 函式介面指僅具有單個抽象方法的介面, 用來表示 Lambda 表示式的型別。)

二: 練習
1. 請看例 2-15 中的 Function 函式介面並回答下列問題。
例 2-15 Function 函式介面

    public interface Function<T, R> {
        R apply(T t);
    }
a. 請畫出該函式介面的圖示。
b. 若要編寫一個計算器程式, 你會使用該介面表示什麼樣的 Lambda 表示式?
c. 下列哪些 Lambda 表示式有效實現了 Function<Long,Long> ?
    x -> x + 1;
    (x, y) -> x + 1;
    x -> x == 1;
2. ThreadLocal Lambda 表示式。 Java 有一個 ThreadLocal 類, 作為容器儲存了當前執行緒裡
區域性變數的值。 Java 8 為該類新加了一個工廠方法, 接受一個 Lambda 表示式, 併產生
一個新的 ThreadLocal 物件, 而不用使用繼承, 語法上更加簡潔。
a. 在 Javadoc 或整合開發環境( IDE) 裡找出該方法。
b. DateFormatter 類是非執行緒安全的。 使用建構函式建立一個執行緒安全的 DateFormatter
物件, 並輸出日期, 如“ 01-Jan-1970”。

3. 型別推斷規則。 下面是將 Lambda 表示式作為引數傳遞給函式的一些例子。 javac 能正
確推斷出 Lambda 表示式中引數的型別嗎? 換句話說, 程式能編譯嗎?
a. Runnable helloWorld = () -> System.out.println("hello world");
b. 使用 Lambda 表示式實現 ActionListener 介面:
    JButton button = new JButton();
    button.addActionListener(event ->
    System.out.println(event.getActionCommand()));
c. 以如下方式過載 check 方法後, 還能正確推斷出 check(x -> x > 5) 的型別嗎?
    interface IntPred {
    boolean test(Integer value); 
    }
    boolean check(Predicate<Integer> predicate);
    boolean check(IntPred predicate);

練習答案:


public class Chapter1And2_practice {

    public static class Question1 {
        //x -> x + 1;
    }

    public static class Question3 {
        //a.yes
        //b.yes
        //c.no
    }

    /**
     * @author NSNP736
     * @description Question2
     */
    public static class Question2 {
        //lamada-by richard
        public final static ThreadLocal<DateFormatter> formatter = ThreadLocal.withInitial(() -> new DateFormatter(new SimpleDateFormat("dd-MMM-yyyy")));
        //Anonymous Inner Class -byzhangdi
        public final static ThreadLocal<DateFormatter> formatter2 = ThreadLocal.withInitial( new Supplier<DateFormatter>(){

            @Override
            public DateFormatter get() {
                DateFormatter dateFormatter = new DateFormatter(new SimpleDateFormat("dd-MMM-yyyy"));
                return dateFormatter;
            }});
    }
    @Test
    public void exampleInB() {
        Calendar cal = Calendar.getInstance();
        cal.set(Calendar.YEAR, 1970);
        cal.set(Calendar.MONTH, Calendar.JANUARY);
        cal.set(Calendar.DAY_OF_MONTH, 1);
        //formatter
        String format1 = Question2.formatter.get().getFormat().format(cal.getTime());
        //formatter2
        ThreadLocal<DateFormatter> formatter = Question2.formatter;
        DateFormatter dateFormatter = formatter.get();
        Format format = dateFormatter.getFormat();
        String format2 = format.format(cal.getTime());

        assertEquals("01-一月-1970", format1);
        assertEquals("01-一月-1970", format2);
    }
}