martedì 25 febbraio 2020

Unbreakable

Mi è capitata una discussione  decisamente spiacevole su un tema che oggi dovrebbe essere superato, ma evidentemente non è così: la laicità dell'uso dell'istruzione break per uscire da un ciclo for in Java.

Vediamo un esempio banale di ricerca su un array con uscita quando si trova l'elemento cercato

package com.byteliberi;
import java.util.List;
import java.util.Arrays;

public class Main {

public static void main(String[] args) {
String[] poliArr = {"triangolo", "quadrato", "pentagono", "esagono"};
List<String> poligoni = Arrays.asList(poliArr);
String trovato = "";
for (String poligono : poligoni) {
if ("quadrato".equals(poligono)) {
trovato = poligono;
break;
}
}
System.out.println(trovato);
}
}

L'output è la parola: quadrato

Il codice in esempio non è e non vuole essere un esempio stilistico di come si cerca un elemento in una lista in Java,  però è un modo pratico e veloce per cercare un elemento e uscire una volta trovato.
L'istruzione break, il cui uso in Java è legittimo e non si ottiene alcun warning usandola, provoca un "salto" fuori dal for. C'è chi non accetta questa soluzione e si aspetta da un professionista l'uso del più elegante ciclo while.

Ora, concordo che i salti non siano il massimo dell'eleganza e che possano portare a codice difficile da leggere se si esagera e si annida l'uso dell'istruzione break con l'istruzione continue, però sono soluzioni pratiche. Esistono perché credo che derivino dal linguaggio C, ma sono pericolose per la leggibilità se ne si abusa, in pratica non creano problemi di affidabilità del codice.

L'uso del while ha un piccolo inconveniente, che lo rende meno pratico, vediamo la nuova versione:

package com.byteliberi;
import java.util.List;
import java.util.ListIterator;
import java.util.Arrays;

public class Main {

public static void main(String[] args) {
String[] poliArr = {"triangolo", "quadrato", "pentagono", "esagono"};
List<String> poligoni = Arrays.asList(poliArr);
String trovato = "";
ListIterator<String> iterator = poligoni.listIterator();
while (iterator.hasNext() && trovato.isEmpty()) {
String poligono = iterator.next();
if ("quadrato".equals(poligono)) {
trovato = poligono;
}
}
System.out.println(trovato);
}

}

Accedere ad un elemento di una lista con il metodo get e l'indice della posizione è efficiente soltanto con gli ArrayList e non con le LinkedList. Non sappiamo e non dobbiamo preoccuparci di quale implementazione sia ritornata da asList, quindi si usa un iteratore. Ora un codice del genere francamente non mi sembra più leggibile di prima, invece ho dovuto introdurre una riga in più ed esplicitare un controllo in più nell'if (c'è un and con due condizioni).

Vediamo ora cosa esiste in Java da qualche versione, le famose lambda che a mio parare (e non solo mio) sono molto eleganti.

package com.byteliberi;
import java.util.Arrays;
import java.util.List;

public class Main {

public static void main(String[] args) {
String[] poliArr = {"triangolo", "quadrato", "pentagono", "esagono"};
List<String> poligoni = Arrays.asList(poliArr);
String trovato = poligoni.stream()
.filter(poligono -> "quadrato".equals(poligono))
.findFirst().orElse(null);
assert(trovato != null);
System.out.println(trovato);
}
}

In questo modo non si deve neppure ricorrere all'uso di un ciclo e si sfrutta una "novità" (anche se è comparsa ormai diversi anni fa, con Java 8) che è la lambda. Questa mi sembra di gran lunga la strada più elegante, ma non la più comoda, confesso che trovo quel break semplicemente comodo.

Oggi credo che discussioni su se sia meglio usare un for o un while lascino il tempo che trovano e che i programmatori che discutono, per non dire litigano su queste cose probabilmente hanno una visione troppo ristretta e manierista del mestiere. Credo che gli elementi migliori debbano avere una visione d'insieme e che questi dettagli implementativi non valgano la pena di essere indagati ulteriormente, se non in sede accademica. Credo inoltre che si lavori assieme e si possa lavorare assieme introducendo un elemento di tolleranza verso lo stile degli altri e di rispetto delle conclusioni degli altri, anche quando non coincidono con le nostre. Lavorare assieme è molto difficile perché gli elementi che entrano in gioco non sono soltanto di natura tecnica, si parla sempre di più di competenze trasversali e discutere su un for o un while in azienda è qualcosa che non dovrebbe succedere.

Java mi piace più di altri linguaggi, anche se oggi non è più il mio solo linguaggio di riferimento, perché è molto potente e permette di superare questi vecchie, inutili diatribe.