Supponiamo di avere un flusso di cose e voglio "arricchirle" a metà stream, posso usare peek() per fare ciò, ad esempio:
streamOfThings.peek(this::thingMutator).forEach(this::someConsumer);
Supponiamo che la mutazione delle cose a questo punto nel codice sia un comportamento corretto; ad esempio, il metodo thingMutator può impostare il campo "lastProcessed" sull'ora corrente.
Tuttavia, peek() nella maggior parte dei contesti significa "guarda, ma non toccare".
L'utilizzo di peek() per muta elementi di flusso è antipattern o sconsiderato?
Modifica:
L'approccio alternativo, più convenzionale, sarebbe quello di convertire il consumatore:
private void thingMutator(Thing thing) {
thing.setLastProcessed(System.currentTimeMillis());
}
a una funzione che restituisce il parametro:
private Thing thingMutator(Thing thing) {
thing.setLastProcessed(currentTimeMillis());
return thing;
}
e usa map() invece:
stream.map(this::thingMutator)...
Ma questo introduce il codice superficiale (il return ) e non sono convinto che sia più chiaro, perché sai che peek() restituisce lo stesso oggetto, ma con map() non è nemmeno chiaro a colpo d'occhio che è lo stesso classe di oggetto.
Inoltre, con peek() puoi avere un lambda che muta, ma con map() devi costruire un naufragio del treno. Confronto:
stream.peek(t -> t.setLastProcessed(currentTimeMillis())).forEach(...)
stream.map(t -> {t.setLastProcessed(currentTimeMillis()); return t;}).forEach(...)
Penso che la versione peek() sia più chiara e che la lambda si stia chiaramente mutando, quindi non ci sono effetti collaterali "misteriosi". Allo stesso modo, se viene utilizzato un riferimento al metodo e il nome del metodo implica chiaramente la mutazione, anche questo è chiaro ed evidente.
Su una nota personale, non evito di usare peek() per mutare - lo trovo molto comodo.