En tancar el mòdul 1 vas deixar BiblioTechApp funcionant, però amb un diagnòstic incòmode: no repeteix, no decideix, no valida i no recorda res. Aquest mòdul ataca els tres primers problemes, i aquesta lliçó s'ocupa del més urgent: decidir. Fins ara el teu programa era una línia recta —llegeix, calcula, imprimeix— i qualsevol "decisió" que prenia era en realitat un truc aritmètic o un operador ternari encastat al mig d'una fórmula. Si un empleat tecleja -5 dies transcorreguts, el programa ho acceptava tan tranquil. Si el retard era de 200 dies, imprimia la mateixa frase que si era d'1. Les sentències condicionals són el mecanisme amb què un programa bifurca el seu camí: executa unes instruccions o unes altres segons l'estat de les dades. Sense elles no hi ha regles de negoci, no hi ha validació i no hi ha programari; amb elles, BiblioTechApp comença a comportar-se com una aplicació de debò.

Contingut

  1. Del programa lineal al programa que decideix
  2. L'if simple
  3. El bloc {} i per què ometre'l és perillós
  4. if-else: les dues branques
  5. L'escala else if
  6. Condicions compostes i curtcircuit aplicat
  7. Imbricació, clàusules de guarda i sortida anticipada
  8. if-else enfront de l'operador ternari
  9. Comparar String en condicions i el parany de null
  10. Les regles de negoci de BiblioTech com a taula de decisió
  11. Validar l'entrada de l'usuari amb if
  12. Errors Habituals i Consells
  13. Exercicis

  1. Del programa lineal al programa que decideix

Un programa sense condicionals executa totes les seves instruccions, sempre, en el mateix ordre. Això és el que fa avui BiblioTechApp:

flowchart TD
    A["Llegir dades"] --> B["Calcular retard"]
    B --> C["Calcular multa"]
    C --> D["Imprimir rebut"]

Un programa amb condicionals té camins alternatius. El mateix recorregut, però amb desviaments:

flowchart TD
    A["Llegir dades"] --> V{"Dades valides?"}
    V -- "no" --> E["Avisar de l'error"]
    V -- "si" --> B["Calcular retard"]
    B --> R{"Hi ha retard?"}
    R -- "no" --> P["Rebut sense multa"]
    R -- "si" --> M["Calcular multa"]
    M --> P

La diferència és enorme: el nombre de recorreguts possibles deixa de ser un. I amb això apareix una responsabilitat nova que t'acompanyarà la resta del curs: cada branca que escrius és una branca que cal provar.

  1. L'if simple

La forma més bàsica executa un bloc només si una condició booleana és certa.

if (condicio) {
    // s'executa nomes si condicio es true
}

Punts que has d'interioritzar des del primer dia:

  • La condició va sempre entre parèntesis.
  • La condició ha de ser de tipus boolean. No hi ha conversió implícita: en Java, if (1) no compila (a diferència de C o JavaScript). Això elimina tota una família d'errors.
  • Després del ) no es posa punt i coma. if (x > 0); compila, però l'if queda buit i el bloc següent s'executa sempre.

Aplicat a BiblioTech:

package com.nexussoftware.bibliotech;

public class BiblioTechApp {
    public static void main(String[] args) {

        final int DIES_PRESTEC = 15;
        final double TARIFA_DIARIA = 0.25;

        String titol = "Java Eficac";
        int diesTranscorreguts = 27;

        int diesRetard = diesTranscorreguts - DIES_PRESTEC;   // 12

        System.out.println("Llibre: " + titol);

        if (diesRetard > 0) {
            double multa = diesRetard * TARIFA_DIARIA;
            System.out.printf("Devolucio fora de termini. Multa: %.2f EUR%n", multa);
        }

        System.out.println("Registre completat.");
    }
}

Observa tres coses:

  1. diesRetard > 0 produeix un boolean; aquest és l'únic requisit.
  2. La variable multa està declarada dins del bloc {}, així que només existeix allà dins. Fora de l'if no la pots fer servir: el compilador dirà cannot find symbol. L'àmbit d'una variable és el bloc en què es declara.
  3. "Registre completat." s'imprimeix sempre, hi hagi multa o no: està fora de l'if.

  1. El bloc {} i per què ometre'l és perillós

Java permet que un if governi una sola sentència sense claus:

if (diesRetard > 0)
    System.out.println("Amb retard");

Això compila i funciona. I és una font d'errors tan coneguda que la majoria de les guies d'estil professionals (Google, Oracle, la de gairebé qualsevol empresa) ho prohibeixen. El motiu:

if (diesRetard > 0)
    System.out.println("Amb retard");
    System.out.println("Aplicant multa...");   // SEMPRE s'executa!

El sagnat suggereix que les dues línies depenen de l'if, però Java ignora el sagnat. Només la primera sentència pertany a l'if; la segona s'executa sempre, fins i tot amb diesRetard igual a 0. Aquest error va ser l'origen de la fallada de seguretat "goto fail" d'Apple el 2014, que va afectar milions de dispositius.

Regla del curs: fes servir sempre claus, fins i tot per a una sola línia. El cost són dos caràcters; el benefici és que afegir una línia més no canviarà mai el significat del codi.

  1. if-else: les dues branques

Quan hi ha una alternativa explícita, else recull tots els casos en què la condició és falsa:

if (diesRetard > 0) {
    System.out.println("Devolucio FORA DE TERMINI");
} else {
    System.out.println("Devolucio EN TERMINI");
}

if i else són mútuament excloents i exhaustius: exactament una de les dues branques s'executa, mai cap i mai totes dues. Aquesta garantia és molt valuosa: si necessites que una variable quedi inicialitzada passi el que passi, un if-else t'ho assegura i el compilador ho sap.

String estat;                  // declarada sense valor
if (diesRetard > 0) {
    estat = "AMB RETARD";
} else {
    estat = "EN TERMINI";
}
System.out.println(estat);     // compila: el compilador sap que sempre s'assigna

Si en tragués l'else, el compilador donaria l'error variable estat might not have been initialized, perquè existiria un camí en què la variable arribaria sense valor. El compilador de Java analitza els camins possibles; convé tenir-lo d'aliat.

  1. L'escala else if

Quan els casos són més de dos, s'encadenen condicions. Tècnicament no existeix una paraula clau elseif: el que hi ha és un if dins de l'else de l'anterior, escrit de manera compacta.

if (condicio1) {
    // cas 1
} else if (condicio2) {
    // cas 2
} else if (condicio3) {
    // cas 3
} else {
    // cap dels anteriors
}

Regles de comportament que has de memoritzar:

  • Les condicions s'avaluen de dalt a baix i s'atura a la primera certa.
  • Només s'executa un bloc, encara que diverses condicions siguin certes.
  • L'else final és opcional; si no hi és, és possible que no s'executi cap branca.

Per això l'ordre importa. Aquesta escala està malament:

// INCORRECTE: el primer cas s'empassa tots els altres
if (diesRetard > 0) {
    System.out.println("Retard lleu");
} else if (diesRetard > 7) {
    System.out.println("Retard greu");       // inabastable a la practica
}

Amb diesRetard = 20, la primera condició ja és certa i s'imprimeix "Retard lleu". La segona branca no s'assoleix mai amb valors més grans que 7. La regla pràctica: ordena de la condició més restrictiva a la més general.

// CORRECTE
if (diesRetard > 7) {
    System.out.println("Retard greu");
} else if (diesRetard > 0) {
    System.out.println("Retard lleu");
} else {
    System.out.println("Sense retard");
}

  1. Condicions compostes i curtcircuit aplicat

Al mòdul 1 vas veure &&, ||, ! i el curtcircuit. Aquí els fas servir de debò. Recordatori en una línia: && avalua l'operand dret només si l'esquerre és true; || l'avalua només si l'esquerre és false.

Això converteix l'ordre dels operands en una eina de protecció:

String referencia = null;

// L'ordre salva el programa: si referencia es null, isEmpty() no es crida mai.
if (referencia != null && !referencia.isEmpty()) {
    System.out.println("Referencia: " + referencia);
}

Si escrivissis les condicions a l'inrevés (!referencia.isEmpty() && referencia != null), el programa intentaria cridar un mètode sobre null i petaria. Aquesta protecció té nom propi: guarda de nul·litat.

El mateix principi aplicat a BiblioTech, on un préstec només es considera "recuperable amb avís" si hi ha retard però encara no s'ha arribat al sostre:

double multa = diesRetard * TARIFA_DIARIA;

if (diesRetard > 0 && multa < MULTA_MAXIMA) {
    System.out.println("Multa parcial: encara no s'ha assolit el sostre.");
}

if (diesRetard == 0 || diesTranscorreguts <= DIES_PRESTEC) {
    System.out.println("Prestec tancat sense incidencies.");
}

I ! per negar, amb un consell: una negació és llegible, dues són un enigma. En lloc de if (!(diesRetard <= 0)), escriu if (diesRetard > 0).

Escrit així Millor així Motiu
if (!(a > b)) if (a <= b) La negació d'una comparació és una altra comparació
if (!(a && b)) if (!a || !b) Llei de De Morgan; sovint encara és més clar reformular el domini
if (esValid == true) if (esValid) Comparar un booleà amb true és redundant
if (esValid == false) if (!esValid) Ídem

  1. Imbricació, clàusules de guarda i sortida anticipada

Imbricar condicionals és posar un if dins d'un altre. Funciona, però degenera de pressa en l'anomenada "fletxa de codi" (arrow code), en què la lògica es desplaça cap a la dreta i esdevé il·legible:

// Dificil de seguir: cal recordar tres condicions alhora
if (empleat != null) {
    if (!empleat.isEmpty()) {
        if (diesTranscorreguts >= 0) {
            if (diesTranscorreguts > DIES_PRESTEC) {
                System.out.println("Processar multa");
            }
        }
    }
}

La tècnica que ho arregla s'anomena clàusula de guarda: en lloc d'imbricar el cas bo, es descarten primer els casos dolents i se surt. Com que en aquest mòdul tot el codi viu al main i encara no has vist mètodes (arriben a la lliçó 03-03), la sortida es fa amb return;, que dins del main acaba el programa.

if (empleat == null || empleat.isEmpty()) {
    System.err.println("ERROR: el nom de l'empleat es obligatori.");
    return;                       // acaba main
}
if (diesTranscorreguts < 0) {
    System.err.println("ERROR: els dies transcorreguts no poden ser negatius.");
    return;
}

// A partir d'aqui, tot el que segueix es el "cami felic", sense sagnat extra.
if (diesTranscorreguts > DIES_PRESTEC) {
    System.out.println("Processar multa");
}

Compara tots dos estils:

Aspecte Imbricació profunda Clàusules de guarda
Sagnat Creix amb cada condició Es manté pla
Càrrega mental Cal sostenir totes les condicions al cap Cada guarda s'oblida tan bon punt es passa
Missatges d'error Difícils d'associar a la seva causa Un per guarda, específic
Camí principal Enterrat al fons Visible al final, sense soroll

Quan al mòdul 3 aprenguis a escriure mètodes, aquest patró serà encara més natural: cada guarda farà return del mètode sense avortar el programa sencer. La idea, però, és la mateixa i convé adoptar-la ja.

  1. if-else enfront de l'operador ternari

Ja vas fer servir el ternari condicio ? valorSiTrue : valorSiFalse al mòdul 1 per saturar el retard a zero. Convé fixar quan cal fer servir cada eina, perquè són intercanviables només en aparença.

El ternari és una expressió: produeix un valor. L'if és una sentència: executa accions.

// Ternari: triar un VALOR. Correcte i llegible.
int diesRetard = (diesTranscorreguts > DIES_PRESTEC) ? diesTranscorreguts - DIES_PRESTEC : 0;
String estat = (diesRetard > 0) ? "AMB RETARD" : "EN TERMINI";

// if-else: executar ACCIONS. El ternari aqui no serviria.
if (diesRetard > 0) {
    System.out.println("Notificant a l'empleat...");
    System.out.printf("Import: %.2f EUR%n", multa);
} else {
    System.out.println("Res a cobrar.");
}
Criteri Fes servir ternari Fes servir if-else
Objectiu Obtenir un valor Executar instruccions
Nombre de branques Exactament 2 2 o més
Longitud Cap folgadament en una línia Diverses sentències per branca
Imbricació Mai més d'un Sense problema
Inicialitzar una variable final Ideal Possible, però més verbós

El límit pràctic és a la imbricació. Això és vàlid però indefensable en una revisió de codi:

// ILLEGIBLE: no ho facis
String gravetat = diesRetard == 0 ? "CAP" : diesRetard <= LLINDAR_LLEU ? "LLEU" : "GREU";

La mateixa lògica amb una escala es llegeix sense esforç, i és el que faràs servir a BiblioTech:

String gravetat;
if (diesRetard == 0) {
    gravetat = "CAP";
} else if (diesRetard <= LLINDAR_LLEU) {
    gravetat = "LLEU";
} else {
    gravetat = "GREU";
}

  1. Comparar String en condicions i el parany de null

Aquí hi ha l'error número u de qui ve d'altres llenguatges. En Java, == sobre objectes compara referències (si són el mateix objecte en memòria), no contingut. Amb String funciona de vegades —pel pool de literals que vas veure al mòdul 1— i falla justament quan el text ve de l'usuari, que és sempre.

String opcio = scanner.nextLine();      // l'usuari escriu: sortir

if (opcio == "sortir") {                // MALAMENT: gairebe sempre false
    System.out.println("Adeu");
}

if (opcio.equals("sortir")) {           // BE: compara contingut
    System.out.println("Adeu");
}

Mètodes útils en condicions:

Mètode Què comprova Exemple true
a.equals(b) Contingut idèntic, sensible a majúscules "sortir".equals("sortir")
a.equalsIgnoreCase(b) Contingut idèntic ignorant majúscules "Sortir".equalsIgnoreCase("SORTIR")
a.isEmpty() Longitud zero "".isEmpty()
a.isBlank() Buit o només espais (Java 11+) " ".isBlank()
a.startsWith(b) Comença per aquest text "978-0000000001".startsWith("978")
a.contains(b) Conté aquest text "Java Eficac".contains("Java")

Per a entrades d'usuari, equalsIgnoreCase combinat amb trim() evita queixes absurdes:

String resposta = scanner.nextLine().trim();

if (resposta.equalsIgnoreCase("si")) {
    System.out.println("Confirmat.");
}

El parany de null. Si una variable String val null, cridar qualsevol mètode sobre ella atura el programa amb un NullPointerException (veuràs com gestionar-lo al mòdul 6; de moment, evita'l). La tècnica estàndard té dues formes:

String referencia = null;

// Opcio A: guarda explicita amb curtcircuit
if (referencia != null && referencia.equals("REF-001")) { ... }

// Opcio B (mes elegant): el literal a l'esquerra, que mai no es null
if ("REF-001".equals(referencia)) { ... }         // false, sense petar

L'opció B es coneix com a comparació de Yoda. "REF-001" és un literal, mai no és null, i equals retorna false netament quan rep null com a argument. Fes-la servir quan el valor pugui ser nul i no vulguis escriure la guarda.

  1. Les regles de negoci de BiblioTech com a taula de decisió

Abans d'escriure condicionals convé escriure la taula de decisió: quins casos existeixen, com es distingeixen i què fa cadascun. Si la taula està bé, el codi surt sol.

Regles de la biblioteca de Nexus Software (constants ja fixades al mòdul 1: DIES_PRESTEC = 15, TARIFA_DIARIA = 0.25, MULTA_MAXIMA = 20.0, LLINDAR_LLEU = 7):

Cas Condició estat gravetat Multa
1. En termini diesRetard == 0 EN TERMINI CAP 0,00 €
2. Retard lleu diesRetard <= LLINDAR_LLEU AMB RETARD LLEU diesRetard * 0,25
3. Retard greu diesRetard > LLINDAR_LLEU i multa < sostre AMB RETARD GREU diesRetard * 0,25
4. Multa amb sostre multa calculada >= MULTA_MAXIMA AMB RETARD MAXIMA 20,00 €

Fixa't que el cas 4 travessa els casos 2 i 3: no depèn dels dies sinó de l'import. Per això es comprova després de calcular la multa, no abans. El flux complet:

flowchart TD
    A["diesRetard = max(0, diesTranscorreguts - DIES_PRESTEC)"] --> B{"diesRetard == 0"}
    B -- "si" --> C["estat = EN TERMINI<br/>gravetat = CAP<br/>multa = 0"]
    B -- "no" --> D["multa = diesRetard * TARIFA_DIARIA"]
    D --> E{"multa >= MULTA_MAXIMA"}
    E -- "si" --> F["multa = MULTA_MAXIMA<br/>gravetat = MAXIMA"]
    E -- "no" --> G{"diesRetard <= LLINDAR_LLEU"}
    G -- "si" --> H["gravetat = LLEU"]
    G -- "no" --> I["gravetat = GREU"]
    C --> Z["Imprimir rebut"]
    F --> Z
    H --> Z
    I --> Z

I la traducció directa a codi, substituint els ternaris imbricats del mòdul 1:

package com.nexussoftware.bibliotech;

public class BiblioTechApp {
    public static void main(String[] args) {

        final int DIES_PRESTEC = 15;
        final double TARIFA_DIARIA = 0.25;
        final double MULTA_MAXIMA = 20.0;
        final int LLINDAR_LLEU = 7;

        String empleat = "Diego Alonso";
        String titol = "Patrons de Disseny";
        String isbn = "978-0000000002";
        int diesTranscorreguts = 120;

        // Retard saturat a zero: mai negatiu.
        int diesRetard = diesTranscorreguts - DIES_PRESTEC;
        if (diesRetard < 0) {
            diesRetard = 0;
        }

        String estat;
        String gravetat;
        double multa;

        if (diesRetard == 0) {
            estat = "EN TERMINI";
            gravetat = "CAP";
            multa = 0.0;
        } else {
            estat = "AMB RETARD";
            multa = diesRetard * TARIFA_DIARIA;

            if (multa >= MULTA_MAXIMA) {
                multa = MULTA_MAXIMA;           // s'aplica el sostre
                gravetat = "MAXIMA";
            } else if (diesRetard <= LLINDAR_LLEU) {
                gravetat = "LLEU";
            } else {
                gravetat = "GREU";
            }
        }

        System.out.println("=== BiblioTech - Nexus Software ===");
        System.out.printf("%-14s %s%n", "Empleat:", empleat);
        System.out.printf("%-14s %s (%s)%n", "Llibre:", titol, isbn);
        System.out.printf("%-14s %d%n", "Dies retard:", diesRetard);
        System.out.printf("%-14s %s / %s%n", "Estat:", estat, gravetat);
        System.out.printf("%-14s %.2f EUR%n", "Multa:", multa);

        // Avis addicional: nomes quan la multa ha arribat al sostre.
        if (gravetat.equals("MAXIMA")) {
            System.out.println();
            System.out.println("AVIS: multa amb sostre aplicat. Notificar a Recursos Humans.");
        }
    }
}

Sortida amb diesTranscorreguts = 120 (105 dies de retard, multa teòrica 26,25 €):

=== BiblioTech - Nexus Software ===
Empleat:       Diego Alonso
Llibre:        Patrons de Disseny (978-0000000002)
Dies retard:   105
Estat:         AMB RETARD / MAXIMA
Multa:         20,00 EUR

AVIS: multa amb sostre aplicat. Notificar a Recursos Humans.

Un detall de disseny que val la pena assenyalar: les tres variables estat, gravetat i multa es declaren abans de l'if i s'assignen a dins. Com que totes les branques acaben assignant-les, el compilador accepta fer-les servir després. Si eliminessis l'else final, deixaria de compilar. És el compilador verificant la teva taula de decisió.

  1. Validar l'entrada de l'usuari amb if

El programa del mòdul 1 acceptava qualsevol cosa. Ara ja pots rebutjar el que no té sentit. L'estratègia, amb clàusules de guarda, és llegir i comprovar immediatament:

package com.nexussoftware.bibliotech;

import java.util.Scanner;

public class BiblioTechApp {
    public static void main(String[] args) {

        final int DIES_PRESTEC = 15;
        Scanner scanner = new Scanner(System.in);

        System.out.print("Empleat: ");
        String empleat = scanner.nextLine().trim();

        if (empleat.isBlank()) {
            System.err.println("ERROR: el nom de l'empleat no pot estar buit.");
            scanner.close();
            return;
        }

        System.out.print("Titol del llibre: ");
        String titol = scanner.nextLine().trim();

        if (titol.isBlank()) {
            System.err.println("ERROR: el titol no pot estar buit.");
            scanner.close();
            return;
        }

        System.out.print("ISBN: ");
        String isbn = scanner.nextLine().trim();

        if (!isbn.startsWith("978")) {
            System.err.println("ERROR: l'ISBN de la biblioteca ha de comencar per 978.");
            scanner.close();
            return;
        }

        System.out.print("Dies transcorreguts: ");
        String entradaDies = scanner.nextLine().trim();
        int diesTranscorreguts = Integer.parseInt(entradaDies);

        if (diesTranscorreguts < 0) {
            System.err.println("ERROR: els dies transcorreguts no poden ser negatius.");
            scanner.close();
            return;
        }
        if (diesTranscorreguts > 3650) {
            System.err.println("ERROR: valor inversemblant (mes de 10 anys).");
            scanner.close();
            return;
        }

        System.out.println("Entrada validada. Processant devolucio de " + empleat + "...");
        int diesRetard = diesTranscorreguts - DIES_PRESTEC;
        if (diesRetard < 0) {
            diesRetard = 0;
        }
        System.out.println("Dies de retard: " + diesRetard);

        scanner.close();
    }
}

Repassa el que acabes d'aconseguir: el programa ja no accepta dies negatius, ni noms buits, ni ISBN d'una altra biblioteca. I atura't en la limitació evident: en detectar un error, el programa es rendeix i acaba. El raonable seria tornar a demanar la dada. Això requereix repetir, i repetir és exactament el que fan els bucles de la lliçó següent.

Queda un forat que encara no pots tapar: si l'usuari escriu vint en lloc de 20, Integer.parseInt no falla amb un if, sinó que atura el programa amb un NumberFormatException. Cap condició no ho pot evitar, perquè l'error passa dins de la conversió. Aquest buit el tanca el mòdul 6, amb excepcions. De moment, assumeix que l'usuari teclegi dígits.

Errors Habituals i Consells

1. El punt i coma fantasma.

if (diesRetard > 0);             // <-- aquest ';' tanca l'if
{
    System.out.println("Amb retard");    // s'executa SEMPRE
}

Compila sense avisos i és endimoniadament difícil de veure. Cap IDE modern no ho deixa passar sense subratllar: fes-li cas.

2. Confondre = amb ==.

boolean disponible = false;
if (disponible = true) { ... }   // ASSIGNA true i despres avalua true: sempre hi entra

Amb booleans compila i produeix un error silenciós. Amb altres tipus (if (x = 5)) no compila, perquè int no és boolean. Escriu if (disponible) a seques i el problema desapareix.

3. Comparar String amb ==. Ja ho has vist: fes servir equals o equalsIgnoreCase. Sempre.

4. Comparar double amb ==. Per la precisió binària que vas veure al mòdul 1, 0.1 + 0.2 == 0.3 és false. Per a imports, compara amb una tolerància o —millor— compara els enters dels quals deriven:

if (Math.abs(multa - MULTA_MAXIMA) < 0.001) { ... }   // "practicament iguals"

5. Escales amb condicions solapades i mal ordenades. Recorda: del més restrictiu al més general. I verifica que cada branca és assolible amb alguna dada real.

6. Oblidar l'else final. Si les teves branques assignen una variable, sense else pot quedar sense assignar. El compilador t'avisarà; no el silenciïs inicialitzant a un valor fals com "" o -1 sense pensar-t'ho, perquè llavors l'error passa a l'execució.

7. Consell d'estil: extreu les condicions complexes a variables booleanes amb nom.

// Abans: cal desxifrar-ho
if (diesRetard > LLINDAR_LLEU && multa < MULTA_MAXIMA && !empleat.isBlank()) { ... }

// Despres: es llegeix com una frase
boolean esRetardGreu   = diesRetard > LLINDAR_LLEU;
boolean multaSotaSostre = multa < MULTA_MAXIMA;
boolean empleatValid   = !empleat.isBlank();

if (esRetardGreu && multaSotaSostre && empleatValid) { ... }

No costa rendiment apreciable i el codi es documenta sol. A més, en depurar (lliçó 02-05) veuràs cada condició per separat.

8. Consell: prova els límits. Si la teva condició és diesRetard <= LLINDAR_LLEU, prova amb 6, 7 i 8. Els errors de condicionals viuen gairebé sempre a la vora exacta.

Exercicis

Exercici 1: classificador de disponibilitat

Escriu un programa que, donades les variables titol, disponible (boolean) i diesRestants (int, dies que falten perquè venci el préstec en curs; val 0 si el llibre és a la prestatgeria), imprimeixi un d'aquests missatges:

  • Si disponible és true: "Java Eficac: DISPONIBLE per a prestec immediat".
  • Si no està disponible i diesRestants és més gran que 0: "Java Eficac: PRESTAT, torna d'aqui a N dies".
  • Si no està disponible i diesRestants és 0 o negatiu: "Java Eficac: PRESTAT I VENCUT, reclamar a l'empleat".

Fes servir una escala else if i claus sempre. Prova-ho amb els tres casos.

Exercici 2: validació d'un préstec nou

Un empleat es vol endur un llibre. La biblioteca de Nexus Software només autoritza el préstec si es compleix tot això:

  • El nom de l'empleat no està en blanc.
  • El llibre està disponible (disponible == true).
  • L'empleat no té multes pendents (multaPendent == 0.0).
  • El nombre de préstecs actius de l'empleat (prestecsActius) és menor que 3.

Escriu el programa amb clàusules de guarda: cada incompliment imprimeix a System.err un missatge específic i acaba amb return. Si tot es compleix, imprimeix l'autorització amb la data de devolució expressada com a "Retornar en 15 dies" fent servir la constant DIES_PRESTEC.

Prova-ho amb Nuria Vidal, disponible true, multa 0.0 i 1 préstec actiu (ha d'autoritzar), i amb Diego Alonso, disponible true, multa 4.50 i 0 préstecs (ha de rebutjar).

Exercici 3: comparació segura de referències

Donades dues variables String referencia i String referenciaCercada, qualsevol de les quals pot valer null, escriu la lògica que imprimeixi:

  • "COINCIDEIX" si totes dues tenen el mateix contingut ignorant majúscules.
  • "SENSE REFERENCIA" si alguna de les dues és null o està en blanc.
  • "NO COINCIDEIX" en qualsevol altre cas.

El programa no es pot aturar amb un NullPointerException en cap combinació possible. Prova, com a mínim, aquestes quatre: ("REF-001", "ref-001"), ("REF-001", "REF-002"), (null, "REF-001"), ("REF-001", null).

Solucions

Solució 1

package com.nexussoftware.bibliotech;

public class BiblioTechApp {
    public static void main(String[] args) {

        String titol = "Java Eficac";
        boolean disponible = false;
        int diesRestants = 4;

        // Escala ordenada: primer el cas mes especific (disponible),
        // despres els dos subcasos de "no disponible".
        if (disponible) {
            System.out.println(titol + ": DISPONIBLE per a prestec immediat");
        } else if (diesRestants > 0) {
            System.out.println(titol + ": PRESTAT, torna d'aqui a " + diesRestants + " dies");
        } else {
            System.out.println(titol + ": PRESTAT I VENCUT, reclamar a l'empleat");
        }
    }
}

Sortida amb els valors de l'exemple: Java Eficac: PRESTAT, torna d'aqui a 4 dies.

Prova els altres dos casos canviant disponible a true i, després, deixant disponible = false amb diesRestants = 0. Observa que no cal escriure else if (!disponible && diesRestants > 0): en ser dins de l'else, ja sabem que disponible és false. Repetir la condició seria soroll.

Solució 2

package com.nexussoftware.bibliotech;

public class BiblioTechApp {
    public static void main(String[] args) {

        final int DIES_PRESTEC = 15;
        final int MAX_PRESTECS = 3;

        String empleat = "Nuria Vidal";
        String titol = "Refactoritzacio";
        String isbn = "978-0000000003";
        boolean disponible = true;
        double multaPendent = 0.0;
        int prestecsActius = 1;

        // --- Clausules de guarda: es descarten primer els casos invalids ---

        if (empleat == null || empleat.isBlank()) {
            System.err.println("REBUTJAT: el nom de l'empleat es obligatori.");
            return;
        }

        if (!disponible) {
            System.err.println("REBUTJAT: '" + titol + "' no esta disponible.");
            return;
        }

        // Comparacio de double amb tolerancia en lloc de != 0.0
        if (multaPendent > 0.001) {
            System.err.printf("REBUTJAT: %s te %.2f EUR de multes pendents.%n",
                              empleat, multaPendent);
            return;
        }

        if (prestecsActius >= MAX_PRESTECS) {
            System.err.printf("REBUTJAT: %s ja te %d prestecs actius (maxim %d).%n",
                              empleat, prestecsActius, MAX_PRESTECS);
            return;
        }

        // --- Cami felic: sense sagnat extra, sense condicions pendents ---

        System.out.println("=== PRESTEC AUTORITZAT ===");
        System.out.printf("%-12s %s%n", "Empleat:", empleat);
        System.out.printf("%-12s %s (%s)%n", "Llibre:", titol, isbn);
        System.out.printf("%-12s Retornar en %d dies%n", "Termini:", DIES_PRESTEC);
        System.out.printf("%-12s %d de %d%n", "Prestecs:", prestecsActius + 1, MAX_PRESTECS);
    }
}

Sortida amb Nuria Vidal:

=== PRESTEC AUTORITZAT ===
Empleat:     Nuria Vidal
Llibre:      Refactoritzacio (978-0000000003)
Termini:     Retornar en 15 dies
Prestecs:    2 de 3

Amb empleat = "Diego Alonso" i multaPendent = 4.50, la tercera guarda es dispara i el programa imprimeix a System.err:

REBUTJAT: Diego Alonso te 4,50 EUR de multes pendents.

L'important de l'exercici: quatre condicions i zero imbricació. Cada guarda és independent, té el seu missatge propi i es pot afegir o treure sense tocar les altres.

Solució 3

package com.nexussoftware.bibliotech;

public class BiblioTechApp {
    public static void main(String[] args) {

        String referencia = "REF-001";
        String referenciaCercada = null;

        // Guarda de nullitat amb curtcircuit: si la primera part de cada
        // '||' es certa, isBlank() no arriba a cridar-se mai sobre null.
        boolean faltaAlguna = referencia == null || referencia.isBlank()
                           || referenciaCercada == null || referenciaCercada.isBlank();

        if (faltaAlguna) {
            System.out.println("SENSE REFERENCIA");
        } else if (referencia.equalsIgnoreCase(referenciaCercada)) {
            System.out.println("COINCIDEIX");
        } else {
            System.out.println("NO COINCIDEIX");
        }
    }
}

Resultats de les quatre combinacions demanades:

referencia referenciaCercada Sortida
"REF-001" "ref-001" COINCIDEIX
"REF-001" "REF-002" NO COINCIDEIX
null "REF-001" SENSE REFERENCIA
"REF-001" null SENSE REFERENCIA

Claus de la solució:

  1. La condició composta s'extreu a faltaAlguna, un booleà amb nom. Es llegeix millor i, en depurar, en podràs inspeccionar el valor d'un cop d'ull.
  2. L'ordre dins de cada || és crític: referencia == null va abans que referencia.isBlank(). Si els intercanviessis, el programa petaria amb null.
  3. Quan s'arriba a l'else if, totes dues variables estan garantides com a no nul·les, així que equalsIgnoreCase és segur. Si l'escala estigués a l'inrevés, no ho seria.

Conclusió

El teu programa ja decideix. Has vist l'if simple i per què les claus no s'han d'ometre mai; l'if-else i la garantia d'exhaustivitat que dona al compilador; l'escala else if, en què l'ordre de les condicions és la lògica; les condicions compostes amb curtcircuit fetes servir com a guardes de nul·litat; les clàusules de guarda com a antídot contra la imbricació; el repartiment de papers entre el ternari (triar un valor) i l'if-else (executar accions); la comparació correcta de String amb equals i equalsIgnoreCase, inclosa la comparació de Yoda per sobreviure a null; i, sobretot, l'hàbit d'escriure primer la taula de decisió i després el codi, que és el que ha convertit les quatre regles de negoci de BiblioTech en una escala neta i verificable.

BiblioTechApp classifica correctament els quatre casos de multa, avisa quan s'assoleix el sostre i rebutja les entrades absurdes. Però es rendeix a la primera: quan detecta una dada invàlida, acaba en lloc de tornar a preguntar, i continua processant una sola devolució per execució.

A la lliçó següent, Bucles, trencaràs aquesta barrera. Amb while, do-while i for el teu programa podrà repetir un bloc tantes vegades com calgui: reintentar la lectura fins que la dada sigui vàlida, recórrer tot el catàleg de la biblioteca i acumular les multes d'una tanda sencera de devolucions sense duplicar ni una sola línia de codi.

Curs de Programació en Java

Mòdul 1: Introducció a Java

Mòdul 2: Flux de control

Mòdul 3: Programació orientada a objectes

Mòdul 4: Programació orientada a objectes avançada

Mòdul 5: Estructures de dades i col·leccions

Mòdul 6: Gestió d'excepcions

Mòdul 7: Entrada/sortida de fitxers

Mòdul 8: Multifil i concurrència

Mòdul 9: Xarxes

Mòdul 10: Temes avançats

Mòdul 11: Frameworks i llibreries de Java

Mòdul 12: Construcció d'aplicacions del món real

© Copyright 2026. Tots els drets reservats