天天看點

Java常用的函數式程式設計

lambda表達式是函數式程式設計的核心,即隐式函數,沒有函數名,隻有函數體。

lambda表達式特點

(1)代碼簡單

(2) 對象不變性

lambda表達式的方法引用

(1)靜态方法:ClassName::methodName

(2)執行個體方法:instance::methodName

(3)超類的執行個體方法:super::methodName

(4)類型上的類型引用:ClassName::methodName

(5)構造方法引用:Class::new

(6)數組構造方法引用:TypeName[]::new

重點:通過@FunctionalInterface類型接口,定義構造函數:

class User{
    private int id;
    private String name;
    public User(int id, String name) {
        this.id = id;
        this.name = name;
    }
    public String getName() {
        return name;
    }
}
class ConstrMethodRef{
    // 通過 User 的工廠類 UserFactory(函數式接口),使用User::new建立接口執行個體時,
    // 系統會根據 UserFacroty.create() 的函數簽名選擇合适的 User 構造函數
    @FunctionalInterface
    interface UserFactory<U extends User>{
        // 函數式接口定義:隻有一個抽象方法;其它(靜态、預設)方法不做限制
        U create(int id, String name);
    }
    static UserFactory<User> userRef = User::new;
    public static void main(String[] args) {
        List<User> list = new ArrayList<>();
        for (int i = 0; i < 10; i++) {
            list.add(userRef.create(i, "xiao" + Integer.toString(i)));
        }
        list.stream().map(User::getName).forEach(System.out::print);
    }
}
           

第二次結果還是原數組,說明函數式程式設計的不變性(是以函數式并行程式設計不用考慮線程安全性問題,因為每個對象都是不變的);

int[] nums = {1, 2, 3, 4};
String[] strs = {"xsx", "csw", "dwfwfve", "efwe", "frwqweqa"};

// 1.将所有元素 +1,并列印;
Arrays.stream(nums).map((x) -> x = x + 1).forEach(System.out::print); // {2,3,4,5}

// 2.傳入的還是 nums,即使 上一條語句 執行過了,是以列印的還是原數組
Arrays.stream(nums).forEach(System.out :: print); // {1,2,3,4}

// 3.元素為奇數 則加1,列印
Arrays.stream(nums).map(x -> (x % 2 == 0 ? x : x + 1)).forEach(System.out::println);

// 4.排序時的比較器(對strs字元串數組排序,規則:先按字元串長度升序,再按大小寫不敏感的字母順序排序)
Arrays.sort(strs, Comparator.comparingInt(String::length).thenComparing(String.CASE_INSENSITIVE_ORDER));

// 5. 
 
           

流式程式設計的演變:

// 1.通過臨時執行個體化一個 Consumer 對象,重寫其中的方法,實作流式程式設計
Arrays.stream(strs).forEach(new Consumer<String>() {
    @Override
    public void accept(String s) {
        System.out.println(s);
    }
});

// 2.推斷Consumer接口,省略Consumer執行個體
Arrays.stream(strs).forEach((final String x) -> { System.out.println(x); });

// 3.省略 參數類型、final 和 大括号;
Arrays.stream(strs).forEach((x) ->  System.out.println(x));

// 4.可以使用方法引用的,直接使用方法引用
Arrays.stream(strs).forEach( System.out::println);

// 5. 使用
  // 通過 Consumer 定義兩個方法引用
  IntConsumer outPrint = System.out::println;
  IntConsumer errPrint = System.err::println;
  // 直接在stream中使用定義的方法引用
  Arrays.stream(nums).forEach(outPrint.andThen(errPrint));
           

并行流

// 1.擷取指定範圍内的質數總數
IntStream.range(1, 10000).parallel().filter(PrimeUtil::isPrime).count();

// 2.從集合得到并行流
double avg = list.parallelStream().mapToInt(s -> s.id).average().getAsDouble();

// 3.并行排序
Arrays.parallelSort(nums);

// 4.并行指派
Arrays.parallelSetAll(nums, (i) -> new Random().nextInt());
           

lambda表達式實作

lambda表達式的實作類似于匿名類的實作,隻是寫法上有所差別,通過編譯反編譯lambda表達式,可以清楚看到。

問題:如何編譯、反編譯、檢視lambda表達式的實作方式?