如果 class 是通过 Lombok 初始化的,则 class 的谓词不能使用实例变量

Predicates of a class are not able to use the instance variable if the class is initialised via Lombok

我正在通过 Lombok Builder 初始化 class。通过它我正在初始化一个变量。但是当我在谓词定义中使用它时,它的变量值不可用。

我的代码如下所示:

@Builder(toBuilder = true)
@NoArgsConstructor
@AllArgsConstructor
class NumCheck {
    int maxCount;
    Predicate<Integer> isLessThanMax = num -> maxCount < num;
}

public class PredicateInstance {
    public static void main(String[] args) {
        int a = 5, b=11;
        NumCheck numCheck = new NumCheck().toBuilder().maxCount(10).build();
        Stream.of(2,5,6,7,10,11,20,32).filter(numCheck.isLessThanMax).forEach(System.out::println);
    }
}

调试时,maxCount 的值没有初始化为 10。它保持为 0。


如果我删除 Lombok 构建器并以正常方式执行,则工作正常:

class NumCheck {
    int maxCount;
    Predicate<Integer> isLessThanMax = num -> maxCount < num;
}

public class PredicateInstance {
    public static void main(String[] args) {
        int a = 5, b=11;
        NumCheck numCheck = new NumCheck();
        numCheck.maxCount = 10;
        Stream.of(2,5,6,7,10,11,20,32).filter(numCheck.isLessThanMax).forEach(System.out::println);
    }
}

这是 Lombok 的已知限制还是我做错了什么?

可行但不适用于我的情况的解决方法:

  1. 将 maxCount 声明为静态。
  2. 声明 BiPredicate 并发送 maxCount 和 num 参数。

问题在于您调用 Lombok 生成器的方式。

而不是

NumCheck numCheck = new NumCheck().toBuilder().maxCount(10).build();

你应该使用:

NumCheck numCheck = NumCheck.builder().maxCount(10).build();

前者使用 no-arg 构造函数创建一个实例,然后从中创建一个构建器,并使用构建器创建第二个实例,该实例被丢弃(未分配给变量)。

后者只是通过构建器创建一个实例,并将其分配给变量。

您应该问问自己 isLessThanMax 是否真的可以通过构建器设置。在我看来更像是一个不变的定义。如果是这样的话,你应该做到 final。 Lombok 在生成构建器时忽略带有初始值设定项的 final 字段。 (在这种情况下,您也不需要 @Builder.Default。但是,@Builder.Default 在字段上没有任何问题。性能或内存影响完全为零。)

如果您对为什么您的代码表现如此奇怪感兴趣,请查看此处的解释。 当使用 no-args 构造函数实例化时,isLessThanMax 使用对此实例的字段 maxCount 的引用进行初始化。 maxCount 默认为 0。然后调用 toBuilder(),它只是将实例的所有值复制到构建器。最后,您对该构建器调用 maxCount(10).build()。生成的第二个实例将具有 maxCount = 10,但与第一个实例的 isLessThanMax 值完全相同。这意味着第二个实例的谓词仍然引用第一个实例的字段,即0。因此,即使您为第二个实例设置 maxCount = 10Predicate 仍会使用第一个实例中的 maxCount = 0 值进行比较。

tl;dr:不要将引用实例字段的方法引用与 toBuilder().

结合使用