行爲參數化與lambda表達式 - 讀《Java 8實戰》

零、 概述

第一部分:1~3章 主要講了行爲參數化和Lambda表達式java

第二部分:4~7章 主要講了流的應用,包括流與集合差別,流的操做,收集器,注的並行執行git

第三部分:8~12章 主要講了怎樣用Java8引入的特性改善老代碼,Optional類和CompleteFuture及新的日期和時間API編程

第四部分:13~16章 主要講了函數式編程app

本文主要是對第一部分的筆記。ide

1、行爲參數化

1.1 行爲參數化定義

行爲參數化就是拿出一個代碼塊,把它準備好卻不去執行它。函數式編程

1.2 舉例:需求

有個果農,有以下需求:函數

  1. 從倉庫中找到紅色蘋果
  2. 找出全部重量超過150g的

擴展一下:設計

  1. 可能之後還須要查找綠色蘋果的功能
  2. 可能還須要找重量超過200g的

1.3 舉例:方案1

傳統實現方案code

// 篩選綠色蘋果
public static List<Apple> filterGreenApples(List<Apple> inventory) {
    List<Apple> result = new ArrayList<>();
    for (Apple apple : inventory) {
        if ("green".equals(apple.getColor())) {
            result.add(apple);
        }
    }
    return result;
}

// 可篩選任意顏色蘋果,把顏色做爲參數
public static List<Apple> filterGreenApplesByColor(List<Apple> inventory, String color) {
    List<Apple> result = new ArrayList<>();
    for (Apple apple : inventory) {
        if (apple.getColor().equals(apple.getColor())) {
            result.add(apple);
        }
    }
    return result;
}
// 篩選不一樣重量的蘋果
public static List<Apple> filterGreenApplesByWeight(List<Apple> inventory, int weight) {
    List<Apple> result = new ArrayList<>();
    for (Apple apple : inventory) {
        if (apple.getWeight() > weight) {
            result.add(apple);
        }
    }
    return result;
}

// 寫一個方法同時支持篩選顏色和重量
public static List<Apple> filterGreenApples(List<Apple> inventory, String color, int weight
            , boolean filterColorFlag) {
	List<Apple> result = new ArrayList<>();
    for (Apple apple : inventory) {
        if ((filterColorFlag && apple.getColor().equals(color))
                || (!filterColorFlag && apple.getWeight() > weight)) {
            result.add(apple);
        }
    }
	return result;
}

1.4 舉例:方案2

使用對象傳遞行爲參數對象

interface ApplePredicate {
    // 一個返回boolea值的函數,把它稱爲謂詞
    boolean test(Apple apple);
}

// 篩選綠色
public class AppleGreenColorPredicate implements ApplePredicate {
    @Override
    public boolean test(Apple apple) {
        return "green".equals(apple.getColor());
    }
}

// 重量大於150
class AppleHeavyWeightPredicate implements ApplePredicate {
    @Override
    public boolean test(Apple apple) {
        return apple.getWeight() > 150;
    }
}

// 紅色且重量大於150
class AppleRedAndHeavyPredicate implements ApplePredicate {
    @Override
    public boolean test(Apple apple) {
        return "red".equals(apple.getColor()) && apple.getWeight() > 150;
    }
}

// 實現
public static List<Apple> filterApples(List<Apple> inventory, ApplePredicate p) {
    List<Apple> result = new ArrayList<>();
    for (Apple apple : inventory) {
        if (p.test(apple)) {
            result.add(apple);
        }
    }
    return result;
}

public void test() {
    List<Apple> inventory = new ArrayList<>();
    // 篩選綠色
    filterApples(inventory, new AppleGreenColorPredicate());

    // 重量大於150
    filterApples(inventory, new AppleHeavyWeightPredicate());

    // 紅色且重量大於150
    filterApples(inventory, new AppleRedAndHeavyPredicate());
}

1.5 舉例:方案3

使用匿名類傳遞行爲參數

// 對選擇標準建模
interface ApplePredicate {
    // 一個返回boolea值的函數,把它稱爲謂詞
    boolean test(Apple apple);
}

// 實現
public static List<Apple> filterApples(List<Apple> inventory, ApplePredicate p) {
    List<Apple> result = new ArrayList<>();
    for (Apple apple : inventory) {
        if (p.test(apple)) {
            result.add(apple);
        }
    }
    return result;
}

public static void main(String[] args) {
    List<Apple> inventory = new ArrayList<>();
    // 篩選綠色
    filterApples(inventory, new ApplePredicate() {
        @Override
        public boolean test (Apple apple){
            return "green".equals(apple.getColor());
        }
    });

    // 重量大於150
    filterApples(inventory, new ApplePredicate() {
        @Override
        public boolean test (Apple apple){
            return apple.getWeight() > 150;
        }
    });

    // 紅色且重量大於150
    filterApples(inventory, new ApplePredicate() {
        @Override
        public boolean test (Apple apple){
            return "red".equals(apple.getColor()) && apple.getWeight() > 150;
        }
    });
}

1.6 舉例:方案4

使用Lambda表達式傳遞行爲參數

interface ApplePredicate {
    boolean test(Apple apple);
}

public static List<Apple> filterApples(List<Apple> inventory, ApplePredicate p) {
    List<Apple> result = new ArrayList<>();
    for (Apple apple : inventory) {
        if (p.test(apple)) {
            result.add(apple);
        }
    }
    return result;
}

public static void main(String[] args) {
    List<Apple> inventory = new ArrayList<>();
    // 篩選綠色
    filterApples(inventory
		, (Apple apple) -> "green".equals(apple.getColor()));

    // 重量大於150
    filterApples(inventory
		, (Apple apple) -> apple.getWeight() > 150);

    // 紅色且重量大於150
    filterApples(inventory
		, (Apple apple) -> "red".equals(apple.getColor()) && apple.getWeight() > 150);
}

在這裏小結一下:

2018-08-11_041102.png

1.7 舉例:方案5

在方案4的基礎上 將List類型抽象化

// 定義一個函數式接口
interface Predicate<T> {
    boolean test(T t);
}

// 定義一個調用函數式接口的方法
public static <T> List<T> filter(List<T> list, Predicate<T> p) {
    List<T> result = new ArrayList<>();
    for (T e : list) {
        if (p.test(e)) {
            result.add(e);
        }
    }
    return result;
}

// 使用
public static void main(String[] args) {
    List<Apple> inventory = FakeDb.getApples();
    List<Apple> redList = Filtering.filter(inventory
		, (Apple apple) -> "red".equals(apple.getColor()));

    List<String> nonEmptyList = filter(Arrays.asList("1", "", "2")
		, (String s) -> !s.isEmpty());
}

2、Lambda表達式

2.1 Lambda表達式定義

簡潔地表示可傳遞的匿名函數的一種方法。

  • 匿名
  • 函數
  • 傳遞
  • 簡潔

2.2 Lambda表達式長什麼樣子?

下面是5個有效的Lambda表達式

// 1 參數是String s,返回值是int
(String s) -> s.length()

// 2 參數是Apple a,返回值是boolean
(Apple a) -> a.getWeight() > 150

//  3 參數是int x,int y 沒有返回值 {}內放語句,怎樣區分語句與表達式
(int x, int y) -> {
    System.out.println("Result:");
    System.out.println(x + y);
}

// 4 無參數,返回int
() -> 42

// 5 參數是兩個Apple類型的變量,返回值是boolean
(Apple a1, Apple a2) -> a1.getWeight().compareTo(a2.getWeight())

2.3 函數式接口

函數式接口就是隻定義一個抽象方法的接口。

函數式接口的抽象方法的簽名基本上就是Lambda表達式的簽名,這種抽象方法叫作函數描述符

一個註解:@FunctionalInterface ,不是必須的,用於表示該接口會設計成一個函數式接口

2.4 Lambda的使用

Predicate 過濾掉列表中的空串

// 定義一個函數式接口
interface Predicate<T> {
    boolean test(T t);
}

// 定義一個調用函數式接口的方法
public static <T> List<T> filter(List<T> list, Predicate<T> p) {
    List<T> result = new ArrayList<>();
    for (T e : list) {
        if (p.test(e)) {
            result.add(e);
        }
    }
    return result;
}

// 使用
public static void main(String[] args) {
    List<String> nonEmptyList = filter(Arrays.asList("1", "", "2")
		, (String s) -> !s.isEmpty());
}

Consumer 計算列表中的每一個元素的平方並輸出

@FunctionalInterface
public interface Consumer<T> {
    void accept(T t);
}

public static <T> void forEach(List<T> list, Consumer<T> c) {
    for (T i : list) {
        c.accept(i);
    }
}

public static void main(String[] args) {
    forEach(Arrays.asList(1, 2, 3, 4), (Integer i) -> System.out.println(i * i));
}

Function 返回列表中每一個元素的長度

@FunctionalInterface
public interface Function<T, R> {
    R apply(T t);
}

public static <T, R> List<R> map(List<T> list, Function<T, R> f) {
    List<R> result = new ArrayList<>();
    for (T s : list) {
        result.add(f.apply(s));
    }
    return result;
}

public static void main(String[] args) {
    List<Integer> result = map(Arrays.asList("1", "22", "333")
		, (String s) -> s.length());
}

2.5 類型推斷

List<Apple> l = new ArrayList<Apple>();
List<Apple> l = new ArrayList<>();

// Java編譯器根據Lambda出現的上下文來推斷Lambda表達式參數的類型
Predicate<Apple> p = (Apple a) -> 'red'.equals(a.getColor())
Predicate<Apple> p = a -> 'red'.equals(a.getColor())

2.6 方法引用

主要爲了簡化代碼

方法引用,3種

  • 指向靜態方法的方法引用 String.parseInt()
  • 指向實例方法的方法引用 str.length()
  • 指向外部對象實例方法的方法引用 globalVar.instanceMethod()
List<String> strList = Arrays.asList("a", "b", "A", "B");
strList.sort((s1, s2) -> s1.compareToIgnoreCase(s2));
strList.sort(String::compareToIgnoreCase); // 等效的方法引用

構造函數引用

Supplier<Apple> c1 = Apple::new; // 指向Apply()構造函數
Apple a1 = c1.get();

Function<Integer, Apple> c2 = Apple::new; // 指向Apply(int weight)構造函數
Apple a2 = c2.apply(110);

BigFunction<String, Integer, Apple> c3 = Apple::new;
// 指向Apply(String color, Integer weight)
Apple c3 = c3.apply("green", 110);

2.7 Lambda實戰

根據Apple的重量來排序

// 行爲參數化,下面是經過不一樣方式傳遞這個行爲的
// 1.使用對象
public class AppleComparator implements Comparator<Apple> {
    public int compare(Apple a1, Apple a2) {
        return a1.getWeight().compareTo(a2.getWeight());
    }
}
inventory.sort(new AppleComparator());

// 2.使用匿名類
inventory.sort(new Comparator<Apple>(){
    public int compare(Apple a1, Apple a2) {
        return a1.getWeight().compareTo(a2.getWeight());
    }
});
    
// 3.使用Lambda表達式
inventory.sort((Apple a1, Apple a2) -> a1.getWeight().compareTo(a2.getWeight()));
// 由於類型推斷,能夠簡化成
inventory.sort((a1, a2) -> a1.getWeight().compareTo(a2.getWeight()));
// 由於有個java.util.Comparator.comparing靜態方法,還能夠簡化成
import static java.util.Comparator.comparing;
inventory.sort(comparing((a) -> a.getWeight()));

// 4.使用方法引用
inventory.sort(comparing(comparing(Apple::getWeight)));

2.8 複合Lambda表達式

比較器複合

// 逆序,蘋果按重量遞減排序
inventory.sort(comparing(Apple::getWeight).reversed());

// 比較器鏈,先按重量遞減排序再按國家排序
inverntory.sort(comparing(Apple::getWeight).reversed()
               .thenComparing(Apple::getCountry));

謂詞複合

// negate,and,or
// 篩選不是紅蘋果
Predicate<Apple> notRedApple = redApple.negate();
// 篩選紅蘋果且重量大於150   或  綠蘋果
redApple.and(a -> a.getWeight() > 150).or(a -> "green".equals(a.getColor()));

// a.or(b).and(c) <==> (a || b) && c

函數複合

// andThen,compose

Function<Integer, Integer> f = x -> x + 1;
Function<Integer, Integer> g = x -> x * 2;

// g(f(x))
Function<Integer, Integer> h = f.andThen(g);
int result = h.apply(1);

// f(g(x))
Function<Integer, Integer> h = f.compose(g);
int result = h.apply(1);

3、代碼

https://gitee.com/yysue/tutorials-java/tree/master/java-8

相關文章
相關標籤/搜索