Throw/Throws — คืออะไร ตัวดำเนินการข้อยกเว้น และทำงานอย่างไรในการพัฒนาแอปมือถือ

ผู้แต่ง: IT Sectr เผยแพร่เมื่อ: 2026-05-26 เวลาอ่าน: 9 นาที

Throw และ Throws เป็นกลไกในการโยนและประกาศข้อยกเว้นในภาษาโปรแกรมมิ่ง ตัวดำเนินการ throw จะขัดจังหวะการทำงานปกติของฟังก์ชันและส่งวัตถุข้อผิดพลาดขึ้นไปในสแต็กการเรียก คีย์เวิร์ด throws ในลายเซ็นของฟังก์ชันจะเตือนฝ่ายที่เรียกถึงความเป็นไปได้ของข้อผิดพลาด ทำให้โค้ดสามารถคาดเดาได้ ตาม เอกสาร Kotlin (2026) throw ใน Kotlin เป็นนิพจน์ (expression) ไม่ใช่คำสั่ง ซึ่งทำให้สามารถใช้ภายในบล็อก when และตัวดำเนินการ Elvis ได้

ประเด็นสำคัญ

  • Throw — ตัวดำเนินการที่ขัดจังหวะการทำงานของฟังก์ชันและส่งวัตถุข้อผิดพลาดขึ้นไปในสแต็กการเรียก
  • Throws — คีย์เวิร์ดในลายเซ็นของฟังก์ชันที่ประกาศว่าฟังก์ชันอาจโยนข้อยกเว้น
  • ใน Swift ฟังก์ชันถูกทำเครื่องหมายด้วย throws การเรียกต้องใช้ try/try?/try! และ throw ยอมรับชนิดใดก็ได้ที่ใช้โปรโตคอล Error
  • ใน Kotlin throw เป็นนิพจน์ชนิด Nothing ไม่มี throws (ข้อยกเว้นทั้งหมดเป็น unchecked)
  • ข้อผิดพลาดที่กำหนดเอง ถูกสร้างขึ้นผ่าน enum (Swift) หรือ sealed class (Kotlin) เพื่อความสะดวกในการจัดการใน catch

Throw และ Throws คืออะไร?

Throw เป็นตัวดำเนินการที่สร้างข้อยกเว้น ณ จุดที่โปรแกรมทำงาน เมื่อพบ throw กระแสการทำงานปัจจุบันจะถูกขัดจังหวะทันทีและการควบคุมจะถูกโอนไปยังตัวจัดการ catch ที่ใกล้ที่สุดในสแต็กการเรียก หากไม่พบตัวจัดการ แอปพลิเคชันจะหยุดทำงาน Throw ในการพัฒนาแอปมือถือใช้เพื่อส่งสัญญาณข้อผิดพลาดที่ไม่สามารถจัดการได้ในระดับนามธรรมปัจจุบัน — ตัวอย่างเช่น การตอบสนองของเซิร์ฟเวอร์ไม่ถูกต้อง ไม่มีเครือข่าย หรืออาร์กิวเมนต์ไม่ถูกต้อง

Throws — ตัวปรับเปลี่ยนในลายเซ็นของฟังก์ชัน (หลักๆ ใน Swift) ที่ประกาศว่าฟังก์ชันอาจโยนข้อผิดพลาด นี่เป็นส่วนหนึ่งของกลไกข้อผิดพลาดแบบ checked ใน Swift: ฝ่ายที่เรียกจำเป็นต้องจัดการข้อผิดพลาดผ่าน do-catch, try?, try! หรือทำเครื่องหมายฟังก์ชันของตนเป็น throws เพื่อการแพร่กระจายต่อไป ใน Kotlin และ Java throws ก็มีอยู่เช่นกัน แต่ Kotlin ถือว่าซ้ำซ้อน — ข้อยกเว้นทั้งหมดใน Kotlin เป็น unchecked หมายความว่าอาจไม่ถูกจัดการโดยไม่มีการบังคับทางวากยสัมพันธ์ ตาม เอกสาร Apple Swift (2026) throws ใน Swift เป็นวิธีเดียวที่จะประกาศความเป็นไปได้ของข้อผิดพลาดในชนิดฟังก์ชันอย่างชัดเจน ทำให้สัญญา API โปร่งใสสำหรับนักพัฒนา

ความแตกต่างระหว่าง throw และ throws เป็นพื้นฐาน: throw คือการกระทำ (การโยนข้อยกเว้นในขณะทำงาน) throws คือการประกาศ (สัญญาในขณะคอมไพล์) ฟังก์ชันที่ไม่มี throws ไม่สามารถใช้ throw ได้ — คอมไพเลอร์ Swift จะสร้างข้อผิดพลาด ฟังก์ชันที่มี throws อาจไม่ใช้ throw — อนุญาตแต่ไร้ความหมาย การแยกนี้ทำให้ throw/throws เป็นเครื่องมือออกแบบ API ที่ทรงพลัง โดยที่สัญญาข้อผิดพลาดสามารถมองเห็นได้ในลายเซ็นของฟังก์ชันก่อนที่จะเรียก

Throw ใน Swift

ใน Swift ตัวดำเนินการ throw ยอมรับชนิดใดก็ได้ที่ใช้โปรโตคอล Error โดยทั่วไปแล้วจะเป็น enum ที่มีเคสสำหรับข้อผิดพลาดประเภทต่างๆ Swift ไม่สนับสนุนข้อยกเว้นแบบ checked แบบ Java — แต่ชนิดฟังก์ชันจะถูกทำเครื่องหมายด้วย throws และการจัดการจะถูกมอบหมายให้ฝ่ายที่เรียก ทำให้ throw ใน Swift ยืดหยุ่นขึ้นแต่ก็มีความรับผิดชอบมากขึ้นสำหรับนักพัฒนา

โปรโตคอล Error และชนิดข้อผิดพลาด

ชนิดใดก็ได้ที่สอดคล้องกับโปรโตคอล Error สามารถโยนผ่าน throw นักพัฒนามักใช้ enum ที่มีเคสโดยไม่มีค่าที่เกี่ยวข้อง (สำหรับข้อผิดพลาดง่ายๆ) หรือมีค่าที่เกี่ยวข้อง (เพื่อส่งบริบท) Swift ไม่ต้องการให้ข้อผิดพลาดเป็น enum — คุณสามารถใช้ struct หรือ class ที่ใช้ Error แต่ enum จะดีกว่าเนื่องจาก exhaustive switch ฝั่งตัวจัดการ คอมไพเลอร์ตรวจสอบว่าทุกเคสถูกจัดการใน do-catch

swift
enum AuthError: Error {
    case invalidCredentials
    case tokenExpired
    case accountLocked(remainingMinutes: Int)
}

func login(username: String, password: String) throws -> Session {
    guard isValid(username) else {
        throw AuthError.invalidCredentials
    }
    let response = try api.authenticate(username, password)
    if response.isLocked {
        throw AuthError.accountLocked(
            remainingMinutes: response.lockDuration
        )
    }
    return Session(token: response.token)
}

AuthError กำหนดสามสถานการณ์: ข้อมูลรับรองไม่ถูกต้อง โทเค็นหมดอายุ และบัญชีที่ถูกล็อกด้วยค่าที่เกี่ยวข้อง remainingMinutes ฟังก์ชัน login ถูกประกาศเป็น throws — คอมไพเลอร์ต้องการให้เรียกผ่าน try ภายในฟังก์ชัน throw ถูกใช้ในสองที่: สำหรับชื่อผู้ใช้ที่ไม่ถูกต้องและสำหรับบัญชีที่ถูกล็อก ค่าที่เกี่ยวข้อง accountLocked ช่วยให้ส่งข้อมูลเฉพาะให้ผู้ใช้ — ต้องรอกี่นาทีก่อนปลดล็อก วิธีการนี้ช่วยลดความจำเป็นในการมี API endpoints แยกต่างหากเพื่อตรวจสอบสถานะการล็อก

Throw ใน Kotlin

ใน Kotlin throw เป็นนิพจน์ชนิด Nothing ไม่ใช่คำสั่ง ซึ่งหมายความว่า throw สามารถใช้ทางด้านขวาของการกำหนดค่า ภายในนิพจน์ when และตัวดำเนินการ Elvis ?: ชนิด Nothing เป็นชนิดย่อยพิเศษของทุกชนิดใน Kotlin ซึ่งช่วยให้ใช้ throw ในที่ที่ต้องการค่าของชนิดใดก็ได้ คอมไพเลอร์เข้าใจว่าการทำงานไม่ดำเนินต่อหลังจาก throw และไม่ต้องการสาขาสำหรับกรณีนี้

ชนิด Nothing และบทบาทในองค์ประกอบ

Nothing เป็นชนิดเฉพาะใน Kotlin ที่เป็นชนิดย่อยของทุกชนิดที่เป็นไปได้ ฟังก์ชันที่คืนค่า Nothing (เช่น TODO()) ไม่เคยเสร็จสมบูรณ์ตามปกติ — ไม่ว่าจะโยนข้อยกเว้นเสมอหรือเข้าสู่ลูปไม่รู้จบ ทำให้ throw เป็นตัวเลือกที่เหมาะสมสำหรับใช้ในที่ที่ต้องการค่า: ตัวดำเนินการ Elvis, when โดยไม่มี else, การเริ่มต้นตัวแปร หาก throw อยู่ในสาขา when คอมไพเลอร์เข้าใจว่าสาขานั้นนำไปสู่ Nothing และไม่ต้องการ return หรือ else สำหรับสาขานั้น

kotlin
data class Config(val apiUrl: String, val timeoutSec: Int)

class ConfigParser {
    fun parse(json: String): Config {
        val obj = JSONObject(json)
        val url = obj.optString("apiUrl")
            ?: throw IllegalArgumentException("apiUrl is required")
        val timeout = obj.optInt("timeoutSec", 30)
        return Config(url, timeout)
    }

    fun getErrorMessage(code: Int): String {
        return when (code) {
            404 -> "Not found"
            500 -> "Server error"
            else -> throw IllegalArgumentException("Unknown code: $code")
        }
    }
}

ในตัวอย่างแรก throw ถูกใช้ในตัวดำเนินการ Elvis ?:: หากฟิลด์ apiUrl ไม่มีอยู่ใน JSON นิพจน์ throw จะขัดจังหวะการทำงานทันทีและโยน IllegalArgumentException ชนิด Nothing ช่วยให้คอมไพเลอร์อนุมานชนิดของด้านขวาเป็น String (Elvis คาดหวัง String, throw มีชนิด Nothing, Nothing เป็นชนิดย่อยของ String) ในตัวอย่างที่สอง throw ภายในนิพจน์ when: หากรหัสไม่ตรงกับรหัสที่รู้จักใดๆ จะมีการโยนข้อยกเว้น คอมไพเลอร์เข้าใจว่าโค้ดหลังจาก throw ไม่สามารถเข้าถึงได้ ดังนั้นชนิดที่ส่งคืนของฟังก์ชัน String จึงไม่ถูกละเมิด

Throws ในลายเซ็นฟังก์ชันและ rethrows

ใน Swift throws ถูกระบุหลังจากรายการพารามิเตอร์และก่อนลูกศรชนิดที่ส่งคืน ฟังก์ชันที่มี throws สามารถเรียกฟังก์ชัน throws อื่นได้เฉพาะภายใน do-catch หรือด้วย try? หากฟังก์ชัน throws ไม่จัดการข้อผิดพลาด มันจะส่งต่อไปยังฝ่ายที่เรียก Swift ยังสนับสนุน rethrows — ตัวปรับเปลี่ยนสำหรับฟังก์ชันลำดับสูงที่ยอมรับ closure แบบ throws และแพร่กระจายข้อผิดพลาดของมัน rethrows หมายความว่าฟังก์ชันจะโยนข้อผิดพลาดก็ต่อเมื่อ closure ที่ส่งเข้ามาโยนข้อผิดพลาด — ฟังก์ชันเองไม่ได้สร้างข้อผิดพลาด

swift
func mapValues<T>(
    _ array: [T],
    transform: (T) throws -> U
) rethrows -> [U] {
    var result = [U]()
    for element in array {
        result.append(try transform(element))
    }
    return result
}

// การใช้กับ closure แบบ throws
let parsed = try mapValues(jsonStrings) { str in
    let data = Data(str.utf8)
    return try JSONDecoder().decode(Item.self, from: data)
}

rethrows ช่วยให้ฟังก์ชัน mapValues ยืดหยุ่น: มันยอมรับทั้ง closure แบบ throws และแบบปกติ หากมีการส่ง closure แบบ throws การเรียก mapValues ต้องใช้ try ถ้าแบบปกติไม่ต้องใช้ try ทำให้ rethrows เหมาะสำหรับฟังก์ชันลำดับสูง เช่น map, filter, reduce ในไลบรารีมาตรฐานของ Swift คำแนะนำของ Apple: ใช้ rethrows สำหรับ API ที่ยอมรับ closure แบบ throws และแหล่งที่มาของข้อผิดพลาดเพียงอย่างเดียวคือ closure นั้น หากฟังก์ชันสามารถโยนข้อผิดพลาดของตัวเองได้ ให้ใช้ throws

ข้อยกเว้นแบบ checked vs unchecked

throws ใน Swift ใกล้เคียงกับข้อยกเว้นแบบ checked (เช่นใน Java) — ข้อผิดพลาดที่ถูกโยนถูกประกาศในลายเซ็น Kotlin และ Dart ใช้ ข้อยกเว้นแบบ unchecked — throws ในลายเซ็นไม่จำเป็น ความแตกต่างเป็นพื้นฐาน: checked บังคับให้นักพัฒนาจัดการข้อผิดพลาด (ปลอดภัยกว่าแต่ยาวกว่า) unchecked ให้อิสระแต่เพิ่มความเสี่ยงในการลืมจัดการข้อผิดพลาด Swift เลือก checked สำหรับ throws, Kotlin เลือก unchecked สำหรับข้อยกเว้นทั้งหมด ทั้งสองวิธีมีข้อดี: Swift เชื่อถือได้มากกว่าในระดับภาษา, Kotlin กะทัดรัดกว่าและสะดวกในห่วงโซ่การแปลงเชิงฟังก์ชัน

ชนิดข้อผิดพลาดที่กำหนดเอง

สำหรับการจัดการข้อผิดพลาดที่มีโครงสร้างในแอปพลิเคชันมือถือ ขอแนะนำให้สร้างชนิดข้อผิดพลาดที่กำหนดเองแทนที่จะใช้ Exception หรือ Error พื้นฐาน ใน Swift ใช้ enum กับโปรโตคอล Error ใน Kotlin ใช้ sealed class ที่สืบทอดจาก Throwable (หรือจาก Exception) ใน Dart ใช้ class ที่สืบทอดจาก Exception ชนิดที่กำหนดเองช่วยให้จัดกลุ่มข้อผิดพลาดตามหมวดหมู่และส่งข้อมูลที่เกี่ยวข้องได้

ภาษาชนิดข้อผิดพลาดคุณสมบัติ
Swiftenum: Error { ... }ค่าที่เกี่ยวข้อง, exhaustive switch ใน catch
Kotlinsealed class : Throwable()Data class สำหรับข้อผิดพลาดพร้อมฟิลด์, นิพจน์ when
Dartclass implements Exceptionฟิลด์ Message, อนุประโยค on ใน catch
Javaclass extends ExceptionChecked vs unchecked, throws บังคับในลายเซ็น

เมื่อออกแบบข้อผิดพลาดที่กำหนดเอง ให้ปฏิบัติตามกฎ: หนึ่งข้อผิดพลาด — หนึ่งสถานการณ์ อย่ารวมสาเหตุต่างๆ ในชนิดเดียวกับแฟล็ก String message — สร้างเคส/คลาสย่อยแยกต่างหากสำหรับแต่ละสถานการณ์ ซึ่งจะช่วยให้ฝ่ายที่เรียกจัดการแต่ละเคสผ่าน pattern matching (when/switch) แทนการเปรียบเทียบสตริง ใน Swift สิ่งนี้ให้ exhaustive checking — คอมไพเลอร์จะเตือนหากไม่มีการจัดการเคสใดของ enum NetworkError

ตัวอย่างข้อผิดพลาดที่กำหนดเองใน Kotlin

kotlin
sealed class NetworkError(val message: String) : Throwable(message) {
    data class Timeout(val durationMs: Long) :
        NetworkError("Request timed out after ${durationMs}ms")
    data class HttpError(val code: Int, val body: String?) :
        NetworkError("HTTP $code")
    data class NoConnection(val cause: IOException) :
        NetworkError("No internet connection")
}

Sealed class NetworkError สืบทอดจาก Throwable (ชนิดข้อยกเว้นมาตรฐานใน Kotlin) แต่ละคลาสย่อยเป็น data class ที่มีฟิลด์ของตัวเอง: Timeout มีระยะเวลาหมดเวลาเป็นมิลลิวินาที HttpError มีรหัสและเนื้อหาการตอบสนอง NoConnection มี IOException ต้นฉบับ การออกแบบนี้ช่วยให้จัดการข้อผิดพลาดแต่ละรายการผ่าน when ด้วยการจับคู่ที่ครบถ้วน (เมื่อคุณเพิ่มคลาสย่อยใหม่ คอมไพเลอร์จะบังคับให้อัปเดตนิพจน์ when ทั้งหมด)

Try, try? และ try! ใน Swift

Swift มีสามวิธีในการเรียกฟังก์ชัน throws แต่ละวิธีมีสัญญาความปลอดภัยของตัวเอง try เป็นวิธีมาตรฐาน: ต้องใช้ do-catch หรืออยู่ภายในฟังก์ชัน throws try? แปลงข้อผิดพลาดเป็น nil — ผลลัพธ์กลายเป็นตัวเลือก เมื่อมีข้อผิดพลาดจะคืนค่า nil ชนิดเปลี่ยนจาก T เป็น T? try! — การบังคับทำงานโดยไม่จัดการ: หากโยนข้อผิดพลาด แอปพลิเคชันจะหยุดทำงาน ใช้ try! เฉพาะเมื่อคุณแน่ใจอย่างยิ่งว่าข้อผิดพลาดเป็นไปไม่ได้ (เช่น ข้อมูลที่ถูกต้องอย่างชัดเจน)

swift
let configPath = Bundle.main.path(forResource: "config", ofType: "json")!

// try? — ผลลัพธ์ที่เป็นตัวเลือก
let data = try? Data(contentsOf: URL(fileURLWithPath: configPath))
let json = try? JSONSerialization.jsonObject(with: data ?? Data())

// try! — ความสำเร็จที่รับประกัน (เมื่อแน่ใจเท่านั้น)
let decoder = JSONDecoder()
let defaultConfig = try! decoder.decode(
    Config.self,
    from: Config.defaultJSON
)

// try — การจัดการมาตรฐาน
do {
    let user = try fetchUser()
    showUser(user)
} catch let error as NetworkError {
    showRetryAlert(error.message)
}

ในตัวอย่าง try! ใช้สำหรับ JSON ที่ถูกต้องอย่างชัดเจนซึ่งฝังอยู่ในบันเดิลของแอป — ข้อผิดพลาดในการถอดรหัสเป็นไปไม่ได้เมื่อมีการเผยแพร่ที่ถูกต้อง try? ใช้สำหรับอ่านไฟล์การกำหนดค่า — หากไฟล์หายไปหรือเสียหาย แอปจะใช้ค่าเริ่มต้นแทนที่จะหยุดทำงาน try ใน do-catch ใช้สำหรับคำขอเครือข่ายที่คาดว่าข้อผิดพลาดจะเกิดขึ้นและต้องการการตอบสนองจากผู้ใช้ คำแนะนำ: หลีกเลี่ยง try! ในโค้ดการผลิต — ใช้เฉพาะกับข้อมูลคงที่ที่ตรวจสอบแล้วในขณะสร้าง

คำถามที่พบบ่อย

ความแตกต่างระหว่าง throw และ throws ใน Swift คืออะไร?

Throw เป็นตัวดำเนินการที่โยนข้อผิดพลาดระหว่างการทำงานของโปรแกรม ขัดจังหวะกระแส Throws เป็นตัวปรับเปลี่ยนลายเซ็นฟังก์ชันที่ประกาศว่าฟังก์ชันอาจโยนข้อผิดพลาด ฟังก์ชันที่ไม่มี throws ไม่สามารถใช้ throw ได้ Throws เป็นสัญญาในขณะคอมไพล์ throw เป็นการกระทำในขณะทำงาน

ทำไม Kotlin ถึงไม่มี throws?

Kotlin ปฏิบัติตามปรัชญาของข้อยกเว้น unchecked: ข้อยกเว้นทั้งหมดอาจไม่ถูกจัดการโดยไม่มีการบังคับทางวากยสัมพันธ์ นักพัฒนา Kotlin เชื่อว่า throws ใน Java นำไปสู่บล็อก try-catch ที่มากเกินไปและการละเว้นข้อยกเว้น checked ผ่าน catch ที่ว่างเปล่า ชนิด Nothing ของ Kotlin ช่วยให้ใช้ throw เป็นนิพจน์ แทนที่ throws ด้วยวิธีการที่ยืดหยุ่นกว่า

เมื่อใดควรใช้ try! ใน Swift

try! ยอมรับได้เฉพาะเมื่อคุณแน่ใจอย่างยิ่งว่าข้อผิดพลาดเป็นไปไม่ได้: JSON ที่ถูกต้องอย่างชัดเจนจากบันเดิล ข้อมูลคงที่ โครงร่าง URL ที่ถูกต้อง ในโค้ดการผลิต try! เป็นข้อยกเว้น ไม่ใช่กฎ try? เหมาะกว่าสำหรับสถานการณ์ที่เป็นตัวเลือกพร้อมค่าเริ่มต้น try กับ do-catch สำหรับการจัดการข้อผิดพลาดที่บังคับ

rethrows ใน Swift คืออะไร?

Rethrows เป็นตัวปรับเปลี่ยนสำหรับฟังก์ชันที่ยอมรับ closure แบบ throws ฟังก์ชันที่มี rethrows จะโยนข้อผิดพลาดก็ต่อเมื่อ closure ที่ส่งเข้ามาโยนข้อผิดพลาด ซึ่งช่วยให้ฟังก์ชันลำดับสูง (map, filter) ทำงานกับทั้ง closure แบบ throws และไม่ throws โดยไม่บังคับ try ฝั่งที่เรียก

สามารถโยนข้อผิดพลาดภายในบล็อก catch ได้หรือไม่

ได้ ภายใน catch คุณสามารถใช้ throw เพื่อแพร่กระจายข้อผิดพลาดขึ้นไปในสแต็ก โดยห่อหุ้มในชนิดอื่นหรือเพิ่มบริบท สิ่งนี้เรียกว่า การโยงข้อผิดพลาด (error chaining) หรือการโยนซ้ำ ใน Swift แค่ throw อีกครั้งภายใน catch ก็เพียงพอ ใน Kotlin throw ภายในบล็อก catch บล็อก finally จะทำงานก่อนที่การควบคุมจะถูกส่งต่อ

สรุป

  • Throw — ตัวดำเนินการโยนข้อยกเว้นที่ขัดจังหวะกระแสปัจจุบันและส่งวัตถุข้อผิดพลาดขึ้นไปในสแต็ก
  • Throws — การประกาศในขณะคอมไพล์ในลายเซ็นฟังก์ชันเกี่ยวกับความเป็นไปได้ของข้อผิดพลาด บังคับใน Swift ไม่มีใน Kotlin
  • ใน Swift throw ยอมรับ enum: Error ฟังก์ชันถูกทำเครื่องหมายด้วย throws เรียกผ่าน try / try? / try!
  • ใน Kotlin throw เป็นนิพจน์ชนิด Nothing ซึ่งอนุญาตให้ใช้ใน when ตัวดำเนินการ Elvis และการกำหนดค่า
  • Rethrows ใน Swift ช่วยให้ฟังก์ชันลำดับสูงแพร่กระจายข้อผิดพลาดจาก closure ที่ส่งเข้ามาเท่านั้น
  • ข้อผิดพลาดที่กำหนดเอง ถูกสร้างขึ้นผ่าน enum (Swift) หรือ sealed class (Kotlin) พร้อมค่าที่เกี่ยวข้องสำหรับข้อมูลโดยละเอียด
  • ออกแบบ ชนิดข้อผิดพลาด ตามหลักการ "หนึ่งเคส — หนึ่งสถานการณ์" เพื่อความสะดวกในการจัดการผ่าน pattern matching

เราจะพัฒนาแอปพลิเคชันบนมือถือแบบครบวงจร

IT Sectr สร้างแอปพลิเคชัน iOS และ Android สำหรับสตาร์ทอัพและธุรกิจตั้งแต่ปี 2017 เราจะให้คำแนะนำและเสนอวิธีแก้ปัญหาที่ดีที่สุดแก่คุณ

ปรึกษาโครงการ

อ่านเพิ่มเติม