最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

一文詳解Java Function的高級使用技巧

 更新時間:2026年03月17日 08:59:41   作者:魔道不誤砍柴功  
本文總結(jié)了10種企業(yè)級Java Function的編程技巧,包括柯里化、EnumMap+Supplier、ImmutableMap+BiConsumer等,通過函數(shù)式編程解決實際問題,提高代碼復(fù)用性和可維護性,需要的朋友可以參考下

前言

來自企業(yè)級的一個經(jīng)驗總結(jié),每個技巧都經(jīng)過生產(chǎn)環(huán)境驗證,附帶完整上下文。

一、柯里化:函數(shù)返回函數(shù)的實戰(zhàn)價值

柯里化(Currying)在教材里總是 a -> b -> a + b 這種讓人打哈欠的例子。但在實際工程中,它解決的是一個非常具體的問題:如何在不修改框架代碼的前提下,讓調(diào)用方自由選擇"先定位到哪個服務(wù),再調(diào)哪個方法"

看這個通用執(zhí)行器:

    private <T, R> WeaResult<R> execute(
            InvoiceParams<T> req,
            Function<InvoiceFactory, Function<InvoiceParams<T>, WeaResult<R>>> methodSelector
    ) {
        String invoiceType = resolveInvoiceType(req);

        InvoiceFactory factory = invoiceFactory.getInstance(invoiceType);
        if (factory == null) {
            throw new RuntimeException("未找到開票工廠, invoiceType=" + invoiceType);
        }

        Function<InvoiceParams<T>, WeaResult<R>> handlerFunc = methodSelector.apply(factory);
        if (handlerFunc == null) {
            throw new RuntimeException("工廠 method 為 null, invoiceType=" + invoiceType);
        }

        try {
            return handlerFunc.apply(req);
        } catch (Exception e) {
            throw new RuntimeException("執(zhí)行失敗: " + e.getMessage(), e);
        }
    }

methodSelector 的類型是 Function<InvoiceFactory, Function<InvoiceParams<T>, WeaResult<R>>>。拆開看:第一層根據(jù)工廠選出一個具體的處理函數(shù),第二層用這個函數(shù)處理請求。兩步走,但調(diào)用方可以用完全不同的方式填充這兩步。

方式一:完整的雙層 lambda,適合需要加額外邏輯的場景(加鎖、異常捕獲):

    public WeaResult<MakeInvoiceResDTO> makeInvoice(InvoiceParams<MakeInvoiceReqDTO> inputParams) {
        return execute(inputParams, invoiceFactory -> e -> {
            try {
                boolean flag = safeLockService.tryLock(buildInvoiceSafeLockEntity(e));
                if (!flag) return WeaResult.fail(SystemEnv.getHtmlLabelName(342919, "加鎖失敗,請稍后在試"), true);

                return invoiceFactory.getMakeInvoice().makeInvoice(e);
            } catch (DuplicateKeyException ex) {
                return WeaResult.fail(5012, buildRepeatMsg(e), true);
            } catch (Exception ex) {
                if (isSocketTimeoutException(ex)) {
                    log.warn(">>>>>>SocketTimeoutException,返回成功+開票中,bizId={}, msg={}", e.getInvoiceReq().getExternalDocumentNo(), ex.getMessage());
                    return WeaResult.success(MakeInvoiceResDTO.builder().returnCode("00").returnMessage(SystemEnv.getHtmlLabelName(343981, "恭喜你,開票請求提交成功!") + ",ErrorMsg:" + ex.getMessage()).build());
                }
                return WeaResult.fail(ex.getMessage(), true);
            }
        });
    }

方式二:一行方法引用鏈,適合純轉(zhuǎn)發(fā)的場景:

    public WeaResult<RedConfirmationApplyResDTO> redMakeInvoiceApply(InvoiceParams<RedConfirmationApplyReqDTO> inputParams) {
        return execute(inputParams, factory -> factory.getRedInvoice()::redConfirmationApply);
    }

    public WeaResult<CloudTitleInfo> getEnterpriseInfo(InvoiceParams<String> inputParams) {
        return execute(inputParams, factory -> factory.getEnterpriseDetail()::getEnterpriseInfo);
    }

factory -> factory.getRedInvoice()::redConfirmationApply 的含義是:拿到工廠后,先取出紅字發(fā)票服務(wù)(getRedInvoice()),再綁定其 redConfirmationApply 方法作為最終的處理函數(shù)。這就是柯里化的實際價值——一個 execute 方法同時支撐了"開票加鎖 + 異常降級"和"紅沖直接轉(zhuǎn)發(fā)"兩種截然不同的調(diào)用模式,零代碼重復(fù)

如果不用柯里化,你需要寫一個 executeWithLock、一個 executeSimple、一個 executeWithRetry……每加一種變體就多一個方法。

二、EnumMap + Supplier:干掉 switch-case 的正確方式

大家都知道可以用 Map 替代 switch,但很多人用的是 Map<Enum, String> 這種存靜態(tài)值的方式。真正有用的是 Map<Enum, Supplier<T>>——存的不是值,而是"算值的過程"

    public RuleCommonDTO getConfig(RuleEnum ruleEnum, CreditConditionRuleContext context) {

        Map<RuleEnum, Supplier<RuleCommonDTO>> supplierOfMap = new EnumMap<>(RuleEnum.class);

        supplierOfMap.put(RuleEnum.COMPANY, () -> buildRuleCommonDTO(
                ebDataSupport.getCompanyConfig(context.getCompanyTaxNo()),
                CompanyConfig::getRemindTypes,
                CompanyConfig::getRemindObjs,
                CompanyConfig::getCompanyName
        ));

        supplierOfMap.put(RuleEnum.CUSTOMER, () -> buildRuleCommonDTO(
                ebDataSupport.getCustomerConfig(context.getCompanyTaxNo(), context.getCusTaxNo()),
                CustomerConfig::getRemindTypes,
                CustomerConfig::getRemindObjs,
                CustomerConfig::getCustomerName
        ));

        supplierOfMap.put(RuleEnum.USER, () -> buildRuleCommonDTO(
                ebDataSupport.getUserConfig(context.getCompanyTaxNo(), context.getApplyMakeInvoiceUser()),
                UserConfig::getRemindTypes,
                UserConfig::getRemindObjs,
                UserConfig::getEmployeeName
        ));

        return Optional.ofNullable(supplierOfMap.get(ruleEnum))
                .map(Supplier::get)
                .orElseThrow(() -> new IllegalArgumentException(
                        SystemEnv.getHtmlLabelName(299572, "無效的 RuleEnum 值: ") + ruleEnum)
                );
    }

關(guān)鍵點在于:三個 Supplier 中的 ebDataSupport.getCompanyConfig()、getCustomerConfig()、getUserConfig() 都是數(shù)據(jù)庫查詢。如果用普通 Map 存值,三個查詢會在方法入口全部執(zhí)行。用 Supplier 包裝后,只有命中的那個枚舉對應(yīng)的查詢才會真正執(zhí)行

配合最后的 Optional.ofNullable(...).map(Supplier::get).orElseThrow(...) 三連,既做了空值防御又做了延遲求值,還給了明確的錯誤提示。

再看 buildRuleCommonDTO 這個被調(diào)用的方法:

    private <T> RuleCommonDTO buildRuleCommonDTO(T config,
                                                 Function<T, List<String>> remindTypesFunc,
                                                 Function<T, List<SubFieldDTO>> remindObjsFunc,
                                                 Function<T, String> nameFunc) {
        return Optional.ofNullable(config)
                .map(e -> RuleCommonDTO.builder()
                        .remindTypes(remindTypesFunc.apply(e))
                        .remindObjs(remindObjsFunc.apply(e))
                        .name(nameFunc.apply(e))
                        .build()
                ).orElse(null);
    }

三個 Function<T, ?> 參數(shù)讓這個方法可以處理 CompanyConfig、CustomerConfig、UserConfig 三種完全不同的類型,調(diào)用方只需要傳對應(yīng)的方法引用。一個私有方法干了本來需要三個方法或者一堆 instanceof 才能干的事。

三、ImmutableMap + BiConsumer:服務(wù)路由表

當系統(tǒng)有 9 個不同的自動化服務(wù)需要根據(jù)能力碼分發(fā)時,if-elseswitch 會變成災(zāi)難??催@個做法:

            ImmutableMap<String, BiConsumer<MakeInvoiceCompanyInfo, MakeInvoiceCompanyInfo>>
                    serviceMap = ImmutableMap.of(
                    A_203059, (a, b) -> autoVerifyService.execute(a, b, authorCode, interKey),
                    A_203067, (a, b) -> autoAggregationService.execute(a, b, authorCode, interKey),
                    A_203057, (a, b) -> autoEntryService.execute(a, b, authorCode, interKey),
                    A_203065, (a, b) -> autoDeductionService.execute(a, b, authorCode, interKey),
                    A_202044, (a, b) -> autoJZFWMakeInvoiceService.execute(a, b, authorCode, interKey),
                    A_202046, (a, b) -> autoBDCXSMakeInvoiceService.execute(a, b, authorCode, interKey),
                    A_202038, (a, b) -> autoBDCZLMakeInvoiceService.execute(a, b, authorCode, interKey),
                    A_202007, (a, b) -> autoMakeInvoiceService.execute(a, b, authorCode, interKey),
                    A_202031, (a, b) -> autoCropMakeInvoiceService.execute(a, b, authorCode, interKey)
            );
            abilityCodes.stream()
                    .filter(serviceMap::containsKey)
                    .forEach(scene ->
                            executeServiceSafely(
                                    () -> Optional.ofNullable(serviceMap.get(scene))
                                            .ifPresent(e -> e.accept(originCompanyInfo, virtualCompanyInfo))
                            )
                    );

有三個細節(jié)值得關(guān)注:

  1. 閉包捕獲。每個 BiConsumer<MakeInvoiceCompanyInfo, MakeInvoiceCompanyInfo> 的 lambda 體內(nèi)都引用了外部的 authorCodeinterKey。這兩個變量是"固定參數(shù)"(對于本次調(diào)用而言),而 a, b 是"動態(tài)參數(shù)"。通過閉包,你自然地實現(xiàn)了部分應(yīng)用(Partial Application)——把四參方法變成了雙參的 BiConsumer。
  2. serviceMap::containsKey 作為 filter 的謂詞。這比 scene -> serviceMap.containsKey(scene) 更簡潔,而且語義更清晰——“過濾出路由表中存在的場景”。
  3. executeServiceSafely 包裝 Runnable。每個服務(wù)的執(zhí)行都被 try-catch 包裹,任何一個服務(wù)拋異常不會影響其他服務(wù)。這個模式的價值在于:異常隔離邏輯只寫一次,9 個服務(wù)自動受益。

四、函數(shù)裝飾器:用 Function 參數(shù)實現(xiàn) AOP

AbstractAutoSupport 中有兩個方法,它們的關(guān)系揭示了函數(shù)式編程中最實用的模式之一——函數(shù)裝飾器

第一層:doInvokeMethod,給任意 Function 加上狀態(tài)追蹤和異常捕獲:

    protected <T, R> R doInvokeMethod(InvoiceParams<T> params,
                                      String interfaceKey,
                                      Function<InvoiceParams<T>, WeaResult<R>> function) {
        WeaResult<R> weaResult = null;
        String errorMsg = "";
        AtomicReference<String> status = new AtomicReference<>("0");
        try {
            weaResult = function.apply(params);
            R data = weaResult.getData();
            checkIsSuccess(status, data, "no-init");
            compressDataStream(data);
            return data;
        } catch (Exception e) {
            setFail(status);
            errorMsg = e.getMessage();
        } finally {
            doUpdateState(interfaceKey, params, weaResult, errorMsg, status.get());
        }
        return null;
    }

第二層:retryOf3Times,給任意 Supplier 加上重試能力:

    protected <T> void retryOf3Times(Supplier<T> supplier) {
        for (int i = 0; i < 3; i++) {
            T t = supplier.get();
            if (canApplyNext(t)) {
                break;
            }
            sleep3Mins();
        }
    }

在實際調(diào)用處,兩層嵌套:

retryOf3Times(() -> doInvokeMethod(build, getInterfaceKey(...), invoiceDeduction::queryApplyStatisticsStatus));

從內(nèi)到外讀:invoiceDeduction::queryApplyStatisticsStatus 是原始業(yè)務(wù)方法,被 doInvokeMethod 裝飾后獲得了"狀態(tài)追蹤 + 異常捕獲 + 數(shù)據(jù)壓縮"的能力,再被 retryOf3Times 裝飾后獲得了"失敗重試"的能力。

這就是窮人版的 AOP。 不需要 AspectJ,不需要注解處理器,不需要動態(tài)代理。一個 Function 參數(shù)就夠了。而且它比 AOP 更好的地方是——調(diào)用方在寫代碼的時候,能清清楚楚地看到這個方法被加了哪些裝飾,不存在"隱式增強"的困惑。

更進一步,executeAndCache 是另一個裝飾器,增加了"成功后緩存結(jié)果"的能力:

    protected <T, R> R executeAndCache(InvoiceParams<T> build, String interfaceKey, String cacheKey,
                                       Function<InvoiceParams<T>, WeaResult<R>> function,
                                       Function<R, List<?>> getListFunc) {
        WeaResult<R> weaResult = null;
        String errorMsg = "";
        AtomicReference<String> status = new AtomicReference<>("0");
        try {
            weaResult = function.apply(build);
            R data = weaResult.getData();
            Optional.ofNullable(data)
                    .map(getListFunc)
                    .filter(list -> !list.isEmpty())
                    .ifPresent(e -> CacheUtils.put(cacheKey, JSON.toJSONString(data)));
            checkIsSuccess(status, data, "init");
            compressDataStream(data);
            return data;
        } catch (Exception e) {
            setFail(status);
            errorMsg = e.getMessage();
        } finally {
            doUpdateState(interfaceKey, build, weaResult, errorMsg, status.get());
        }
        return null;
    }

注意第二個 Function<R, List<?>> getListFunc 參數(shù)——它的職責是從返回結(jié)果中提取"是否有有效數(shù)據(jù)"的判斷依據(jù)。只有當提取出的列表非空時,才寫緩存。這讓緩存條件完全由調(diào)用方控制,executeAndCache 本身不需要知道 R 的具體結(jié)構(gòu)。

五、Supplier + 分布式鎖:資源管控的函數(shù)式寫法

    public <T> WeaResult<T> withLock(String key, int timeout, Supplier<WeaResult<T>> action) {
        boolean locked = false;
        try {
            locked = distributionLock.isSuccessTryLock(key, timeout);
            if (!locked) {
                return WeaResult.fail(SystemEnv.getHtmlLabelName(321088, "當前操作頻繁,請稍后再試") + ":" + key, true);
            }
            return action.get();
        } catch (Exception ex) {
            log.error(">>>>>>Error execute withLock, key={}", key, ex);
            return WeaResult.fail(SystemEnv.getHtmlLabelName(321091, "操作失敗,請稍后再試") + ":" + key, true);
        } finally {
            if (locked) {
                try {
                    distributionLock.unLock(key);
                } catch (Exception e) {
                    log.error(">>>>>>Error unlocking, key={}", key, e);
                }
            }
        }
    }

不用 Supplier,你要么把鎖邏輯散落到每個調(diào)用方(復(fù)制粘貼 try-finally-unlock),要么搞一套 AOP + 注解的方案。Supplier<WeaResult<T>> 讓這件事變成了一行調(diào)用:

return invLockUtils.withLock("make_invoice_" + bizId, () -> {
    // 你的業(yè)務(wù)邏輯
    return doMakeInvoice(params);
});

注意 finally 塊中的 if (locked) 判斷——如果加鎖本身就失敗了(locked = false),不能去解鎖一個你沒持有的鎖。這個細節(jié)在手寫鎖時很容易漏掉,而封裝成 withLock 后,這種邊界處理只需要正確實現(xiàn)一次。

六、Function 做泛型數(shù)據(jù)適配器

當系統(tǒng)需要從同一個底層數(shù)據(jù)源(EB 表單)中讀取完全不同結(jié)構(gòu)的配置時,Function<Map<String, FieldDTO>, T> 充當了"數(shù)據(jù)適配器"的角色:

    private <T> T getConfig(String tableIdKey, QueryConditionWrapper conditionWrapper, Function<Map<String, FieldDTO>, T> configMapper) {
        try {
            String tableId = ebFormObjUtil.getTableIdSync(tableIdKey);

            List<Map<String, FieldDTO>> configData = ebFormFieldService.getValueByConditionTree(tableId, conditionWrapper);
            return Optional.ofNullable(configData)
                    .filter(data -> !data.isEmpty())
                    .map(data -> configMapper.apply(data.get(0)))
                    .orElse(null);
        } catch (Exception ex) {
            log.error("Error in getConfig for tableIdKey {}: {}", tableIdKey, ex.getMessage(), ex);
            return null;
        }
    }

調(diào)用方傳入的 lambda 是一個完整的建造過程——從原始 Map 到強類型 DTO:

    public CreditGlobalConfig getCreditGlobalConfig() {
        return getConfig(TagConstant.UF_INV_LIMIT_CREDIT_SETTING,
                new QueryConditionWrapper()
                        .eq("xeszzkg", "1")
                        .eq("zhkey", tenantKey())
                        .eq("yyid", String.valueOf(ebFormObjUtil.getAppId(TagConstant.CS_APPID_TAG, tenantKey()))),
                data -> CreditGlobalConfig.builder()
                        .globalSwitch(FieldUtils.getValueIfPresent(data.get("xeszzkg")).orElse("0"))
                        .appId(String.valueOf(ebFormObjUtil.getAppId(TagConstant.CS_APPID_TAG, tenantKey())))
                        .tenantKey(tenantKey())
                        .customerSwitch(FieldUtils.getValue(data.get("kg_2aji")))
                        .customerAmount(FieldUtils.getValue(data.get("khwdmryje")))
                        .warnType(FieldUtils.getFirstValue(data.get("kzfs")))
                        .build()
        );
    }

每個 getXxxConfig() 方法共享完全相同的"查表 → 取第一條 → 轉(zhuǎn)換 → 空值兜底"流程,只有最后的"轉(zhuǎn)換"步驟不同。Function 參數(shù)讓你做到了"流程復(fù)用,差異外置"——這是模板方法模式的函數(shù)式重寫,不需要繼承。

七、EnumMap + Function + CompletableFuture:并行策略引擎

    private boolean matchRules(CreditConditionRuleContext context, MatchType matchType) {
        if (CollectionUtils.isEmpty(conditionRuleList)) return false;

        Map<MatchType, Function<List<InvCreditConditionRuleService>, Boolean>> matchStrategies = new EnumMap<>(MatchType.class);

        matchStrategies.put(MatchType.ANY, rules -> anyMatch(context, rules));
        matchStrategies.put(MatchType.ALL, rules -> rules.stream().allMatch(rule -> rule.match(context)));

        return matchStrategies.getOrDefault(matchType, rules -> false).apply(reSorted(conditionRuleList));
    }

    private static boolean anyMatch(CreditConditionRuleContext context, List<InvCreditConditionRuleService> rules) {
        ExecutorService executor = InvThreadPoolUtil.workBizThreadPool;
        List<CompletableFuture<Boolean>> futures = rules.stream()
                .map(rule -> CompletableFuture.supplyAsync(() -> rule.match(context), executor))
                .collect(Collectors.toList());

        return futures.stream().anyMatch(CompletableFuture::join);
    }

這段代碼值得拆解的地方在于:

  • MatchType.ANY 對應(yīng)的策略函數(shù)內(nèi)部用了 CompletableFuture.supplyAsync 做并行匹配——多條規(guī)則同時跑,任一命中即返回。
  • MatchType.ALL 對應(yīng)的策略函數(shù)用了串行的 allMatch——必須全部滿足,短路求值。
  • getOrDefault(matchType, rules -> false) 提供了一個安全的 fallback 函數(shù)。不是返回 null,不是拋異常,而是返回一個"永遠返回 false"的函數(shù)。

注意 rules -> false 中的 rules 參數(shù)根本沒被使用——這是一個故意忽略輸入的函數(shù),它的存在只是為了滿足 Function<List<...>, Boolean> 的簽名。這種寫法在函數(shù)式編程中叫做 “常量函數(shù)”(Constant Function),比 null 判斷優(yōu)雅得多。

八、Function + BiFunction:兩步式服務(wù)編排

    protected <V, S, R> WeaResult<R> handleRequest(
            ApiParams<V> request,
            InvCompanyInfoService companyInfoService,
            Function<MakeInvoiceCompanyInfo, S> serviceResolver,
            BiFunction<S, InvoiceParams<V>, WeaResult<R>> handler
    ) {
        WeaResult<R> apply = null;
        try {
            echoInfoLog(request);
            setTargetCurrentEmployeeId(request);

            V invoiceReq = request.getInvoiceReq();
            MakeInvoiceCompanyInfo companyInfo = companyInfoService.getInvCompanyInfoByTaxNo(getTaxNo(invoiceReq));
            InvoiceParams<V> params = InvoiceParams.<V>builder()
                    .companyInfo(companyInfo)
                    .invoiceReq(invoiceReq)
                    .build();
            S service = serviceResolver.apply(companyInfo);
            apply = handler.apply(service, params);
        } catch (Exception ex) {
            log.error(">>>>>>Error when handleRequest ex:{}", ex.getMessage(), ex);
            return WeaResult.fail(ex.getMessage(), true);
        } finally {
            TenantRpcContext.removeTargetTenantKey();
            TenantRpcContext.removeTargetEmployeeId();
        }
        return apply;
    }

三個泛型參數(shù) <V, S, R> 分別代表:請求體類型、服務(wù)類型、響應(yīng)類型。兩個函數(shù)參數(shù)的分工:

  • Function<MakeInvoiceCompanyInfo, S> serviceResolver:根據(jù)公司信息決定用哪個服務(wù)實例(可能是百望、可能是航信、可能是樂企)。
  • BiFunction<S, InvoiceParams<V>, WeaResult<R>> handler:拿到服務(wù)實例后,執(zhí)行具體的業(yè)務(wù)操作。

這種設(shè)計把"選哪個服務(wù)"和"用服務(wù)做什么"徹底解耦。調(diào)用方類似:

return handleRequest(request, companyInfoService,
    companyInfo -> instanceFetcher,                    // 第一步:選服務(wù)
    (fetcher, params) -> fetcher.makeInvoice(params)   // 第二步:調(diào)方法
);

而框架層的 handleRequest 負責了所有調(diào)用方不想關(guān)心的事:日志、租戶上下文設(shè)置、異常捕獲、上下文清理。

九、Optional 鏈的工程邊界

Optional 鏈好用,但有邊界。先看一個好的例子——清晰的逐層解包:

    private String resolveInvoiceType(InvoiceParams<?> req) {
        return Optional.ofNullable(req.getCompanyInfo())
                .map(MakeInvoiceCompanyInfo::getThirdType)
                .map(InvoiceThirdType::getInvoiceType)
                .orElseThrow(() ->
                        new RuntimeException("缺失 thirdType 字段: " + JSON.toJSONString(req.getCompanyInfo())));
    }

每一步 .map() 都是一個明確的屬性訪問,讀起來像導(dǎo)航路徑:req → companyInfo → thirdType → invoiceType。

再看一個巧妙的帶 flatMap 的 Optional 鏈:

    private List<UserEntity> getReceivers(RuleCommonDTO commonDTO, Function<List<SubFieldDTO>, List<UserEntity>> toUserEntityList) {
        List<String> remindTypes = commonDTO.getRemindTypes();
        List<SubFieldDTO> remindObjs = commonDTO.getRemindObjs();
        return Optional.ofNullable(remindTypes)
                .filter(e -> e.stream().anyMatch("0"::equals))
                .flatMap(e -> Optional.ofNullable(remindObjs)
                        .filter(r -> !r.isEmpty()))
                .map(r -> toUserEntityList.apply(remindObjs))
                .orElse(new ArrayList<>());
    }

這段鏈做了三件事:① 檢查提醒類型中是否包含 “0”(指定接收人模式),② 檢查接收人列表是否非空,③ 用傳入的 Function 把原始數(shù)據(jù)轉(zhuǎn)成 UserEntity。三個判斷條件任一不滿足,整條鏈短路返回空列表。 這比三層嵌套 if 緊湊得多。

但 Optional 也有濫用的時候。代碼庫里有這樣的寫法:

Integer type = Optional.ofNullable(
    Optional.ofNullable(
        Optional.ofNullable(invoice_info.getCommInfo())
            .orElse(new InvoiceCommInfo()).getPro())
        .orElse(new InvoiceProVo()).getType())
    .orElse(0);

三層嵌套的 Optional.ofNullable(...).orElse(new Xxx()).getYyy(),每層都在創(chuàng)建空對象作為 fallback 然后立刻調(diào)方法。這不叫函數(shù)式,這叫折磨。更清晰的寫法是:

Integer type = Optional.ofNullable(invoice_info.getCommInfo())
    .map(InvoiceCommInfo::getPro)
    .map(InvoiceProVo::getType)
    .orElse(0);

經(jīng)驗法則:Optional 鏈中如果出現(xiàn)了 orElse(new Xxx()).getYyy() 這種"創(chuàng)建臨時對象只為鏈式調(diào)用"的寫法,說明你應(yīng)該用 .map() 替代。

十、反射 vs Function:一個反面教材

    protected <R> boolean canApplyNext(R data) {
        return Optional.ofNullable(data)
                .map(d -> {
                    try {
                        return (String) d.getClass().getMethod("getReturnCode").invoke(d);
                    } catch (Exception ex) {
                        return null;
                    }
                })
                .filter("00"::equals).isPresent();
    }

這個方法對任意類型 R 的對象反射調(diào)用 getReturnCode()。因為 R 沒有上界約束,編譯器不知道 R 有沒有這個方法,只能在運行時用反射碰運氣。

改法很直接——加一個 Function 參數(shù):

protected <R> boolean canApplyNext(R data, Function<R, String> codeExtractor) {
    return Optional.ofNullable(data)
            .map(codeExtractor)
            .filter("00"::equals)
            .isPresent();
}

// 調(diào)用
canApplyNext(result, BWBaseRes::getReturnCode);

反射調(diào)用一次大約 50~100ns,方法引用調(diào)用大約 5ns,差一個數(shù)量級。但性能不是重點——重點是編譯期安全。反射版本在方法名打錯(比如 getReturncode vs getReturnCode)時只會在運行時 NPE,而 Function 版本會在編譯時就報錯。

總結(jié):Function 高級用法的幾條原則

1. 柯里化不是炫技,是分層抽象的工具。 當你的框架需要"兩步定位"(先選服務(wù)再選方法),Function<A, Function<B, R>> 比任何設(shè)計模式都簡潔。

2. Map<Enum, Supplier> 比 switch-case 優(yōu)越的地方不在于"消除了 switch",而在于實現(xiàn)了延遲求值。 只有命中的分支才會執(zhí)行計算。

3. 函數(shù)裝飾器是輕量級 AOP。 doInvokeMethod(params, key, actualMethod) 這種寫法讓橫切關(guān)注點(日志、狀態(tài)、異常)透明地疊加在業(yè)務(wù)函數(shù)上,且調(diào)用方完全可見、可控。

4. Supplier + 資源管理(鎖、連接、事務(wù))是 try-with-resources 的泛化版本。 把"需要被保護的代碼"包成 Supplier,在外層統(tǒng)一處理獲取-釋放-異常。

5. Function<RawData, T> 做數(shù)據(jù)適配器,讓一個讀取方法服務(wù)于 N 種目標類型。 這是泛型和函數(shù)式結(jié)合最自然的場景。

6. 常量函數(shù) x -> defaultValue 作為 getOrDefault 的 fallback,比 null 判斷安全。 它保證了返回值永遠是合法的函數(shù),調(diào)用方不需要做空檢查。

7. Optional 鏈的正確用法是 .map().map().orElse(),不是 .orElse(new Xxx()).getYyy()。 后者本質(zhì)上是在濫用 Optional 來模擬空安全運算符,創(chuàng)建了一堆無意義的臨時對象。

以上就是一文詳解Java Function的高級使用技巧的詳細內(nèi)容,更多關(guān)于Java Function使用技巧的資料請關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

  • Java 解析XML數(shù)據(jù)的4種方式

    Java 解析XML數(shù)據(jù)的4種方式

    這篇文章主要介紹了Java 解析XML數(shù)據(jù)的4種方式,幫助大家更好的用Java處理數(shù)據(jù),感興趣的朋友可以了解下
    2020-09-09
  • Java?ServletContext與ServletConfig接口使用教程

    Java?ServletContext與ServletConfig接口使用教程

    ServletConfig對象,叫Servlet配置對象。主要用于加載配置文件的初始化參數(shù)。我們知道一個Web應(yīng)用里面可以有多個servlet,如果現(xiàn)在有一份數(shù)據(jù)需要傳給所有的servlet使用,那么我們就可以使用ServletContext對象了
    2022-09-09
  • Java遞歸讀取文件例子_動力節(jié)點Java學(xué)院整理

    Java遞歸讀取文件例子_動力節(jié)點Java學(xué)院整理

    本文通過一段示例代碼給大家介紹了java遞歸讀取文件的方法,代碼簡單易懂,非常不錯,具有參考借鑒價值,需要的朋友參考下吧
    2017-05-05
  • springboot集成sensitive-word實現(xiàn)敏感詞過濾的兩種方案

    springboot集成sensitive-word實現(xiàn)敏感詞過濾的兩種方案

    敏感詞過濾通常是指從文本中檢測并移除或替換掉被認為是不適當、冒犯性或違反特定社區(qū)準則的詞匯,這篇文章主要介紹了springboot集成sensitive-word實現(xiàn)敏感詞過濾,需要的朋友可以參考下
    2024-08-08
  • Java在Word中添加多行圖片水印

    Java在Word中添加多行圖片水印

    這篇文章主要介紹了Java在Word中添加多行圖片,圖文講解的很清晰,有對于這方面不懂得同學(xué)可以跟著研究下
    2021-02-02
  • SpringBoot整合Mybatis,解決TypeAliases配置失敗的問題

    SpringBoot整合Mybatis,解決TypeAliases配置失敗的問題

    這篇文章主要介紹了SpringBoot整合Mybatis,解決TypeAliases配置失敗的問題,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2021-07-07
  • mybatis-plus 攔截器敏感字段加解密的實現(xiàn)

    mybatis-plus 攔截器敏感字段加解密的實現(xiàn)

    數(shù)據(jù)庫在保存數(shù)據(jù)時,對于某些敏感數(shù)據(jù)需要脫敏或者加密處理,本文主要介紹了mybatis-plus 攔截器敏感字段加解密的實現(xiàn),感興趣的可以了解一下
    2021-11-11
  • 舉例講解Java中的多線程編程

    舉例講解Java中的多線程編程

    這篇文章主要介紹了舉例講解Java中的多線程編程,線程是Java學(xué)習(xí)中的重要知識,需要的朋友可以參考下
    2015-09-09
  • 基于Jackson實現(xiàn)API接口數(shù)據(jù)脫敏的示例詳解

    基于Jackson實現(xiàn)API接口數(shù)據(jù)脫敏的示例詳解

    用戶的一些敏感數(shù)據(jù),例如手機號、郵箱、身份證等信息,在數(shù)據(jù)庫以明文存儲,但在接口返回數(shù)據(jù)給瀏覽器(或三方客戶端)時,希望對這些敏感數(shù)據(jù)進行脫敏,所以本文就給大家介紹以惡如何利用Jackson實現(xiàn)API接口數(shù)據(jù)脫敏,需要的朋友可以參考下
    2023-08-08
  • java兩個線程同時寫一個文件

    java兩個線程同時寫一個文件

    這篇文章主要為大家詳細介紹了java兩個線程同時寫一個文件,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2019-08-08

最新評論

长泰县| 安龙县| 涿州市| 诸暨市| 林西县| 颍上县| 靖江市| 大竹县| 宜章县| 邮箱| 巴塘县| 察雅县| 浦东新区| 石楼县| 新和县| 广德县| 正镶白旗| 龙里县| 鸡泽县| 惠水县| 三都| 北票市| 铜陵市| 晋江市| 城步| 抚远县| 丹阳市| 白朗县| 芜湖县| 新宾| 九江市| 千阳县| 敖汉旗| 阜平县| 济阳县| 房产| 儋州市| 龙门县| 洮南市| 同仁县| 卢氏县|