O ile oczywiście ładnie potniesz kod na mniejsze elementy, bo możesz spłodzić potwora:

Listing 1. Generowanie losowego NRB

public String generateNrb() {
    return concat(
            continually(() -> r.nextInt(10)).take(24),
            Stream.of(2, 5, 2, 1, 0, 0)
    ) 
            .zip(NrbGenerator.WEIGHTS.toStream()) 
            .unzip(t -> of(t._1() * t._2(), "" + t._1())) 
            .map((crcOfDigits, digits) ->
                    of(
                            (98 - (crcOfDigits
                                    .collect(
                                            summarizingInt(Integer::intValue)
                                    )
                                    .getSum() % 97)
                            ) + "",
                            digits.collect(
                                    joining()
                            )
                    )
            ) 
            .map(
                    (crc, val) -> of(
                            crc.length() < 2 ? "0" + crc : crc,
                            val.substring(0, 24)
                    )
            ) 
            .toSeq() 
            .map(Object::toString) 
            .collect(joining());
}

I pamiętajcie, nie zostawiajcie kodu w takim stanie. Po doprowadzeniu do tego etapu, należy kod jeszcze zrefaktoryzować wyciągając poszczególne kroki do osobnych funkcji/metod, tak by uzyskać:

Listing 2. To samo inaczej

public String generateNrb() {
    return concat(
            generateRandomNrbDigits(),
            getPL00Indicator()
    ) 
            .zip(NrbGenerator.WEIGHTS.toStream()) 
            .unzip(t -> of(
                    calculateDigitCrcValue(t), digitToString(t)
                    )
            ) 
            .map(this::crc, this::joinNrbDigits) 
            .map(this::addLeading0ToCrc, this::dropPL00Indicator) 
            .toSeq() 
            .map(Object::toString) 
            .collect(joining());
}

Dużo lepiej 🙂 Choć znając scalowców to by zapisali jako operator w rodzaju 8======|)—