JAVA高级(一)——lambda
JAVA高級(一)——lambda
- lambda基礎
- 1、是什么是函數(shù)是接口?
- 2、lambda的特點
- 3、在哪里以及如何使用Lambda
- lambda實現(xiàn):環(huán)繞執(zhí)行模式
- 1、行為參數(shù)化
- 2、使用函數(shù)時接口傳遞行為
- 3、執(zhí)行一個行為并轉為lambda
- 使用函數(shù)式接口
- 1、常用的函數(shù)型接口
- 2、重構使用lambda的問題
- 2.1 重構lambda找不到情況
- 2.2 從lambda表達式到方法引用的轉換
- 3、使用lambda重構設計模式
- 3.1 重構策略模式
- 3.2 模板方法
lambda基礎
1、是什么是函數(shù)是接口?
函數(shù)式接口(Functional Interface)就是一個有且僅有一個抽象方法,但是可以有多個非抽象方法的接口。
? 上圖就是一個函數(shù)是接口樣式;
2、lambda的特點
- 匿名————沒有明確的名稱:寫的少想的多(就是不直觀);
- 函數(shù)————它不像普通的方法有特定的類。但是方法一樣,lambda有方法一切的特性;
- 傳遞————lambda表達式可以作為參數(shù)傳遞給方法或者存儲在變量中;
- 簡潔————無需使用匿名類,可以省掉很多模板;
下面是一個使用了lambda簡化的代碼模板:
public void test03(){// 原來Consumer<Person> consumer = new Consumer<Person>() {@Overridepublic void accept(Person person) {System.out.println(person);}};// now 1Consumer<Person> consumer1 = person -> System.out.println(person);// now 2Consumer<Person> consumer2 = System.out::println;}3、在哪里以及如何使用Lambda
當接口是函數(shù)式接口時,可以使用;
lambda實現(xiàn):環(huán)繞執(zhí)行模式
這里我將述說如何自定義一個template去實現(xiàn)我們自己想要的函數(shù)格式;
代碼樣例:
// 這一行代碼只能閱讀固定的文件public String BufferReadProcessor1() throws IOException {try(BufferedReader br = new BufferedReader(new FileReader("data.txt"))){return br.readLine();}}1、行為參數(shù)化
如果我一次性想讀取多個行文件呢?
String result = processFile((BufferedReader br) -> br.readLine() + br.readLine());2、使用函數(shù)時接口傳遞行為
package com.xiao.java_base.lambdas.demo01;import java.io.BufferedReader; import java.io.IOException;@FunctionalInterface public interface BufferReaderProcessor {String process(BufferedReader bufferedReader) throws IOException; }現(xiàn)在就可以把這個接口作為新的ProcessFile方法參數(shù)了
3、執(zhí)行一個行為并轉為lambda
@Test public void test01() throws IOException {BufferReaderProcessor bufferReaderProcessor = new BufferReaderProcessor() {@Overridepublic String process(BufferedReader bufferedReader) throws IOException {return bufferedReader.readLine() + bufferedReader.readLine();}};// 使用lambda進行優(yōu)化BufferReaderProcessor bufferReaderProcessor = bufferedReader ->bufferedReader.readLine() + bufferedReader.readLine();// 執(zhí)行process進行調(diào)用bufferReaderProcessor.process(new BufferedReader(new FileReader("data.txt"))); }這里就說明了如果創(chuàng)建函數(shù)是接口、使用、合并的過程;
使用函數(shù)式接口
1、常用的函數(shù)型接口
Predicate:里面有一個 test() 方法,設置 Predicate函數(shù)接口的實現(xiàn)方法,通過傳遞參數(shù),讓其進行判斷是否符合函數(shù)方法中的實現(xiàn)方法,如果符合就獲取,否者就不進行操作;
public static <T> List<T> filter(List<T> list, Predicate<T> p) {List<T> results = new ArrayList<>();for (T t : list) {if (p.test(t)) {results.add(t);}}return results; }public static void main(String[] args) {Predicate<String> nonEmptyStringPredicate = (String s) -> !s.isEmpty();List<String> eeqwer = filter(Arrays.asList("args","123"), nonEmptyStringPredicate);System.out.println(eeqwer); }Function:創(chuàng)造型函數(shù)式接口
public class FunctionS<T, R> {public static <T, R> List<R> map(List<T> list, Function<T, R> f) {ArrayList<R> result = new ArrayList<>();for (T t : list) {result.add(f.apply(t));}return result;}public static void main(String[] args) {List<Integer> map = map(Arrays.asList("lambda", "in", "action"), String::length);// 傳統(tǒng)寫法//List<Integer> map = map(Arrays.asList("lambda", "in", "action"), new Function<String, Integer>() {// @Override// public Integer apply(String s) {// return s.length();// }//});System.out.println(map);} }Consume:消費型函數(shù)式接口,傳入的數(shù)據(jù)源在接口中被消費,沒有返回值;
public class ConsumeS<T> {public static <T> void consumes(List<T> list, Consumer<T> consumer){for (T t : list) {consumer.accept(t);}}public static void main(String[] args) {consumes(Arrays.asList("args","123","eeeeer"),System.out::println);}/*運行結果:args123eeeeer*/}注意:我們還需要考慮一個問題,就是基本數(shù)據(jù)類型的裝箱和拆箱功能,這些會極大的影響java的運行效率
所以java8 就自帶了一個專門不用拆箱和裝箱的類:
比如: IntPredicate、DoublePredicate。。。
? 一般來說,針對專門的輸入?yún)?shù)類型的函數(shù)式接口的名稱都要加上對應的數(shù)據(jù)類型,那么就是可以免于上述性能問題;
這里是int數(shù)據(jù)類型 ?
這里是double基本數(shù)據(jù)類型 ?
2、重構使用lambda的問題
2.1 重構lambda找不到情況
接下來這個代碼使用lambda的情況下都是可以成立,但是這個到底需要找那個呢?
public static void doSomething(Runnable runnable){runnable.run();}public static <T>void doSomething(Task<T> t){t.execute();}public static void main(String[] args) {// 這個沒問題doSomething(new Task<Object>() {@Overridepublic void execute() {System.out.println("SUCCESS!");}});// 查看錯誤doSomething(() -> System.out.println("AAA"));}此時IDEA給出了編譯報錯的問題,所以我們不必擔心;但是如何解決這個問題呢?
可以對Task嘗試使用顯式的類型轉換來解決這種模棱兩可的情況:
2.2 從lambda表達式到方法引用的轉換
Lambda表達式非常適用于需要傳遞代碼片段的場景。不過,為了改善代碼的可讀性,也請盡量使用方法引用。因為方法名往往能更直觀地表達代碼的意圖。
Map<CaloricLevel, List<Dish>> dishesByCaloricLevel =menu.stream().collect(groupingBy(dish -> {if (dish.getCalories() <= 400) return CaloricLevel.DIET;else if (dish.getCalories() <= 700) return CaloricLevel.NORMAL;else return CaloricLevel.FAT;}));那么我們可以將 lambda 表達式中的信息進行抽離,直接使用下面這種方式:
// src/com/xiao/java_base/lambdas/demo03 List<Dish> dishes = Arrays.asList(new Dish("土豆片", null, 200),new Dish("油炸土豆", null, 500),new Dish("油油炸土豆片", null, 800),new Dish("油油油炸土豆片", null, 1000));Map<CaloricLevel, List<Dish>> collect =dishes.stream().collect(groupingBy(Dish::getCaloricLevel));collect.forEach((caloricLevel,list) ->{System.out.println("-------------------"+caloricLevel.name()+"-------------------");list.forEach(System.out::print);System.out.println("---------------------------------------------------------");});但是同時需要修改 Dish 中的 getCaloricLevel 方法
public CaloricLevel getCaloricLevel() {if (this.getCalories() <= 400) return CaloricLevel.DIET;else if (this.getCalories() <= 700) return CaloricLevel.NORMAL;else return CaloricLevel.FAT; }所以現(xiàn)在思路就很明確了:
? lambda表達式直接通過方法引用的方式獲取到對應的能量等級枚舉值。
看一下獲取的分組值:
3、使用lambda重構設計模式
3.1 重構策略模式
什么是策略模式呢?
一個代表某個算法的接口(Strategy接口)。
一個或多個該接口的具體實現(xiàn),它們代表了算法的多種實現(xiàn)(比如,實體類ConcreteStrategyA或者ConcreteStrategyB)。
一個或多個使用策略對象的客戶。
假設你希望驗證輸入的內(nèi)容是否根據(jù)標準進行了恰當?shù)母袷交?#xff08;比如只包含小寫字母或數(shù)字)。你可以從定義一個驗證文本(以String的形式表示)的接口入手:
public interface ValidationStrategy {boolean execute(String s); }其次,你定義了該接口的一個或多個具體實現(xiàn):
public class IsAllLowerCase implements ValidationStrategy {public boolean execute(String s){return s.matches("[a-z]+");} } public class IsNumeric implements ValidationStrategy {public boolean execute(String s){return s.matches("\\d+");} }之后,你就可以在你的程序中使用這些略有差異的驗證策略了:
public class Validator{private final ValidationStrategy strategy;public Validator(ValidationStrategy v){this.strategy = v;}public boolean validate(String s){return strategy.execute(s);} } Validator numericValidator = new Validator(new IsNumeric()); boolean b1 = numericValidator.validate("aaaa"); ←---- 返回false Validator lowerCaseValidator = new Validator(new IsAllLowerCase ()); boolean b2 = lowerCaseValidator.validate("bbbb"); ←---- 返回true使用lambda優(yōu)化的方式:
3.2 模板方法
就如同下面代碼,雖然說是策略模式但是,也是屬于lambda的模板方法
如下業(yè)務場景:
需要編寫一個簡單的在線銀行應用。通常,用戶需要輸入一個用戶賬戶,之后應用才能從銀行的數(shù)據(jù)庫中得到用戶的詳細信息,最終完成一些讓用戶滿意的操作。不同分行的在線銀行應用讓客戶滿意的方式可能略有不同,比如給客戶的賬戶發(fā)放紅利,或者僅僅是少發(fā)送一些推廣文件。
abstract class OnlineBanking {public void processCustomer(int id){Customer c = Database.getCustomerWithId(id);makeCustomerHappy(c);}abstract void makeCustomerHappy(Customer c); }這里只是一個代碼樣式,其思想是和銀行代碼一樣的。
[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-0szli7P0-1637059492930)(E:/typora%E7%AC%94%E8%AE%B0/typoraImg/image-20211115220437927-16369855413413.png)]
public static void main(String[] args) {boolean bo1 = validate("aaa", s-> s.matches("[a-z]+"));System.out.println(bo1);boolean bo2 = validate("bbb", s-> s.matches("\\\\d+"));System.out.println(bo2); }此時如果有其他銀行要使用這個模板,就需要繼承這個類,實現(xiàn)OnlineBanking這個類,但是改用lambda表達式就可以使用下面這個模板。
public void processCustomer(int id, Consumer<Customer> makeCustomerHappy){Customer c = Database.getCustomerWithId(id);makeCustomerHappy.accept(c); }現(xiàn)在,你可以很方便地通過傳遞Lambda表達式,直接插入不同的行為,
new OnlineBankingLambda().processCustomer(1337, (Customer c) ->System.out.println("Hello " + c.getName());總結
以上是生活随笔為你收集整理的JAVA高级(一)——lambda的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: PAC与样本复杂度
- 下一篇: ssm学生宿舍管理系统源码