Effective Java's own interpretation. I tried to interpret item 70 of the 3rd edition by writing my own code.
"Recoverable" means that the current thread cannot be stopped even if an exception occurs, and the state can be returned to another control. Since the method implementer is aware that an exception may occur, it outputs a checked exception and forces the method caller to handle it. A "programming error" is one in which the method is used in a way that the implementer prohibits and the current thread needs to be stopped. Print a run-time exception (which should generally not be caught).
Consider the following service layer method.
--Purchase a book with the specified ID --The reference table is the "book", "bank account", and "purchased book" table. --If you have enough money to buy books in your "bank account", add books to your "purchased books" and reduce your "bank account" balance. --If there is no amount in your "bank account" to buy books, an out-of-balance exception will be output.
** Book purchase method **
/**
*Purchase a book with the specified ID
*Add purchased books to the purchased books table to reduce your balance
*Output an exception if the balance is insufficient
*
* @param bookId Book ID to be purchased
* @param userId user ID
* @throws InsufficientFundsException Insufficient balance exception
*/
public void buyBook(String bookId, int userId) throws InsufficientFundsException {
//Get the price of the book you want to buy
Book selectedBook = bookDao.selectByBookId(bookId);
int bookPrice = selectedBook.getPrice();
//Get the balance
BankAccount myBankAccount = bankAccountDao.selectByUserId(userId);
int myBalance = myBankAccount.getBalance();
//Output an exception when the balance is insufficient
if (bookPrice > myBalance) {
int shortage = bookPrice - myBalance;
throw new InsufficientFundsException("The balance is insufficient.", shortage);
}
//Add purchased books to the purchased books table
BoughtBook myBoughtBook = new BoughtBook();
myBoughtBook.setUserId(userId);
myBoughtBook.setBookId(bookId);
boughtBookDao.insert(myBoughtBook);
//Reduce balance and update
int afterMyBalance = myBalance - bookPrice;
myBankAccount.setBalance(afterMyBalance);
bankAccountDao.update(myBankAccount);
}
** Insufficient balance exception class **
/**
*Insufficient balance exception
*/
public class InsufficientFundsException extends Exception {
private int shortage;
/**
*Exception message and shortfall
*
* @param message Exception message
* @param shortage shortage amount
*/
public InsufficientFundsException(String message, int shortage) {
super(message);
this.shortage = shortage;
}
public int getShortage() {
return this.shortage;
}
}
Whether you can buy a book or not depends on your current bank account balance. In short, there is a possibility that an exceptional situation may occur in which the book cannot be purchased, so a check exception is output to force the caller to change to another process or issue an error message to force handling. If an unchecked exception is output, the caller may not be aware that the handling needs to be implemented, and the current thread may stop without entering the recovery process.
It may be useful on the handling side to include additional information in the exceptions that are output. For example, in the above code, by including the insufficient balance information in the exception, it is possible to specify how much the balance is insufficient when the handling side wants to send an error message to the user.
Consider the following service layer method.
--Send the specified amount to the specified person --Refer to the "bank account" table of the remittance partner --Add the balance of "Bank account" by the specified transfer amount --If the specified remittance amount is not a positive number, an invalid argument exception is output.
** Remittance method **
/**
*Send the specified amount to the specified person
*Specify a positive number for the remittance amount
*
* @param transferPrice Transfer amount
* @param targetUserId ID of the remittance partner
*/
public void transferMoney(int transferPrice, int targetUserId) {
if (transferPrice <= 0) {
throw new IllegalArgumentException("The remittance amount must be specified as a positive number.");
}
BankAccount targetBankAccount = bankAccountDao.selectByUserId(targetUserId);
int nowBalance = targetBankAccount.getBalance();
int afterBalance = nowBalance + transferPrice;
targetBankAccount.setBalance(afterBalance);
bankAccountDao.update(targetBankAccount);
}
This method prohibits the specification of non-positive numbers. This is because if a negative number is specified, the balance of the other party will be reduced. Since the implementation of the method caller can prevent 100% of non-positive numbers, if a non-positive number is specified, it is judged as a program bug, a runtime exception is output, and the current thread is stopped. This is because if the processing is continued, unintended processing may occur, which is dangerous.
Not all run-time exceptions should be caught, Spring data access exceptions are implemented as run-time exceptions and may catch and rethrow another checked exception.