for循环 与 lambda 表达式 关于局部变量和对象内部变量研究

a、b、c三个方法中 c通过不了编译,并提示【lambda 表达式中使用的变量应为 final 或有效 final】

  public void a(LocalTourOrder order, List<LocalOrderMoney> moneyList) {
    //团款
    BigDecimal receiveAmount = BigDecimal.ZERO;
    //代收
    BigDecimal insteadReceiveFee = BigDecimal.ZERO;
    //代付
    BigDecimal insteadPaymentFee = BigDecimal.ZERO;
    //未收团款
    BigDecimal unCollectionReceiveAmount = BigDecimal.ZERO;
    if (CollUtil.isNotEmpty(moneyList)) {
      for (LocalOrderMoney money : moneyList) {
        Integer moneyType = money.getMoneyType();
        if (OrderMoneyType.RECEIVE.getCode().equals(moneyType)) {
          receiveAmount = receiveAmount.add(money.getAmount());
        } else if (OrderMoneyType.INSTEAD_RECEIVE.getCode().equals(moneyType)) {
          insteadReceiveFee = insteadReceiveFee.add(money.getAmount());
        } else if (OrderMoneyType.INSTEAD_PAYMENT.getCode().equals(moneyType)) {
          insteadPaymentFee = insteadPaymentFee.add(money.getAmount());
        }
      }
      unCollectionReceiveAmount = receiveAmount.subtract(insteadReceiveFee).add(insteadPaymentFee);
    }
    order.setReceiveAmount(receiveAmount);
    order.setInsteadReceiveFee(insteadReceiveFee);
    order.setInsteadPaymentFee(insteadPaymentFee);
    order.setUnCollectionReceiveAmount(unCollectionReceiveAmount);
  }

  public void b(LocalTourOrder order, List<LocalOrderMoney> moneyList) {
    order.setReceiveAmount(BigDecimal.ZERO);
    order.setInsteadReceiveFee(BigDecimal.ZERO);
    order.setInsteadPaymentFee(BigDecimal.ZERO);
    moneyList.forEach(money -> {
      if (OrderMoneyType.RECEIVE.getCode().equals(money.getMoneyType())) {
        order.setReceiveAmount(order.getReceiveAmount().add(money.getAmount()));
      } else if (OrderMoneyType.INSTEAD_PAYMENT.getCode().equals(money.getMoneyType())) {
        money.setUnCollectionAmount(money.getAmount());
        order.setInsteadPaymentFee(order.getInsteadPaymentFee().add(money.getAmount()));
      } else if (OrderMoneyType.INSTEAD_RECEIVE.getCode().equals(money.getMoneyType())) {
        money.setUnCollectionAmount(money.getAmount());
        order.setInsteadReceiveFee(order.getInsteadReceiveFee().add(money.getAmount()));
      }
    });
    // 初始化未收款金额
    order.setUnCollectionReceiveAmount(
        order.getReceiveAmount().add(order.getInsteadPaymentFee())
            .subtract(order.getInsteadReceiveFee()));
  }

  public void c(LocalTourOrder order, List<LocalOrderMoney> moneyList) {
    BigDecimal receiveAmount = BigDecimal.ZERO;
    BigDecimal insteadReceiveFee = BigDecimal.ZERO;
    BigDecimal insteadPaymentFee = BigDecimal.ZERO;
    BigDecimal unCollectionReceiveAmount = BigDecimal.ZERO;
    moneyList.forEach(money -> {
      Integer moneyType = money.getMoneyType();
      if (OrderMoneyType.RECEIVE.getCode().equals(moneyType)) {
        receiveAmount = receiveAmount.add(money.getAmount());
      } else if (OrderMoneyType.INSTEAD_RECEIVE.getCode().equals(moneyType)) {
        insteadReceiveFee = insteadReceiveFee.add(money.getAmount());
      } else if (OrderMoneyType.INSTEAD_PAYMENT.getCode().equals(moneyType)) {
        insteadPaymentFee = insteadPaymentFee.add(money.getAmount());
      }
    });
    unCollectionReceiveAmount = receiveAmount.subtract(insteadReceiveFee).add(insteadPaymentFee);
    order.setReceiveAmount(receiveAmount);
    order.setInsteadReceiveFee(insteadReceiveFee);
    order.setInsteadPaymentFee(insteadPaymentFee);
    order.setUnCollectionReceiveAmount(unCollectionReceiveAmount);
  }

1. 并发安全性
Lambda 表达式可能被传递到不同的线程中执行。如果允许 Lambda 修改外部局部变量,而局部变量是存储在栈上的(每个线程有自己的栈),当另一个线程访问该变量时,原始栈帧可能已经销毁,导致变量副本不一致,引发难以追踪的并发问题。强制 final 或有效 final 意味着变量值在捕获时就已经确定,避免了这种“可变共享状态”的风险。

2. 保持与内部类的一致性
在 Lambda 出现之前,匿名内部类就已经要求访问的局部变量必须是 final 的。Lambda 沿用了这一约束,目的是减少开发者从匿名类迁移到 Lambda 时的意外行为,也保持了语言规则的统一性。

3. 简化闭包实现
Lambda 本质上是一个闭包,它会捕获所使用的外部局部变量的值(而不是变量本身)。如果允许修改变量,就需要把变量从栈内存转移到堆内存,并通过某种引用传递机制来同步修改,这会大大增加实现复杂度和性能开销。强制 final/ effectively final 后,只需复制一份常量值到 Lambda 中,简单可靠。

4. 避免语义混淆
允许 Lambda 修改外部局部变量容易与实例字段(可随意修改)或数组元素(可修改)混淆。Java 的设计者希望开发者明确区分:局部变量是按值捕获的,不应被修改;而对象内部的状态(通过字段访问)是可以改变的。这种区分让代码意图更清晰。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值