Liigu peamise sisu juurde

Erindite loomine

Sissejuhatus

Javasse sisseehitatud erindid katavad paljusid levinud olukordi, kuid need on sageli liiga üldised, et kirjeldada, mis teie konkreetses domeenis valesti läks. Oma erindite loomine muudab vead tähendusrikkamaks ja võimaldab neid täpsemalt käsitleda.

Enda erindi defineerimine

Programmeerija enda poolt loodud erind on lihtsalt klass, mis laiendab kas Exception (kontrollitud erind) või RuntimeException (kontrollimata erind) klassi. See klass minimaalselt peab koosnema konstruktorist, mis edastab veateate edasi ülemklassile. Näiteks:

// Checked exception
public class InsufficientFundsException extends Exception {
public InsufficientFundsException(String message) {
super(message);
}
}

// Unchecked exception
public class InvalidProductException extends RuntimeException {
public InvalidProductException(String message) {
super(message);
}
}

Neid erindeid saab visata ja püüda nagu igat teist:

public void withdraw(double amount) throws InsufficientFundsException {
if (amount > balance) {
throw new InsufficientFundsException("Not enough funds: requested " + amount);
}
balance -= amount;
}
try {
account.withdraw(1000.0);
} catch (InsufficientFundsException e) {
System.out.println(e.getMessage()); // Not enough funds: requested 1000.0
}

Lisaks on võimalik erinditele välju ja meetodeid ka ise juurde luua. Selle eesmärk on veaga lisainfot edastada, mis võib silumisel või vea käsitlemisel kasuks tulla.

public class InsufficientFundsException extends Exception {
private final double requested;
private final double available;

public InsufficientFundsException(double requested, double available) {
super("Insufficient funds: requested " + requested + ", available " + available);
this.requested = requested;
this.available = available;
}

public double getRequested() {
return requested;
}

public double getAvailable() {
return available;
}
}

Antud juhul tuleks arvestada sellega, et erindid peaksid olema muutumatud. Seetõttu kasutatakse tavaliselt final välju ja neid ei muudeta pärast loomist.

Tekkepõhjuse säilitamine

Erindite tõlkimisel on vajalik tekkepõhjust säilitada. Klassid Exception ja RuntimeException pakuvad nelja konstruktorit, mida oma erindiklass saab edasi pakkuda:

  • ilma argumentideta
  • veateatega
  • tekkepõhjusega
  • veateate ja tekkepõhjusega konstruktor.
public class ServiceException extends RuntimeException {
public ServiceException() {
super();
}

public ServiceException(String message) {
super(message);
}

public ServiceException(Throwable cause) {
super(cause);
}

public ServiceException(String message, Throwable cause) {
super(message, cause);
}
}

Antud juhul kasutatakse viimast, et tekke põhjus edastada uuele erindile:

public class ServiceException extends RuntimeException {
public ServiceException(String message, Throwable cause) {
super(message, cause);
}
}
try {
loadFromDatabase();
} catch (SQLException e) {
throw new ServiceException("Failed to load data", e);
}

Antud näiteks esialgne SQLException säilitatakse ning on kättesaadav getCause() meetodi kaudu. See on kasulik koodi silumiseks.

enum vea põhjusena

Kui üks erind peab esindama mitut erinevat veaolukorda, siis on kasulik kasutada sisemist enum klassi vea põhjuse jaoks. Näiteks:

public class StockException extends Exception {

public enum Reason {
STOCK_IS_FULL,
STOCK_ALREADY_CONTAINS_PRODUCT,
NEGATIVE_PRICE
}

private final Reason reason;

public StockException(Reason reason) {
super(reason.name());
this.reason = reason;
}

public Reason getReason() {
return reason;
}
}

Selle erindi viskamine on puhas ja loetav:

throw new StockException(StockException.Reason.STOCK_IS_FULL);

Ning vea püüdmisel on võimalik näiteks kasutada switch-avaldist, et olukorrale reageerida vastavalt:

try {
stock.addProduct(product);
} catch (StockException e) {
switch (e.getReason()) {
case STOCK_IS_FULL ->
System.out.println("Cannot add product: stock is full");
case STOCK_ALREADY_CONTAINS_PRODUCT ->
System.out.println("Product is already in stock");
case NEGATIVE_PRICE ->
System.out.println("Product has a negative price");
}
}

See on alternatiiv getMessage() kontrollimisele või mitme erineva erindiklassi loomisele.

Nimereeglid

Erindiklassid järgivad Java üldisi klasside nimereegleid koos ühe lisatingimusega:

  • Erindiklassi nimi peab lõppema Exception-iga, näiteks StockException, InsufficientFundsException, ProductRegistrationException.
  • Lisaks nimi peaks probleemi kirjeldama, mitte selle tekkekohta.
  • Väldi liiga üldiseid nimesid nagu ServiceException või ApplicationException, kui võimalik kasutada täpsemat nime.

Millal luua uus erindiklass

Uus erindiklass on õigustatud, kui:

  • vea käsitlemine sõltub vea tüübist
  • viga on osa programmi domeeniloogikast
  • viga peab kandma lisainfot

Ei ole vajalik, kui:

  • olemasolev Java erind kirjeldab olukorda piisavalt täpselt
  • erindit ei käsitleta erinevalt teistest