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
- Del programa lineal al programa que decideix
- L'
ifsimple - El bloc
{}i per què ometre'l és perillós if-else: les dues branques- L'escala
else if - Condicions compostes i curtcircuit aplicat
- Imbricació, clàusules de guarda i sortida anticipada
if-elseenfront de l'operador ternari- Comparar
Stringen condicions i el parany denull - Les regles de negoci de BiblioTech com a taula de decisió
- Validar l'entrada de l'usuari amb
if - Errors Habituals i Consells
- Exercicis
- 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.
- L'
if simple
if simpleLa forma més bàsica executa un bloc només si una condició booleana és certa.
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'ifqueda 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:
diesRetard > 0produeix unboolean; aquest és l'únic requisit.- La variable
multaestà declarada dins del bloc{}, així que només existeix allà dins. Fora de l'ifno la pots fer servir: el compilador diràcannot find symbol. L'àmbit d'una variable és el bloc en què es declara. "Registre completat."s'imprimeix sempre, hi hagi multa o no: està fora de l'if.
- El bloc
{} i per què ometre'l és perillós
{} i per què ometre'l és perillósJava permet que un if governi una sola sentència sense claus:
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.
if-else: les dues branques
if-else: les dues branquesQuan 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'assignaSi 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.
- L'escala
else if
else ifQuan 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'
elsefinal é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");
}
- 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 |
- 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.
if-else enfront de l'operador ternari
if-else enfront de l'operador ternariJa 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";
}
- Comparar
String en condicions i el parany de null
String en condicions i el parany de nullAquí 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 petarL'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.
- 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ó.
- Validar l'entrada de l'usuari amb
if
ifEl 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 entraAmb 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:
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éstrue:"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 ésnullo 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:
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ó:
- 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. - L'ordre dins de cada
||és crític:referencia == nullva abans quereferencia.isBlank(). Si els intercanviessis, el programa petaria ambnull. - Quan s'arriba a l'
else if, totes dues variables estan garantides com a no nul·les, així queequalsIgnoreCaseé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
- Introducció a Java
- Configuració de l'entorn de desenvolupament
- Sintaxi i estructura bàsica
- Variables i tipus de dades
- Operadors
- Entrada i sortida per consola
- El teu primer programa complet: BiblioTech
Mòdul 2: Flux de control
- Sentències condicionals
- Bucles
- Sentències switch
- Break i continue
- Depuració i traces d'execució
- Projecte: menú interactiu de BiblioTech
Mòdul 3: Programació orientada a objectes
- Introducció a la POO
- Classes i objectes
- Mètodes
- Constructors
- Herència
- Polimorfisme
- Encapsulament
- Abstracció
- La classe Object: equals, hashCode i toString
Mòdul 4: Programació orientada a objectes avançada
- Interfícies
- Classes abstractes
- Classes internes
- Classes anònimes
- Expressions lambda
- Interfícies funcionals i referències a mètodes
- Enumeracions i registres
Mòdul 5: Estructures de dades i col·leccions
- Arrays
- El framework de col·leccions
- ArrayList
- LinkedList
- HashMap
- HashSet
- Cua i Deque
- Pila
- Ordenació i cerca en col·leccions
Mòdul 6: Gestió d'excepcions
- Introducció a les excepcions
- Bloc try-catch
- Throw i throws
- Excepcions personalitzades
- Bloc finally
- Try-with-resources i AutoCloseable
- Estratègies de gestió d'errors i logging
Mòdul 7: Entrada/sortida de fitxers
- Lectura de fitxers
- Escriptura de fitxers
- Fluxos de fitxers
- BufferedReader i BufferedWriter
- Serialització
- L'API NIO.2: Path i Files
- Formats d'intercanvi: CSV i Properties
Mòdul 8: Multifil i concurrència
- Introducció al multifil
- Creació de fils
- Cicle de vida d'un fil
- Sincronització
- Utilitats de concurrència
- Col·leccions concurrents i variables atòmiques
- Tasques asíncrones amb CompletableFuture
Mòdul 9: Xarxes
- Introducció a les xarxes
- Sockets
- ServerSocket
- DatagramSocket i DatagramPacket
- URL i HttpURLConnection
- El client HTTP modern
Mòdul 10: Temes avançats
- Genèrics
- Anotacions
- Reflexió
- Característiques de Java 8: Streams i Optional
- Dates i hores amb java.time
- Java 9 i més enllà
- Memòria, recol·lecció de brossa i rendiment
Mòdul 11: Frameworks i llibreries de Java
- Introducció als frameworks de Java
- Spring Framework
- Hibernate
- JUnit
- Maven
- Proves avançades amb Mockito
- Llibreries essencials de l'ecosistema
