ภาพรวม
Exception ใน Kotlin ทั้งหมดสืบทอดมาจาก Throwable เหมือน Java แตกต่างคือ Kotlin ไม่มี Checked Exception (ไม่บังคับ try-catch หรือ throws) Custom Exception จัดอยู่ในกลุ่ม Unchecked Exception
Checked vs Unchecked Exception
| ประเภท | Checked Exception | Unchecked Exception |
|---|---|---|
| ตรวจสอบเมื่อ | Compile Time | Runtime |
| ตัวอย่าง | IOException, FileNotFoundException | NullPointerException, ArithmeticException, Custom Exception |
| การจัดการ | บังคับ try-catch หรือ throws | ไม่บังคับ (แต่ควรจัดการ) |
| ใน Kotlin | ไม่มี (ทุก Exception เป็น Unchecked) | ทั้งหมด |
สร้าง Custom Exception
// สร้าง Class สืบทอดจาก Exception
class MyCustomException(message: String) : Exception(message)
// หรือสร้างให้ละเอียดขึ้น
class ValidationException(
message: String,
val fieldName: String,
val invalidValue: Any?
) : Exception(message) {
// สามารถเพิ่ม Property เพิ่มเติมได้
}
ตัวอย่างการใช้งาน
fun main() {
val sampleNumber = 100
try {
validateNumber(sampleNumber)
println("Welcome!! You entered a correct value")
} catch (e: MyCustomException) {
println("Custom Exception: ${e.message}")
} catch (e: Exception) {
println("Other Exception: ${e.message}")
} finally {
println("Exception Handling in Kotlin")
}
}
fun validateNumber(number: Int) {
if (number > 50) {
// โยน Custom Exception
throw MyCustomException("Invalid Input - Please enter a number ≤ 50")
}
}
class MyCustomException(message: String) : Exception(message)
ผลลัพธ์
Custom Exception: Invalid Input - Please enter a number ≤ 50
Exception Handling in Kotlin
Best Practices สำหรับ Custom Exception
1. Naming Convention
// ใช้ Suffix "Exception"
class InsufficientBalanceException(message: String) : Exception(message)
class UserNotFoundException(userId: String) : Exception("User not found: $userId")
2. เพิ่ม Context Information
class ApiException(
val statusCode: Int,
val responseBody: String?,
message: String = "API Error"
) : Exception(message) {
override fun toString(): String {
return "ApiException(statusCode=$statusCode, body=$responseBody, message=$message)"
}
}
3. Sealed Class สำหรับ Error Hierarchy (Modern Approach)
sealed class AppError {
data class NetworkError(val cause: Throwable) : AppError()
data class ValidationError(val field: String, val message: String) : AppError()
data class ServerError(val code: Int, val message: String) : AppError()
object UnknownError : AppError()
}
// ใช้งานกับ when (Exhaustive)
fun handleError(error: AppError) = when (error) {
is AppError.NetworkError -> "Network issue: ${error.cause}"
is AppError.ValidationError -> "Invalid ${error.field}: ${error.message}"
is AppError.ServerError -> "Server error ${error.code}: ${error.message}"
AppError.UnknownError -> "Unknown error"
}
4. ใช้ Result Type แทน Exception (Functional Style)
sealed class Result<out T> {
data class Success<T>(val value: T) : Result<T>()
data class Failure(val error: Throwable) : Result<Nothing>()
}
fun divide(a: Int, b: Int): Result<Int> {
return if (b == 0) Result.Failure(ArithmeticException("Divide by zero"))
else Result.Success(a / b)
}
สรุป
- Custom Exception สืบทอดจาก
Exception(Unchecked) - ใช้
throwเพื่อโยน Exception - ควรเพิ่ม Context/Property ให้ละเอียดเพื่อ Debugging
- พิจารณา Sealed Class หรือ Result Type สำหรับ Error Handling แบบ Modern/Functional