data class Person(val name: String, val age: Int)
// 1. What Exception?
fun main(args: Array<String>) {
var person: Person? = null
val age: Int? = person!!.age
println(age)
}
// 2. What Exception?
open class Animal {}
class Dog: Animal() {}
class Cat: Animal() {}
fun main(args: Array<String>) {
val dog = Dog()
val cat = dog as Cat
}
상황에 맞는 Exception Handle을 해야한다.
Exception Class
Throwable
Error / Exception에 대한 메세지를 담고, Chained Exception에 대한 정보들을 기록
The Throwable class is the superclass of all errors and exceptions in the Java language. Only objects that are instances of this class (or one of its subclasses) are thrown by the Java Virtual Machine or can be thrown by the Java throw statement. Similarly, only this class or one of its subclasses can be the argument type in a catch clause. For the purposes of compile-time checking of exceptions, Throwable and any subclass of Throwable that is not also a subclass of either [RuntimeException](https://docs.oracle.com/en/java/javase/14/docs/api/java.base/java/lang/RuntimeException.html) or [Error](https://docs.oracle.com/en/java/javase/14/docs/api/java.base/java/lang/Error.html) are regarded as checked exceptions …
fun callee(flag: Boolean) {
if (flag) println("호출당했어!")
}
fun caller(flag: Boolean) {
callee(flag)
}
출력을 안하는 Exception을 던질 때
하위 단계 코드 변경 → 상위 단계 코드 변경
모두 catch 블록에서 처리
선언부에 throws 절 추가
fun callee(flag: Boolean) throws NotPrintException {
if (flag) {
println("호출당했어!")
}
else {
throw NotPrintException()
}
}
fun caller(flag: Boolean) **throws NotPrintException** {
callee(flag)
}
🌱 Checked Exception은 **캡슐화를 깰 수 있다.**
Exception에 의미를 제공하기
🌱 오류가 발생한 **원인과 위치**를 찾을 수 있어야 한다.
stacktrace 활용
message에 유의미한 정보를 담아서 던지기
실패한 연산 이름, 실패 유형 언급
Logging 가능 → catch 블록에서 오류 기록
// ...
throw new IllegarArgumentException();
// ...
throw new InvalidSearchArgumentException("검색 조건은 빈칸일 수 없습니다.");
Exception Class 정의하기
호출자(Caller)를 고려해서 Exception을 정의할 수 있다.
/**
* + 앱에서 발생하는 클라이언트 및 서버 에러 정보
* + [message] 메세지
* + [cause] 원인이 되는 익셉션
*/
class MyException(
message: String? = null,
cause: Throwable? = null
): Exception(message, cause) {
/**
* 에러 코드 [ErrorCode]
*/
var errorCode: Int = ErrorCode.UNKNOWN_ERROR
private set
/**
* 사용자에게 서버 에러 메세지 표시가 필요한 경우 값을 가진다.
*/
var serverAlert: ErrorMessage? = null
private set
data class ErrorMessage(val code: Int, val message: String)
companion object {
/**
* 사용자 고지가 필요한 서버 에러 정보를 포함한 Exception 생성
*/
fun alertOf(code: Int, message: String? = null): MyException {
return MyException().apply {
errorCode = ErrorCode.SERVER_ALERT_ERROR
serverAlert = ErrorMessage(code = code, message = message ?: "")
}
}
/**
* 일반적인 서버 에러로 부터 Exception 생성
*/
fun serverErrorOf(code: Int, message: String? = null): MyException {
val error = ErrorCode.parseServerError(code)
return MyException(message).apply {
errorCode = error
}
}
/**
* 원인 익셉션으로 Exception 생성
*/
fun exceptionOf(cause: Exception, message: String? = null): MyException {
val error = ErrorCode.parseException(cause)
return MyException(
message = message ?: cause.message,
cause = cause
).apply {
errorCode = error
}
}
}
}
List<Employee> employees = getEmployees();
for(Employee e : employees) {
totalPay += e.getPay();
}
// Colections.emptyList() 활용
public List<Employee> getEmployees() {
if (/* 직원이 없다면 */) {
return **Collections.emptyList();**
}
}
Null 전달
메서드가 null을 반환하는 방식도 나쁘지만, 메서드에 null을 전달하는건 더 나쁘다.
두 지점 사이의 거리를 구하는 메서드
public double xProjection(Point p1, Point p2) {
return (p2.x - p1.x) * 1.5
}
인수로 null을 전달하면?
calculator.xProjection(null, new Point(2, 3));
예외를 만들어 던지기
Exception을 잡아내는 처리기가 필요
public double xProjection(Point p1, Point p2) {
if (p1 == null || p2 == null) {
throw InvalidArgumentException("Invalid Point");
}
return (p2.x - p1.x) * 1.5
}
assert
여전히 오류 발생
public double xProjection(Point p1, Point p2) {
assert p1 != null : "p1 should not be null";
assert p2 != null : "p2 should not be null";
return (p2.x - p1.x) * 1.5;
}
🌱 *애초에 null을 넘기지 못하게 하라*
결론
명시적 Exception은 반드시 이름을 지정하여 catching
어플리케이션에서 구현한 함수 내에서 Exception 발생이 예상되는 경우
최상위 Exception 클래스에서 반드시 catch 처리
Exception을 처리하는 궁극적인 위치가 중요.
Exception 발생시에는 Crashlytics 와 같은 에러 로그 라이브러리 등을 이용하여 개발자가 인지할 수 있도록 하기