Kotlin Bootcamp สําหรับ Programmer 4: โปรแกรมที่เน้นวัตถุ

Codelab นี้เป็นส่วนหนึ่งของหลักสูตร Kotlin Bootcamp สําหรับโปรแกรมเมอร์ คุณจะได้รับประโยชน์สูงสุดจากหลักสูตรนี้ หากทํางานผ่าน Codelab ตามลําดับ คุณอาจอ่านผ่านบางหัวข้อได้ ทั้งนี้ขึ้นอยู่กับความรู้ของคุณ หลักสูตรนี้มุ่งเน้นที่โปรแกรมเมอร์ที่มีความรู้เกี่ยวกับภาษาเชิงวัตถุและต้องการเรียนรู้ Kotlin

บทนำ

ใน Codelab นี้ คุณจะสร้างโปรแกรม Kotlin แล้วเรียนรู้เกี่ยวกับชั้นเรียนและวัตถุใน Kotlin คุณจะคุ้นเคยกับเนื้อหาส่วนใหญ่นี้ ถ้าคุณรู้ภาษาเชิงวัตถุอื่น แต่ Kotlin มีความแตกต่างที่สําคัญบางประการในการลดจํานวนโค้ดที่ต้องใช้ในการเขียน รวมถึงดูข้อมูลเกี่ยวกับชั้นเรียนนามธรรมและการมอบสิทธิ์อินเทอร์เฟซ

แทนที่หลักสูตรนี้จะสร้างแอปตัวอย่างเดียว บทเรียนในหลักสูตรนี้ออกแบบมาเพื่อสร้างเสริมความรู้ แต่นักเรียนไม่ต้องพึ่งพากันและกันเพื่อให้คุณเข้าใจส่วนต่างๆ ที่คุณคุ้นเคย ตัวอย่างจํานวนมากใช้ธีมสัตว์น้ําร่วมกัน และอยากดูเรื่องราวเกี่ยวกับสัตว์น้ําทั้งหมด โปรดดูหลักสูตร Kotlin Bootcamp for Programmers Udacity

สิ่งที่ควรทราบอยู่แล้ว

  • ข้อมูลเบื้องต้นของ Kotlin รวมทั้งประเภท โอเปอเรเตอร์ และการวนซ้ํา
  • ไวยากรณ์ฟังก์ชันของ Kotlin's
  • พื้นฐานของการเขียนโปรแกรมที่เน้นวัตถุ
  • ข้อมูลพื้นฐานของ IDE เช่น IntelliJ IDEA หรือ Android Studio

สิ่งที่คุณจะได้เรียนรู้

  • วิธีสร้างชั้นเรียนและเข้าถึงพร็อพเพอร์ตี้ใน Kotlin
  • วิธีสร้างและใช้เครื่องมือสร้างชั้นเรียนใน Kotlin
  • วิธีสร้างคลาสย่อยและวิธีการทํางานของการรับช่วงต่อ
  • เกี่ยวกับชั้นเรียนนามธรรม อินเทอร์เฟซ และการมอบสิทธิ์ในอินเทอร์เฟซ
  • วิธีสร้างและใช้ชั้นเรียนข้อมูล
  • วิธีใช้ซิงเกิลตัน Enum และคลาสปิดผนึก

สิ่งที่คุณจะทํา

  • สร้างชั้นเรียนด้วยพร็อพเพอร์ตี้
  • สร้างเครื่องมือสร้างสําหรับชั้นเรียน
  • สร้างคลาสย่อย
  • ดูตัวอย่างคลาสและอินเทอร์เฟซนามธรรม
  • สร้างคลาสข้อมูลแบบง่าย
  • ดูข้อมูลเกี่ยวกับซิงเกิล Enum และคลาสปิดผนึก

ข้อกําหนดการจัดโปรแกรมต่อไปนี้ควรเป็นสิ่งที่คุณคุ้นเคยอยู่แล้ว

  • ชั้นเรียนคือพิมพ์เขียวของวัตถุ เช่น คลาส Aquarium คือพิมพ์เขียวสําหรับสร้างวัตถุในพิพิธภัณฑ์สัตว์น้ํา
  • วัตถุ คืออินสแตนซ์ของคลาส วัตถุพิพิธภัณฑ์สัตว์น้ําคือ Aquarium ตามจริง
  • พร็อพเพอร์ตี้คือลักษณะของคลาส เช่น ความยาว ความกว้าง และความสูงของ Aquarium
  • Methods หรือที่เรียกว่าฟังก์ชันสมาชิกเป็นฟังก์ชันของชั้นเรียน เมธอดคือสิ่งที่คุณ "do" อยู่ได้ด้วยวัตถุ ตัวอย่างเช่น คุณจะfillWithWater()ออบเจ็กต์ Aquarium ได้
  • อินเทอร์เฟซคือข้อกําหนดที่ชั้นเรียนจะนําไปใช้ได้ ตัวอย่างเช่น การทําความสะอาดมักพบวัตถุอื่นนอกเหนือจาก พิพิธภัณฑ์สัตว์น้ํา และโดยทั่วไปแล้วการทําความสะอาดก็ทําในลักษณะที่คล้ายๆ กันสําหรับวัตถุต่างๆ ดังนั้น คุณอาจมีอินเทอร์เฟซที่ชื่อ Clean ซึ่งกําหนดเมธอด clean() ชั้นเรียน Aquarium สามารถใช้อินเทอร์เฟซ Clean เพื่อทําความสะอาดตู้ปลาด้วยฟองน้ํานิ่ม
  • แพ็กเกจคือวิธีจัดกลุ่มโค้ดที่เกี่ยวข้องเพื่อจัดระเบียบหรือสร้างคลังโค้ด เมื่อสร้างแพ็กเกจแล้ว คุณจะนําเข้าเนื้อหาของแพ็กเกจลงในไฟล์อื่นและนํารหัสและคลาสมาใช้ซ้ําได้

ในงานนี้ คุณจะได้สร้างแพ็กเกจและคลาสใหม่พร้อมทั้งพร็อพเพอร์ตี้และวิธีการ

ขั้นตอนที่ 1: สร้างแพ็กเกจ

แพ็กเกจช่วยให้คุณจัดระเบียบโค้ดได้

  1. ในแผง Project ใต้โปรเจ็กต์ สวัสดี Kotlin ให้คลิกขวาที่โฟลเดอร์ src
  2. เลือก New > Package และตั้งชื่อว่า example.myapp

ขั้นตอนที่ 2: สร้างชั้นเรียนด้วยพร็อพเพอร์ตี้

ชั้นเรียนจะกําหนดด้วยคีย์เวิร์ด class และชื่อชั้นเรียนตามแบบแผนเริ่มต้นด้วยอักษรตัวพิมพ์ใหญ่

  1. คลิกขวาที่แพ็กเกจ example.myapp
  2. เลือก New > Kotlin File / Class
  3. ในส่วนประเภท ให้เลือกชั้นเรียน และตั้งชื่อชั้นเรียนว่า Aquarium IntelliJ IDEA มีชื่อแพ็กเกจในไฟล์และสร้างคลาส Aquarium ที่ว่างเปล่าให้คุณ
  4. ในคลาส Aquarium ให้กําหนดและเริ่มต้นคุณสมบัติ var สําหรับความกว้าง ความสูง และความยาว (เป็นเซนติเมตร) เริ่มต้นพร็อพเพอร์ตี้ด้วยค่าเริ่มต้น
package example.myapp

class Aquarium {
    var width: Int = 20
    var height: Int = 40
    var length: Int = 100
}

ภายในพร็อพเพอร์ตี้ Kotlin จะสร้าง Getter และ Setter โดยอัตโนมัติสําหรับพร็อพเพอร์ตี้ที่คุณกําหนดในชั้นเรียน Aquarium เพื่อให้คุณเข้าถึงพร็อพเพอร์ตี้ได้โดยตรง เช่น myAquarium.length

ขั้นตอนที่ 3: สร้างฟังก์ชันหลัก()

สร้างไฟล์ใหม่ชื่อ main.kt เพื่อเก็บฟังก์ชัน main()

  1. คลิกขวาที่แพ็กเกจ example.myapp ในแผงโปรเจ็กต์ทางด้านซ้าย
  2. เลือก New > Kotlin File / Class
  3. ในเมนูแบบเลื่อนลงประเภท ให้เก็บรายการที่เลือกเป็นไฟล์ และตั้งชื่อไฟล์ main.kt IntelliJ IDEA จะรวมชื่อแพ็กเกจ แต่ไม่รวมคําจํากัดความของคลาสสําหรับไฟล์
  4. กําหนดฟังก์ชัน buildAquarium() และสร้างอินสแตนซ์ของ Aquarium ภายใน ในการสร้างอินสแตนซ์ ให้อ้างอิงคลาสเสมือนเป็นฟังก์ชัน Aquarium() การดําเนินการนี้จะโทรหาเครื่องมือสร้างชั้นเรียนและสร้างอินสแตนซ์ของคลาส Aquarium ซึ่งคล้ายกับการใช้ new ในภาษาอื่นๆ
  5. กําหนดฟังก์ชัน main() แล้วเรียกใช้ buildAquarium()
package example.myapp

fun buildAquarium() {
    val myAquarium = Aquarium()
}

fun main() {
    buildAquarium()
}

ขั้นตอนที่ 4: เพิ่มเมธอด

  1. ในชั้นเรียน Aquarium ให้เพิ่มวิธีพิมพ์คุณสมบัติมิติข้อมูลของพิพิธภัณฑ์สัตว์น้ํา
    fun printSize() {
        println("Width: $width cm " +
                "Length: $length cm " +
                "Height: $height cm ")
    }
  1. ใน main.kt ใน buildAquarium() ให้เรียกใช้เมธอด printSize() ใน myAquarium
fun buildAquarium() {
    val myAquarium = Aquarium()
    myAquarium.printSize()
}
  1. เรียกใช้โปรแกรมโดยคลิกรูปสามเหลี่ยมสีเขียวถัดจากฟังก์ชัน main() สังเกตผลลัพธ์
⇒ Width: 20 cm Length: 100 cm Height: 40 cm 
  1. ใน buildAquarium() ให้เพิ่มโค้ดเพื่อกําหนดความสูงเป็น 60 และพิมพ์คุณสมบัติของมิติข้อมูลที่เปลี่ยนแปลง
fun buildAquarium() {
    val myAquarium = Aquarium()
    myAquarium.printSize()
    myAquarium.height = 60
    myAquarium.printSize()
}
  1. เรียกใช้โปรแกรมและสังเกตเอาต์พุต
⇒ Width: 20 cm Length: 100 cm Height: 40 cm 
Width: 20 cm Length: 100 cm Height: 60 cm 

ในงานนี้ คุณจะต้องสร้างเครื่องมือสร้างสําหรับชั้นเรียนและทํางานกับพร็อพเพอร์ตี้ต่อ

ขั้นตอนที่ 1: สร้างเครื่องมือสร้าง

ในขั้นตอนนี้ ให้เพิ่มเครื่องมือสร้างไปยังชั้นเรียน Aquarium ที่คุณสร้างในงานแรก ในตัวอย่างก่อนหน้า อินสแตนซ์ทั้งหมดของ Aquarium จะสร้างด้วยมิติข้อมูลเดียวกัน คุณสามารถเปลี่ยนมิติข้อมูลเมื่อสร้างมิติข้อมูลด้วยการตั้งค่าพร็อพเพอร์ตี้ได้ง่ายขึ้น แต่การสร้างเริ่มต้นด้วยขนาดที่ถูกต้องจะทําได้ง่ายกว่า

ในบางภาษาโปรแกรม เครื่องมือสร้างจะกําหนดโดยการสร้างเมธอดภายในชั้นเรียนที่มีชื่อเดียวกับคลาส ใน Kotlin คุณกําหนดตัวสร้างโดยตรงในการประกาศคลาสเอง โดยระบุพารามิเตอร์ภายในวงเล็บเสมือนว่าคลาสเป็นเมธอด พารามิเตอร์เหล่านั้นสามารถใส่ค่าเริ่มต้นได้เช่นเดียวกับฟังก์ชันใน Kotlin

  1. ในคลาส Aquarium ที่คุณสร้างก่อนหน้านี้ ให้เปลี่ยนแปลงการกําหนดคลาสให้รวมพารามิเตอร์ตัวสร้าง 3 รายการที่มีค่าเริ่มต้นสําหรับ length, width และ height และกําหนดให้กับพร็อพเพอร์ตี้ที่เกี่ยวข้อง
class Aquarium(length: Int = 100, width: Int = 20, height: Int = 40) {
   // Dimensions in cm
   var length: Int = length
   var width: Int = width
   var height: Int = height
...
}
  1. วิธี Kotlin ที่กะทัดรัดมากขึ้นคือการกําหนดพร็อพเพอร์ตี้กับเครื่องมือสร้างโดยตรงโดยใช้ var หรือ val และ Kotlin ยังสร้าง Getter และ Setter โดยอัตโนมัติด้วย จากนั้นนําคําจํากัดความของพร็อพเพอร์ตี้ออกจากเนื้อหาในชั้นเรียนได้
class Aquarium(var length: Int = 100, var width: Int = 20, var height: Int = 40) {
...
}
  1. เมื่อสร้างออบเจ็กต์ Aquarium ด้วยเครื่องมือสร้างนั้น คุณจะระบุอาร์กิวเมนต์ไม่ได้และรับค่าเริ่มต้น หรือระบุเฉพาะบางออบเจ็กต์ หรือระบุออบเจ็กต์ทั้งหมดและสร้าง Aquarium ที่มีขนาดแบบกําหนดเองทั้งหมด ในฟังก์ชัน buildAquarium() ให้ลองใช้วิธีต่างๆ ในการสร้างออบเจ็กต์ Aquarium โดยใช้พารามิเตอร์ที่มีชื่อ
fun buildAquarium() {
    val aquarium1 = Aquarium()
    aquarium1.printSize()
    // default height and length
    val aquarium2 = Aquarium(width = 25)
    aquarium2.printSize()
    // default width
    val aquarium3 = Aquarium(height = 35, length = 110)
    aquarium3.printSize()
    // everything custom
    val aquarium4 = Aquarium(width = 25, height = 35, length = 110)
    aquarium4.printSize()
}
  1. เรียกใช้โปรแกรมและสังเกตเอาต์พุต
⇒ Width: 20 cm Length: 100 cm Height: 40 cm 
Width: 25 cm Length: 100 cm Height: 40 cm 
Width: 20 cm Length: 110 cm Height: 35 cm 
Width: 25 cm Length: 110 cm Height: 35 cm 

โปรดสังเกตว่าคุณไม่ได้โหลดเครื่องมือสร้างมากเกินไปและเขียนเวอร์ชันอื่นสําหรับแต่ละกรณี (และอีก 2-3 รายการสําหรับชุดค่าผสมอื่นๆ) Kotlin สร้างสิ่งที่จําเป็นจากค่าเริ่มต้นและพารามิเตอร์ที่มีชื่อ

ขั้นตอนที่ 2: เพิ่มบล็อก init

เครื่องมือสร้างตัวอย่างด้านบนเป็นเพียงการประกาศพร็อพเพอร์ตี้ และกําหนดมูลค่าของนิพจน์ดังกล่าวเท่านั้น หากเครื่องมือสร้างของคุณจําเป็นต้องมีโค้ดการเริ่มต้นเพิ่มเติม คุณสามารถวางโค้ดไว้ในบล็อก init อย่างน้อย 1 รายการ ในขั้นตอนนี้ คุณจะเพิ่มบล็อก init บางบล็อกไปยัง Aquarium ชั้นเรียน

  1. ในชั้นเรียน Aquarium ให้เพิ่มบล็อก init เพื่อพิมพ์ว่าวัตถุกําลังเริ่มต้นและเพิ่มบล็อกที่ 2 เพื่อพิมพ์ปริมาตรเป็นลิตร
class Aquarium (var length: Int = 100, var width: Int = 20, var height: Int = 40) {
    init {
        println("aquarium initializing")
    }
    init {
        // 1 liter = 1000 cm^3
        println("Volume: ${width * length * height / 1000} l")
    }
}
  1. เรียกใช้โปรแกรมและสังเกตเอาต์พุต
aquarium initializing
Volume: 80 l
Width: 20 cm Length: 100 cm Height: 40 cm 
aquarium initializing
Volume: 100 l
Width: 25 cm Length: 100 cm Height: 40 cm 
aquarium initializing
Volume: 77 l
Width: 20 cm Length: 110 cm Height: 35 cm 
aquarium initializing
Volume: 96 l
Width: 25 cm Length: 110 cm Height: 35 cm 

โปรดสังเกตว่าบล็อก init จะทํางานตามลําดับที่ปรากฏในคําจํากัดความของชั้นเรียน และบล็อกทั้งหมดเมื่อมีการเรียกใช้ตัวสร้าง

ขั้นตอนที่ 3: ดูข้อมูลเกี่ยวกับเครื่องมือสร้างรอง

ในขั้นตอนนี้ คุณจะได้เรียนรู้เกี่ยวกับเครื่องมือสร้างสํารองและเพิ่มลงในคลาส นอกเหนือจากเครื่องมือสร้างหลักที่มีบล็อก init ได้อย่างน้อย 1 บล็อกแล้ว คลาส Kotlin อาจมีตัวสร้างรองอย่างน้อย 1 ตัวเพื่ออนุญาตเครื่องมือสร้างที่มากเกินไป ซึ่งก็คือตัวสร้างที่มีอาร์กิวเมนต์ต่างกัน

  1. ในคลาส Aquarium ให้เพิ่มเครื่องมือสร้างรองที่มีจํานวนปลาเป็นอาร์กิวเมนต์โดยใช้คีย์เวิร์ด constructor สร้างพร็อพเพอร์ตี้บ่อปลา val สําหรับปริมาณตู้ปลาที่คํานวณได้ในหน่วยลิตรตามจํานวนปลา สมมติว่าน้ํา 2 ลิตร (2,000 ซม. 3 ลิตร) ต่อปลา และเพิ่มห้องอีกเล็กน้อยเพื่อให้น้ําหกใส่ไม่ได้
constructor(numberOfFish: Int) : this() {
    // 2,000 cm^3 per fish + extra room so water doesn't spill
    val tank = numberOfFish * 2000 * 1.1
}
  1. ในเครื่องมือสร้างรอง ให้คงความยาวและความกว้าง (ซึ่งกําหนดไว้ในตัวสร้างหลัก) ไว้เท่าเดิม และคํานวณความสูงที่จําเป็นในการทําให้ถังมีขนาดเท่ากับ
    // calculate the height needed
    height = (tank / (length * width)).toInt()
  1. เพิ่มฟังก์ชันเพื่อสร้าง Aquarium โดยใช้ตัวสร้างสํารองรายการใหม่ในฟังก์ชัน buildAquarium() พิมพ์ขนาดและปริมาณ
fun buildAquarium() {
    val aquarium6 = Aquarium(numberOfFish = 29)
    aquarium6.printSize()
    println("Volume: ${aquarium6.width * aquarium6.length * aquarium6.height / 1000} l")
}
  1. เรียกใช้โปรแกรมและสังเกตเอาต์พุต
⇒ aquarium initializing
Volume: 80 l
Width: 20 cm Length: 100 cm Height: 31 cm 
Volume: 62 l

โปรดสังเกตว่าพิมพ์วอลุ่ม 2 ครั้ง 1 ครั้ง ได้แก่ บล็อก init ในเครื่องมือสร้างหลักก่อนเรียกใช้เครื่องมือสร้างรอง และ 1 ครั้งด้วยโค้ดใน buildAquarium()

คุณสามารถใส่คีย์เวิร์ด constructor ในเครื่องมือสร้างหลักได้เช่นกัน แต่ในกรณีส่วนใหญ่แล้ว ไม่จําเป็นต้องใช้

ขั้นตอนที่ 4: เพิ่ม Getter พร็อพเพอร์ตี้ใหม่

ในขั้นตอนนี้ ให้เพิ่ม Getter พร็อพเพอร์ตี้อย่างชัดแจ้ง Kotlin กําหนด Getter และ Setter โดยอัตโนมัติเมื่อคุณกําหนดพร็อพเพอร์ตี้ แต่บางครั้งคุณจะต้องปรับเปลี่ยนหรือคํานวณค่าของพร็อพเพอร์ตี้ เช่น ด้านบนคุณพิมพ์ระดับเสียงของ Aquarium คุณสามารถกําหนดให้วอลุ่มเป็นพร็อพเพอร์ตี้ได้โดยกําหนดตัวแปรและ Getter สําหรับตัวแปร เนื่องจากต้องมีการคํานวณ volume Getter จึงต้องแสดงผลค่าที่คํานวณได้ซึ่งคุณใช้ฟังก์ชัน 1 บรรทัดได้

  1. ในชั้นเรียน Aquarium ให้กําหนดพร็อพเพอร์ตี้ Int ชื่อ volume และกําหนดเมธอด get() ที่คํานวณปริมาตรในบรรทัดถัดไป
val volume: Int
    get() = width * height * length / 1000  // 1000 cm^3 = 1 l
  1. นําบล็อก init ที่พิมพ์วอลุ่มออก
  2. นํารหัสใน buildAquarium() ที่พิมพ์วอลุ่มออก
  3. ด้วยวิธีการ printSize() ให้เพิ่มบรรทัดเพื่อพิมพ์วอลุ่ม
fun printSize() {
    println("Width: $width cm " +
            "Length: $length cm " +
            "Height: $height cm "
    )
    // 1 l = 1000 cm^3
    println("Volume: $volume l")
}
  1. เรียกใช้โปรแกรมและสังเกตเอาต์พุต
⇒ aquarium initializing
Width: 20 cm Length: 100 cm Height: 31 cm 
Volume: 62 l

ขนาดและปริมาตรยังคงเหมือนเดิม แต่ปริมาณจะพิมพ์เพียงครั้งเดียวเมื่อเริ่มต้นโดยทั้งเครื่องมือสร้างหลักและตัวรองรอง

ขั้นตอนที่ 5: เพิ่มตัวตั้งค่าพร็อพเพอร์ตี้

ในขั้นตอนนี้ คุณจะต้องสร้างการตั้งค่าพร็อพเพอร์ตี้ใหม่สําหรับวอลุ่ม

  1. ในชั้นเรียน Aquarium ให้เปลี่ยน volume เป็น var เพื่อให้ตั้งค่าได้มากกว่า 1 ครั้ง
  2. เพิ่มตัวตั้งค่าสําหรับพร็อพเพอร์ตี้ volume โดยเพิ่มเมธอด set() ด้านล่าง Getter ซึ่งจะคํานวณความสูงอีกครั้งโดยอิงตามปริมาณน้ําที่ระบุ โดยปกติ ชื่อของพารามิเตอร์ Setter คือ value แต่คุณเปลี่ยนแปลงได้หากต้องการ
var volume: Int
    get() = width * height * length / 1000
    set(value) {
        height = (value * 1000) / (width * length)
    }
  1. ใน buildAquarium() ให้เพิ่มโค้ดเพื่อตั้งค่าระดับเสียงสําหรับตู้ปลาเป็น 70 ลิตร พิมพ์ขนาดใหม่
fun buildAquarium() {
    val aquarium6 = Aquarium(numberOfFish = 29)
    aquarium6.printSize()
    aquarium6.volume = 70
    aquarium6.printSize()
}
  1. เรียกใช้โปรแกรมอีกครั้งและสังเกตความสูงและระดับเสียงที่เปลี่ยนไป
⇒ aquarium initialized
Width: 20 cm Length: 100 cm Height: 31 cm 
Volume: 62 l
Width: 20 cm Length: 100 cm Height: 35 cm 
Volume: 70 l

ยังไม่มีตัวแก้ไขการปรับเปลี่ยน เช่น public หรือ private ในโค้ด นั่นเป็นเพราะค่าเริ่มต้นทั้งหมดของทุกอย่างใน Kotlin คือสาธารณะ ซึ่งหมายความว่าทุกอย่างจะเข้าถึงได้ทุกที่ รวมถึงคลาส เมธอด พร็อพเพอร์ตี้ และตัวแปรสมาชิก

ใน Kotlin คลาส ออบเจ็กต์ อินเทอร์เฟซ เครื่องมือสร้าง ฟังก์ชัน พร็อพเพอร์ตี้ และตัวตั้งค่าจะมีตัวปรับแต่งระดับการเข้าถึงได้ ดังนี้

  • public หมายถึงแสดงต่อผู้ใช้ภายนอกชั้นเรียน ข้อมูลทุกอย่างจะเป็นแบบสาธารณะโดยค่าเริ่มต้น รวมถึงตัวแปรและวิธีการในชั้นเรียน
  • internal หมายความว่าจะแสดงภายในโมดูลนั้นเท่านั้น โมดูลคือชุดไฟล์ Kotlin ที่คอมไพล์ไว้ด้วยกัน เช่น ไลบรารีหรือแอปพลิเคชัน
  • private หมายความว่าจะปรากฏในชั้นเรียนดังกล่าวเท่านั้น (หรือไฟล์ต้นฉบับหากคุณใช้งานฟังก์ชัน)
  • protected เหมือนกับ private แต่จะปรากฏแก่คลาสย่อยด้วย

ดูข้อมูลเพิ่มเติมได้ที่ตัวปรับแต่งระดับการเข้าถึงในเอกสาร Kotlin

ตัวแปรสมาชิก

พร็อพเพอร์ตี้ภายในชั้นเรียนหรือตัวแปรสมาชิกจะมีค่าเริ่มต้นเป็น public หากกําหนดด้วย var ก็จะเปลี่ยนแปลงได้ ทั้งอ่านและเขียนได้ หากกําหนดโดยใช้ val นโยบายจะเป็นแบบอ่านอย่างเดียวหลังจากการเริ่มต้น

หากต้องการให้พร็อพเพอร์ตี้อ่านหรือเขียนโค้ดได้ แต่โค้ดภายนอกจะอ่านได้อย่างเดียว คุณปล่อยพร็อพเพอร์ตี้และ Getter ให้เป็นสาธารณะและประกาศตัวตั้งค่าได้ดังที่แสดงด้านล่าง

var volume: Int
    get() = width * height * length / 1000
    private set(value) {
        height = (value * 1000) / (width * length)
    }

ในงานนี้ คุณจะได้ศึกษาวิธีการทํางานของคลาสย่อยและการสืบทอดใน Kotlin ซึ่งคล้ายกับภาษาที่คุณเห็นในภาษาอื่น แต่มีข้อแตกต่างบางประการ

ใน Kotlin โดยค่าเริ่มต้นจะเป็นคลาสย่อยไม่ได้ ในทํานองเดียวกัน คลาสย่อยและตัวแปรสมาชิกจะถูกลบล้างไม่ได้ (แต่จะเข้าถึงได้)

คุณต้องทําเครื่องหมายชั้นเรียนเป็น open เพื่ออนุญาตชั้นเรียนย่อย ในทํานองเดียวกัน คุณต้องทําเครื่องหมายพร็อพเพอร์ตี้และตัวแปรสมาชิกเป็น open เพื่อลบล้างค่าเหล่านี้ในคลาสย่อย ต้องใช้คีย์เวิร์ด open เพื่อป้องกันไม่ให้รายละเอียดการใช้งานรั่วไหลโดยไม่ได้ตั้งใจโดยเป็นส่วนหนึ่งของอินเทอร์เฟซของคลาส

ขั้นตอนที่ 1: เปิดชั้นเรียนตู้ปลา

ในขั้นตอนนี้ คุณจะสร้างชั้นเรียน Aquarium open เพื่อลบล้างในขั้นตอนถัดไป

  1. ทําเครื่องหมายคลาส Aquarium และพร็อพเพอร์ตี้ทั้งหมดด้วยคีย์เวิร์ด open
open class Aquarium (open var length: Int = 100, open var width: Int = 20, open var height: Int = 40) {
    open var volume: Int
        get() = width * height * length / 1000
        set(value) {
            height = (value * 1000) / (width * length)
        }
  1. เพิ่มพร็อพเพอร์ตี้ shape ที่เปิดอยู่ซึ่งมีค่า "rectangle"
   open val shape = "rectangle"
  1. เพิ่มพร็อพเพอร์ตี้ water แบบเปิดที่มี Getter ที่แสดง 90% ของปริมาณของ Aquarium
    open var water: Double = 0.0
        get() = volume * 0.9
  1. เพิ่มโค้ดลงในเมธอด printSize() เพื่อพิมพ์รูปร่างและปริมาณน้ําเป็นเปอร์เซ็นต์ของปริมาตร
fun printSize() {
    println(shape)
    println("Width: $width cm " +
            "Length: $length cm " +
            "Height: $height cm ")
    // 1 l = 1000 cm^3
    println("Volume: $volume l Water: $water l (${water/volume*100.0}% full)")
}
  1. ใน buildAquarium() เปลี่ยนโค้ดเพื่อสร้าง Aquarium ที่มี width = 25, length = 25 และ height = 40
fun buildAquarium() {
    val aquarium6 = Aquarium(length = 25, width = 25, height = 40)
    aquarium6.printSize()
}
  1. เรียกใช้โปรแกรมและสังเกตเอาต์พุตใหม่
⇒ aquarium initializing
rectangle
Width: 25 cm Length: 25 cm Height: 40 cm 
Volume: 25 l Water: 22.5 l (90.0% full)

ขั้นตอนที่ 2: สร้างคลาสย่อย

  1. สร้างคลาสย่อยของ Aquarium ที่ชื่อ TowerTank ซึ่งใช้ถังทรงกลมแทนถังทรงสี่เหลี่ยมผืนผ้า คุณสามารถเพิ่ม TowerTank ด้านล่าง Aquarium ได้ เนื่องจากคุณเพิ่มชั้นเรียนอื่นในไฟล์เดียวกับชั้นเรียนของ Aquarium ได้
  2. ใน TowerTank ให้ลบล้างพร็อพเพอร์ตี้ height ซึ่งกําหนดไว้ในเครื่องมือสร้าง หากต้องการลบล้างพร็อพเพอร์ตี้ ให้ใช้คีย์เวิร์ด override ในคลาสย่อย
  1. ทําให้เครื่องมือสร้างสําหรับ TowerTank ใช้ diameter ใช้ diameter สําหรับทั้ง length และ width เมื่อเรียกใช้เครื่องมือสร้างในซูเปอร์คลาส Aquarium
class TowerTank (override var height: Int, var diameter: Int): Aquarium(height = height, width = diameter, length = diameter) {
  1. ลบล้างพร็อพเพอร์ตี้ระดับเสียงเพื่อคํานวณทรงกระบอก สูตรสําหรับทรงกระบอกคือพายคูณรัศมียกกําลัง 2 คูณความสูง คุณต้องนําเข้าค่าคงที่ PI จาก java.lang.Math
    override var volume: Int
    // ellipse area = π * r1 * r2
    get() = (width/2 * length/2 * height / 1000 * PI).toInt()
    set(value) {
        height = ((value * 1000 / PI) / (width/2 * length/2)).toInt()
    }
  1. ใน TowerTank ให้ลบล้างพร็อพเพอร์ตี้ water เป็น 80% ของระดับเสียง
override var water = volume * 0.8
  1. ลบล้าง shape เป็น "cylinder"
override val shape = "cylinder"
  1. คลาส TowerTank สุดท้ายควรมีลักษณะคล้ายกับโค้ดด้านล่าง

Aquarium.kt:

package example.myapp

import java.lang.Math.PI

... // existing Aquarium class

class TowerTank (override var height: Int, var diameter: Int): Aquarium(height = height, width = diameter, length = diameter) {
    override var volume: Int
    // ellipse area = π * r1 * r2
    get() = (width/2 * length/2 * height / 1000 * PI).toInt()
    set(value) {
        height = ((value * 1000 / PI) / (width/2 * length/2)).toInt()
    }

    override var water = volume * 0.8
    override val shape = "cylinder"
}
  1. ใน buildAquarium() ให้สร้าง TowerTank ซึ่งมีเส้นผ่านศูนย์กลาง 25 ซม. และสูง 45 ซม. พิมพ์ขนาด

main.kt:

package example.myapp

fun buildAquarium() {
    val myAquarium = Aquarium(width = 25, length = 25, height = 40)
    myAquarium.printSize()
    val myTower = TowerTank(diameter = 25, height = 40)
    myTower.printSize()
}
  1. เรียกใช้โปรแกรมและสังเกตเอาต์พุต
⇒ aquarium initializing
rectangle
Width: 25 cm Length: 25 cm Height: 40 cm 
Volume: 25 l Water: 22.5 l (90.0% full)
aquarium initializing
cylinder
Width: 25 cm Length: 25 cm Height: 40 cm 
Volume: 18 l Water: 14.4 l (80.0% full)

บางครั้งคุณต้องการกําหนดพฤติกรรมทั่วไปหรือพร็อพเพอร์ตี้ที่จะแชร์ไปยังชั้นเรียนที่เกี่ยวข้องบางชั้น Kotlin มี 2 วิธีที่ทําได้คือ อินเทอร์เฟซและชั้นเรียนนามธรรม ในงานนี้ คุณจะได้สร้างคลาส AquariumFish นามธรรมสําหรับพร็อพเพอร์ตี้ที่มักพบในปลาทั้งหมด คุณสร้างอินเทอร์เฟซชื่อ FishAction เพื่อกําหนดลักษณะการทํางานทั่วไปสําหรับปลาทั้งหมด

  • จะไม่สามารถแยกคลาสที่เป็นนามธรรมและอินเทอร์เฟซด้วยตนเองได้ ซึ่งหมายความว่าคุณสร้างออบเจ็กต์ประเภทเหล่านั้นโดยตรงไม่ได้
  • ชั้นเรียนนามธรรมมีเครื่องมือสร้าง
  • อินเทอร์เฟซต้องไม่มีตรรกะตัวสร้างหรือจัดเก็บสถานะใดๆ

ขั้นตอนที่ 1 สร้างชั้นเรียนนามธรรม

  1. สร้างไฟล์ใหม่ชื่อ AquariumFish.kt ใน example.myapp
  2. สร้างชั้นเรียนหรือที่เรียกว่า AquariumFish และทําเครื่องหมายด้วย abstract
  3. เพิ่มพร็อพเพอร์ตี้ String ที่ชื่อ color และทําเครื่องหมายด้วย abstract
package example.myapp

abstract class AquariumFish {
    abstract val color: String
}
  1. สร้างชั้นเรียนย่อยของ AquariumFish, Shark และ Plecostomus
  2. เนื่องจาก color เป็นนามธรรม คลาสย่อยจึงต้องนําไปใช้ ทําให้ Shark เป็นสีเทาและ Plecostomus สีทอง
class Shark: AquariumFish() {
    override val color = "gray"
}

class Plecostomus: AquariumFish() {
    override val color = "gold"
}
  1. ใน main.kt ให้สร้างฟังก์ชัน makeFish() เพื่อทดสอบชั้นเรียน ระบุ Shark และ Plecostomus แล้วพิมพ์สีของแต่ละรายการ
  2. ลบรหัสการทดสอบก่อนหน้านี้ใน main() และเพิ่มการโทรไปที่ makeFish() โค้ดควรมีลักษณะดังนี้

main.kt:

package example.myapp

fun makeFish() {
    val shark = Shark()
    val pleco = Plecostomus()

    println("Shark: ${shark.color}")
    println("Plecostomus: ${pleco.color}")
}

fun main () {
    makeFish()
}
  1. เรียกใช้โปรแกรมและสังเกตเอาต์พุต
⇒ Shark: gray 
Plecostomus: gold

แผนภาพต่อไปนี้แสดงคลาส Shark และ Plecostomus ซึ่งเป็นคลาสย่อยของนามธรรม AquariumFish

แผนภาพแสดงชั้นเรียนนามธรรม พิพิธภัณฑ์สัตว์น้ํา และคลาสย่อย 2 ชั้น ได้แก่ ฉลามและฉลาม

ขั้นตอนที่ 2 สร้างอินเทอร์เฟซ

  1. ใน AquariumFish.kt ให้สร้างอินเทอร์เฟซชื่อ FishAction ด้วยเมธอด eat()
interface FishAction  {
    fun eat()
}
  1. เพิ่ม FishAction ลงในแต่ละคลาสย่อยและใช้ eat() โดยพิมพ์สิ่งที่ปลาทํา
class Shark: AquariumFish(), FishAction {
    override val color = "gray"
    override fun eat() {
        println("hunt and eat fish")
    }
}

class Plecostomus: AquariumFish(), FishAction {
    override val color = "gold"
    override fun eat() {
        println("eat algae")
    }
}
  1. ในฟังก์ชัน makeFish() ให้ปลาแต่ละตัวที่คุณสร้างกินบางอย่างด้วยการเรียก eat()
fun makeFish() {
    val shark = Shark()
    val pleco = Plecostomus()
    println("Shark: ${shark.color}")
    shark.eat()
    println("Plecostomus: ${pleco.color}")
    pleco.eat()
}
  1. เรียกใช้โปรแกรมและสังเกตเอาต์พุต
⇒ Shark: gray
hunt and eat fish
Plecostomus: gold
eat algae

แผนภาพต่อไปนี้แสดงคลาส Shark และคลาส Plecostomus ซึ่งทั้ง 2 รายการนี้ประกอบขึ้นและใช้อินเทอร์เฟซ FishAction

กรณีที่ควรใช้ชั้นเรียนนามธรรมและอินเทอร์เฟซ

ตัวอย่างข้างต้นก็เรียบง่าย แต่เมื่อคุณมีชั้นเรียนที่เกี่ยวโยงกันเป็นจํานวนมาก ชั้นเรียนและนามธรรมที่มีนามธรรมจะช่วยให้คุณออกแบบให้สะอาดตา เป็นระเบียบมากขึ้น และดูแลรักษาได้ง่ายขึ้น

ดังที่ระบุไว้ข้างต้น ชั้นเรียนในนามธรรมอาจมีเครื่องมือสร้างและอินเทอร์เฟซได้ แต่อินเทอร์เฟซจะมีความคล้ายคลึงกันอย่างมาก เราควรใช้แต่ละรายการเมื่อใด

เมื่อใช้อินเทอร์เฟซในการเขียนชั้นเรียน ระบบจะขยายฟังก์ชันการทํางานของคลาสตามอินสแตนซ์ของชั้นเรียนที่มี การเรียบเรียงมีแนวโน้มที่จะทําให้โค้ดใช้ซ้ําและเหตุผลได้มากกว่าการสืบทอดจากคลาสนามธรรม นอกจากนี้คุณยังใช้อินเทอร์เฟซหลายรายการในชั้นเรียนหนึ่งชั้นเรียนได้อีกด้วย แต่สามารถย่อยได้เฉพาะจากนามธรรมเดียว

การเรียบเรียงเพลงมักทําให้การสรุปเนื้อหาดีขึ้น ทําให้การเชื่อมต่อ (สอดคล้องกัน) อินเทอร์เฟซดูสะอาดตามากขึ้น และโค้ดที่ใช้งานได้มากขึ้น ด้วยเหตุนี้ การใช้การออกแบบร่วมกับอินเทอร์เฟซจึงเป็นการออกแบบที่แนะนํามากกว่า ในทางตรงกันข้าม มรดกจากชั้นเรียนเชิงนามธรรมมักจะเหมาะสมกับปัญหาบางอย่าง ดังนั้น คุณจึงควรเลือกใช้การเรียบเรียง แต่เมื่อการสืบทอดรู้สึกว่า Kotlin ก็สามารถทําแบบนั้นได้ด้วยเช่นกัน

  • ใช้อินเทอร์เฟซหากคุณมีวิธีการหลายอย่างและการใช้ค่าเริ่มต้น 1 หรือ 2 รายการ เช่น ใน AquariumAction ด้านล่าง
interface AquariumAction {
    fun eat()
    fun jump()
    fun clean()
    fun catchFish()
    fun swim()  {
        println("swim")
    }
}
  • ใช้ชั้นเรียนนามธรรมได้ทุกเมื่อในชั้นเรียน เช่น การกลับไปที่ชั้นเรียน AquariumFish อาจทําให้ AquariumFish ทั้งหมดใช้ FishAction และให้การใช้งาน eat เริ่มต้นในขณะที่ใช้งานนามธรรม color ได้ด้วย เนื่องจากไม่มีสีเริ่มต้นจริงๆ สําหรับปลา
interface FishAction  {
    fun eat()
}

abstract class AquariumFish: FishAction {
   abstract val color: String
   override fun eat() = println("yum")
}

งานก่อนหน้านี้ได้แนะนําชั้นเรียนเชิงนามธรรม อินเทอร์เฟซ และแนวคิดการประพันธ์ การมอบสิทธิ์อินเทอร์เฟซคือเทคนิคขั้นสูงที่ออบเจ็กต์ผู้ช่วย (หรือผู้รับมอบสิทธิ์) จะใช้เมธอดของอินเทอร์เฟซ ซึ่งชั้นเรียนจะใช้หลังจากนั้น เทคนิคนี้จะมีประโยชน์เมื่อใช้อินเทอร์เฟซในชุดชั้นเรียนที่ไม่เกี่ยวข้อง กล่าวคือคุณเพิ่มฟังก์ชันการทํางานที่จําเป็นของอินเทอร์เฟซให้กับชั้นเรียนผู้ช่วยแยกต่างหาก และแต่ละชั้นเรียนจะใช้อินสแตนซ์ของคลาสผู้ช่วยในการใช้ฟังก์ชันได้

ในงานนี้ คุณจะใช้การมอบสิทธิ์อินเทอร์เฟซเพื่อเพิ่มฟังก์ชันให้กับชั้นเรียน

ขั้นตอนที่ 1: สร้างอินเทอร์เฟซใหม่

  1. ในคลาส AquariumFish.kt ให้นําคลาส AquariumFish ออก Plecostomus และ Shark จะรับค่าเดิมจากคลาส AquariumFish ไปใช้อินเทอร์เฟซกับการดําเนินการของปลาและสี
  2. สร้างอินเทอร์เฟซใหม่ FishColor ที่กําหนดสีเป็นสตริง
interface FishColor {
    val color: String
}
  1. เปลี่ยน Plecostomus เพื่อใช้อินเทอร์เฟซ 2 รายการ ได้แก่ FishAction และ FishColor คุณต้องลบล้าง color จาก FishColor และ eat() จาก FishAction
class Plecostomus: FishAction, FishColor {
    override val color = "gold"
    override fun eat() {
        println("eat algae")
    }
}
  1. เปลี่ยนคลาส Shark เพื่อใช้อินเทอร์เฟซ 2 รายการได้แก่ FishAction และ FishColor แทนการรับค่าจาก AquariumFish
class Shark: FishAction, FishColor {
    override val color = "gray"
    override fun eat() {
        println("hunt and eat fish")
    }
}
  1. โค้ดที่เสร็จแล้วควรจะมีลักษณะดังนี้
package example.myapp

interface FishAction {
    fun eat()
}

interface FishColor {
    val color: String
}

class Plecostomus: FishAction, FishColor {
    override val color = "gold"
    override fun eat() {
        println("eat algae")
    }
}

class Shark: FishAction, FishColor {
    override val color = "gray"
    override fun eat() {
        println("hunt and eat fish")
    }
}

ขั้นตอนที่ 2: สร้างชั้นเรียนเดียว

ต่อไป คุณจะทําการตั้งค่าสําหรับส่วนการมอบสิทธิ์โดยสร้างคลาสผู้ช่วยเหลือที่นํา FishColor ไปใช้ คุณสร้างชั้นเรียนพื้นฐานชื่อ GoldColor ที่ใช้ FishColor เพียงแต่บอกว่ามีสีเป็นสีทอง

การสร้าง GoldColor หลายอินสแตนซ์เป็นเรื่องที่ไม่เหมาะสม เนื่องจากทั้ง 2 อย่างนี้เหมือนกันทุกประการ ดังนั้น Kotlin จึงให้คุณประกาศคลาสที่คุณสามารถสร้างอินสแตนซ์ได้เพียง 1 อินสแตนซ์โดยใช้คีย์เวิร์ด object แทน class Kotlin จะสร้างอินสแตนซ์นั้น 1 รายการ และอินสแตนซ์ดังกล่าวอ้างอิงโดยชื่อคลาส จากนั้นออบเจ็กต์อื่นๆ ทั้งหมดจะใช้อินสแตนซ์นี้ได้เพียงครั้งเดียว จะไม่สามารถสร้างอินสแตนซ์อื่นๆ ของคลาสนี้ได้ หากคุณคุ้นเคยกับรูปแบบซิงเกิลตัน นี่คือวิธีการนํา Singleton มาใช้ใน Kotlin

  1. ใน AquariumFish.kt ให้สร้างออบเจ็กต์สําหรับ GoldColor ลบล้างสี
object GoldColor : FishColor {
   override val color = "gold"
}

ขั้นตอนที่ 3: เพิ่มการมอบสิทธิ์อินเทอร์เฟซสําหรับ FishColor

ขณะนี้คุณพร้อมที่จะใช้การมอบสิทธิ์อินเทอร์เฟซแล้ว

  1. ใน AquariumFish.kt ให้นําการลบล้างของ color ออกจาก Plecostomus
  2. เปลี่ยนคลาส Plecostomus เพื่อใช้สีจาก GoldColor ซึ่งทําได้โดยเพิ่ม by GoldColor ไปยังการประกาศชั้นเรียนเพื่อสร้างการมอบสิทธิ์ แทนที่จะใช้ FishColor ให้ติดตั้งใช้งานตาม GoldColor ดังนั้นทุกครั้งที่มีการเข้าถึง color ระบบจึงให้สิทธิ์แก่ GoldColor
class Plecostomus:  FishAction, FishColor by GoldColor {
   override fun eat() {
       println("eat algae")
   }
}

ตามระดับชั้น Pleco ทั้งหมดจะเป็นสีทอง แต่จริงๆ แล้วปลาพวกนี้มีหลายสี คุณสามารถแก้ไขปัญหานี้ได้โดยเพิ่มพารามิเตอร์เครื่องมือสร้างสีโดย GoldColor เป็นสีเริ่มต้นสําหรับ Plecostomus

  1. เปลี่ยนคลาส Plecostomus ให้ผ่านใน fishColor ด้วยเครื่องมือสร้าง และตั้งเป็นค่าเริ่มต้นเป็น GoldColor เปลี่ยนการมอบสิทธิ์จาก by GoldColor เป็น by fishColor
class Plecostomus(fishColor: FishColor = GoldColor):  FishAction,
       FishColor by fishColor {
   override fun eat() {
       println("eat algae")
   }
}

ขั้นตอนที่ 4: เพิ่มการมอบสิทธิ์อินเทอร์เฟซสําหรับ FishAction

คุณสามารถใช้การมอบสิทธิ์อินเทอร์เฟซสําหรับ FishAction ในลักษณะเดียวกันได้

  1. ใน AquariumFish.kt สร้างคลาส PrintingFishAction ที่ใช้ FishAction ซึ่งต้องใช้ String food และพิมพ์สิ่งที่ปลากิน
class PrintingFishAction(val food: String) : FishAction {
    override fun eat() {
        println(food)
    }
}
  1. ในคลาส Plecostomus ให้นําฟังก์ชันการลบล้าง eat() ออก เนื่องจากคุณจะแทนที่ด้วยการมอบสิทธิ์
  2. ในการประกาศ Plecostomus ระบบจะมอบสิทธิ์ FishAction ให้ PrintingFishAction โดยผ่าน "eat algae"
  3. เมื่อใช้การมอบสิทธิ์ดังกล่าว จะไม่มีโค้ดในส่วนเนื้อหาของคลาส Plecostomus ดังนั้นให้นํา {} ออก เนื่องจากการลบล้างทั้งหมดจะดําเนินการโดยการมอบสิทธิ์อินเทอร์เฟซ
class Plecostomus (fishColor: FishColor = GoldColor):
        FishAction by PrintingFishAction("eat algae"),
        FishColor by fishColor

แผนภาพต่อไปนี้แสดงคลาส Shark และ Plecostomus ซึ่งประกอบด้วยอินเทอร์เฟซ PrintingFishAction และ FishColor แต่มอบสิทธิ์ในการใช้งาน

การมอบสิทธิ์อินเทอร์เฟซนั้นมีประสิทธิภาพมาก และโดยปกติคุณควรเลือกวิธีการใช้งานเมื่อใดก็ตามที่ใช้ชั้นเรียนนามธรรมในภาษาอื่น วิธีนี้จะช่วยให้คุณใช้การจัดองค์ประกอบในลักษณะการทํางานต่างๆ ได้ แทนที่จะต้องจัดคลาสย่อยจํานวนมาก โดยที่แต่ละคลาสมีความเชี่ยวชาญแตกต่างกัน

คลาสข้อมูลคล้ายกับ struct ในภาษาอื่นๆ โดยหลักๆ แล้วจะมีการคงไว้ชั่วคราวข้อมูลบางอย่าง แต่ออบเจ็กต์คลาสข้อมูลยังคงเป็นออบเจ็กต์ ออบเจ็กต์คลาสข้อมูล Kotlin มีประโยชน์เพิ่มเติมบางอย่าง เช่น ยูทิลิตีสําหรับการพิมพ์และการคัดลอก ในงานนี้ คุณจะได้สร้างคลาสข้อมูลแบบง่ายๆ และเรียนรู้เกี่ยวกับการสนับสนุนที่ Kotlin มอบให้สําหรับประเภทข้อมูล

ขั้นตอนที่ 1: สร้างคลาสข้อมูล

  1. เพิ่มแพ็กเกจใหม่ decor ไว้ในแพ็กเกจ example.myapp เพื่อเก็บรหัสใหม่ คลิกขวาที่ example.myapp ในแผงโปรเจ็กต์ แล้วเลือกไฟล์ > ใหม่ > แพ็กเกจ
  2. ในแพ็กเกจ ให้สร้างชั้นเรียนใหม่ชื่อ Decoration
package example.myapp.decor

class Decoration {
}
  1. หากต้องการทําให้ Decoration เป็นคลาสข้อมูล ให้เพิ่มคํานําหน้า data ด้วยการประกาศคลาส
  2. เพิ่มพร็อพเพอร์ตี้ String ชื่อ rocks เพื่อให้ข้อมูลบางอย่างแก่ชั้นเรียน
data class Decoration(val rocks: String) {
}
  1. ภายนอกไฟล์ ให้เพิ่มฟังก์ชัน makeDecorations() เพื่อสร้างและพิมพ์อินสแตนซ์ของ Decoration ด้วย "granite"
fun makeDecorations() {
    val decoration1 = Decoration("granite")
    println(decoration1)
}
  1. เพิ่มฟังก์ชัน main() เพื่อเรียก makeDecorations() และเรียกใช้โปรแกรมของคุณ สังเกตผลลัพธ์ที่เหมาะสมซึ่งระบบสร้างขึ้นเพราะเป็นคลาสข้อมูล
⇒ Decoration(rocks=granite)
  1. ใน makeDecorations() ให้สร้างอินสแตนซ์ออบเจ็กต์ Decoration อีก 2 รายการที่เป็นทั้ง "slate&quot และพิมพ์
fun makeDecorations() {
    val decoration1 = Decoration("granite")
    println(decoration1)

    val decoration2 = Decoration("slate")
    println(decoration2)

    val decoration3 = Decoration("slate")
    println(decoration3)
}
  1. ใน makeDecorations() ให้เพิ่มคําสั่งรูปอัดที่พิมพ์ผลลัพธ์ของการเปรียบเทียบ decoration1 กับ decoration2 และคําสั่งที่ 2 เพื่อเปรียบเทียบ decoration3 กับ decoration2 ให้ใช้เมธอด equals() ที่ได้จากคลาสข้อมูล
    println (decoration1.equals(decoration2))
    println (decoration3.equals(decoration2))
  1. เรียกใช้โค้ด
⇒ Decoration(rocks=granite)
Decoration(rocks=slate)
Decoration(rocks=slate)
false
true

ขั้นตอนที่ 2 ใช้การทําลาย

หากต้องการเข้าถึงพร็อพเพอร์ตี้ของออบเจ็กต์ข้อมูลและกําหนดให้กับตัวแปร คุณอาจกําหนดออบเจ็กต์เหล่านั้นทีละ 1 รายการก็ได้ ดังนี้

val rock = decoration.rock
val wood = decoration.wood
val diver = decoration.diver

แต่คุณสามารถสร้างตัวแปร 1 รายการสําหรับแต่ละพร็อพเพอร์ตี้ และกําหนดออบเจ็กต์ข้อมูลให้กับกลุ่มตัวแปรนั้นแทน Kotlin ป้อนค่าพร็อพเพอร์ตี้ในแต่ละตัวแปร

val (rock, wood, diver) = decoration

เราเรียกสิ่งนี้ว่าการทําลายและเป็นการสรุปสั้นๆ ที่มีประโยชน์ จํานวนตัวแปรควรตรงกับจํานวนพร็อพเพอร์ตี้ และกําหนดตัวแปรตามลําดับที่ประกาศในชั้นเรียน ลองดูตัวอย่างที่สมบูรณ์ใน Decoration.kt

// Here is a data class with 3 properties.
data class Decoration2(val rocks: String, val wood: String, val diver: String){
}

fun makeDecorations() {
    val d5 = Decoration2("crystal", "wood", "diver")
    println(d5)

// Assign all properties to variables.
    val (rock, wood, diver) = d5
    println(rock)
    println(wood)
    println(diver)
}
⇒ Decoration2(rocks=crystal, wood=wood, diver=diver)
crystal
wood
diver

หากไม่ต้องการใช้พร็อพเพอร์ตี้อย่างน้อย 1 รายการ คุณจะข้ามพร็อพเพอร์ตี้ได้โดยใช้ _ แทนชื่อตัวแปรดังที่แสดงในโค้ดด้านล่าง

    val (rock, _, diver) = d5

ในงานนี้ คุณจะได้เรียนรู้เกี่ยวกับคลาสเรียนพิเศษบางคลาสใน Kotlin ได้แก่

  • ชั้นเรียน Singleton
  • Enum
  • ชั้นเรียนปิดบังหน้าเว็บจริง

ขั้นตอนที่ 1: เรียกคืนชั้นเรียนเดี่ยว

จําตัวอย่างก่อนหน้านี้ด้วยคลาส GoldColor

object GoldColor : FishColor {
   override val color = "gold"
}

เนื่องจากอินสแตนซ์ทั้งหมดของ GoldColor ทําแบบเดียวกัน จึงมีการประกาศเป็น object แทนที่จะเป็น class เพื่อให้เป็นรายการเดียว และมีได้เพียงอินสแตนซ์เดียว

ขั้นตอนที่ 2: สร้าง Enum

Kotlin รองรับ enum ด้วย ซึ่งช่วยให้คุณแจกแจงรายละเอียดและอ้างอิงถึงชื่อได้เหมือนกับภาษาอื่น ประกาศ Enum ด้วยการประกาศคํานําหน้าด้วยคีย์เวิร์ด enum การประกาศ enum พื้นฐานต้องการรายชื่อเท่านั้น แต่คุณยังกําหนดช่องอย่างน้อย 1 ช่องที่เกี่ยวข้องกับชื่อแต่ละชื่อได้ด้วย

  1. ใน Decoration.kt ให้ลองใช้ตัวอย่าง enum
enum class Color(val rgb: Int) {
   RED(0xFF0000), GREEN(0x00FF00), BLUE(0x0000FF);
}

ตัวเลขจะคล้ายกับรายการเดียว โดยใส่ได้เพียง 1 ค่าเท่านั้น และแต่ละรายการมีค่าในการแจกแจง ตัวอย่างเช่น คุณจะมี Color.RED, 1 Color.GREEN และ Color.BLUE ได้เพียง 1 รายการเท่านั้น ในตัวอย่างนี้ ระบบจะกําหนดค่า RGB ให้กับพร็อพเพอร์ตี้ rgb เพื่อแสดงคอมโพเนนต์สี นอกจากนี้คุณยังได้รับค่าตามลําดับฐานของ Enum โดยใช้พร็อพเพอร์ตี้ ordinal และชื่อของพร็อพเพอร์ตี้โดยใช้พร็อพเพอร์ตี้ name ได้ด้วย

  1. ลองใช้อีกตัวอย่างหนึ่งของ Enum
enum class Direction(val degrees: Int) {
    NORTH(0), SOUTH(180), EAST(90), WEST(270)
}

fun main() {
    println(Direction.EAST.name)
    println(Direction.EAST.ordinal)
    println(Direction.EAST.degrees)
}
⇒ EAST
2
90

ขั้นตอนที่ 3: สร้างชั้นเรียนที่ปิดบังหน้าเว็บจริง

ชั้นเรียนที่ปิดบังหน้าเว็บจริงคือชั้นเรียนที่แบ่งย่อยได้ แต่ต้องอยู่ในไฟล์ที่ประกาศไว้เท่านั้น ระบบจะแสดงข้อผิดพลาดหากพยายามเลือกชั้นเรียนย่อยในไฟล์อื่น

เนื่องจากคลาสและคลาสย่อยอยู่ในไฟล์เดียวกัน Kotlin จะทราบคลาสย่อยทั้งหมดแบบคงที่ เมื่อถึงตอนคอมไพเลอร์ คอมไพเลอร์จะเห็นคลาสและคลาสย่อยทั้งหมด และรู้ว่านี่คือทั้งหมดเพื่อให้คอมไพเลอร์ดําเนินการตรวจสอบให้คุณได้มากขึ้น

  1. ใน AquariumFish.kt ลองดูตัวอย่างคลาสที่ปิดบังหน้าเว็บจริงโดยการรักษาธีมน้ํา
sealed class Seal
class SeaLion : Seal()
class Walrus : Seal()

fun matchSeal(seal: Seal): String {
   return when(seal) {
       is Walrus -> "walrus"
       is SeaLion -> "sea lion"
   }
}

ระบบเรียนคลาส Seal ในระดับย่อยในไฟล์อื่นไม่ได้ หากต้องการเพิ่ม Seal ประเภท คุณต้องเพิ่มประเภทอื่นๆ ไว้ในไฟล์เดียวกัน วิธีนี้ทําให้ชั้นเรียนที่ปิดผนึกเป็นวิธีที่ปลอดภัยในการนําเสนอประเภทได้หลายประเภท เช่น คลาสที่ปิดผนึกได้นั้นเหมาะสําหรับความสําเร็จหรือข้อผิดพลาดเกี่ยวกับการคืนสินค้าจาก API เครือข่าย

บทเรียนนี้ครอบคลุมเรื่องมากมาย แม้ว่าภาษาส่วนใหญ่น่าจะทําความคุ้นเคยกับภาษาโปรแกรมอื่นๆ ที่เน้นวัตถุ แต่ Kotlin เพิ่มฟีเจอร์บางอย่างเพื่อให้โค้ดกระชับและอ่านได้ง่าย

ชั้นเรียนและเครื่องมือสร้าง

  • กําหนดชั้นเรียนใน Kotlin โดยใช้ class
  • Kotlin สร้าง Setter และ Getter สําหรับพร็อพเพอร์ตี้โดยอัตโนมัติ
  • กําหนดตัวสร้างหลักโดยตรงในคํานิยามของชั้นเรียน ดังตัวอย่างต่อไปนี้
    class Aquarium(var length: Int = 100, var width: Int = 20, var height: Int = 40)
  • หากเครื่องมือสร้างหลักต้องการโค้ดเพิ่มเติม ให้เขียนโค้ดไว้ในบล็อก init อย่างน้อย 1 รายการ
  • ชั้นเรียนจะกําหนดเครื่องมือสร้างรองได้มากกว่า 1 รายการโดยใช้ constructor แต่สไตล์ Kotlin คือการใช้ฟังก์ชันจากโรงงานแทน

ตัวปรับแต่งระดับการเข้าถึงและคลาสย่อย

  • ชั้นเรียนและฟังก์ชันทั้งหมดใน Kotlin จะมีค่าเริ่มต้นเป็น public แต่คุณจะใช้ตัวแก้ไขเพื่อเปลี่ยนระดับการเข้าถึงเป็น internal, private หรือ protected ได้
  • หากต้องการสร้างคลาสย่อย จะต้องทําเครื่องหมายชั้นเรียนระดับบนสุดว่า open
  • หากต้องการลบล้างเมธอดและพร็อพเพอร์ตี้ในคลาสย่อย วิธีการและพร็อพเพอร์ตี้จะต้องมีการทําเครื่องหมาย open ในชั้นเรียนระดับบนสุด
  • คลาสที่ปิดบังหน้าเว็บจริงอาจอยู่ในคลาสย่อยได้เฉพาะในไฟล์ที่กําหนดไว้เท่านั้น สร้างชั้นเรียนที่ปิดบังหน้าเว็บจริงด้วยการประกาศ sealed นําหน้า

คลาสข้อมูล ซิงเกิล และ Enum

  • สร้างคลาสข้อมูลโดยนําหน้าการประกาศด้วย data
  • การทําลายเป็นคําอธิบายสั้นๆ สําหรับกําหนดคุณสมบัติของออบเจ็กต์ data ให้กับตัวแปรต่างๆ
  • สร้างชั้นเรียนเดี่ยวโดยใช้ object แทน class
  • กําหนด enum โดยใช้ enum class

ชั้นเรียน อินเทอร์เฟซ และการมอบสิทธิ์

  • ชั้นเรียนนามธรรมและอินเทอร์เฟซเป็นการแชร์พฤติกรรมทั่วไประหว่างชั้นเรียน 2 วิธี
  • คลาสนามธรรมจะกําหนดพร็อพเพอร์ตี้และพฤติกรรม แต่ออกจากการใช้งานในคลาสย่อย
  • อินเทอร์เฟซจะกําหนดลักษณะการทํางาน และอาจจัดเตรียมการใช้งานเริ่มต้นให้กับพฤติกรรมบางส่วนหรือทั้งหมด
  • เมื่อใช้อินเทอร์เฟซในการเขียนชั้นเรียน ระบบจะขยายฟังก์ชันการทํางานของคลาสตามอินสแตนซ์ของชั้นเรียนที่มี
  • การมอบสิทธิ์อินเทอร์เฟซใช้องค์ประกอบแต่ยังมอบสิทธิ์การติดตั้งใช้งานคลาสอินเทอร์เฟซด้วย
  • การจัดองค์ประกอบเป็นวิธีที่มีประสิทธิภาพในการเพิ่มฟังก์ชันในชั้นเรียนโดยใช้การมอบสิทธิ์ในอินเทอร์เฟซ แต่ในการเรียบเรียงโดยทั่วไป ขอแนะนําให้ใช้คลาสแบบนามธรรมที่สืบทอดมาจะเหมาะกับบางปัญหามากกว่า

เอกสารประกอบเกี่ยวกับ Kotlin

หากต้องการข้อมูลเพิ่มเติมเกี่ยวกับหัวข้อใดก็ตามในหลักสูตรนี้หรือคุณติดค้าง https://kotlinlang.org คือจุดเริ่มต้นที่ดีที่สุด

บทแนะนําเกี่ยวกับ Kotlin

เว็บไซต์ https://try.kotlinlang.org มีบทแนะนําที่ครอบคลุมชื่อ Kotlin Koans ซึ่งเป็นล่ามบนเว็บ และชุดเอกสารอ้างอิงที่สมบูรณ์พร้อมตัวอย่าง

หลักสูตร Udacity

ดูหลักสูตร Udacity เกี่ยวกับหัวข้อนี้ที่หัวข้อ Kotlin Bootcamp สําหรับโปรแกรมเมอร์

IntelliJ IDEA

ดูเอกสารสําหรับ IntelliJ IDEA ได้ในเว็บไซต์ JetBrains

ส่วนนี้จะอธิบายการบ้านและรายงานสําหรับนักเรียนที่ทํางานผ่าน Codelab นี้ซึ่งเป็นส่วนหนึ่งของหลักสูตรที่นําโดยผู้สอน สิ่งที่ผู้สอนต้องทํามีดังนี้

  • มอบหมายการบ้านหากจําเป็น
  • สื่อสารกับนักเรียนเกี่ยวกับวิธีส่งงานทําการบ้าน
  • ตัดเกรดการบ้าน

ผู้สอนจะใช้คําแนะนําเหล่านี้เท่าใดก็ได้หรือตามที่ต้องการก็ได้ และสามารถกําหนดให้การบ้านอื่นๆ ที่ตนคิดว่าเหมาะสมได้

หากคุณใช้ Codelab ด้วยตัวเอง ก็ให้ใช้การบ้านเพื่อทดสอบความรู้ของคุณได้

ตอบคําถามเหล่านี้

คำถามที่ 1

ชั้นเรียนจะมีวิธีการพิเศษที่ใช้เป็นพิมพ์เขียวสําหรับสร้างออบเจ็กต์ของชั้นเรียนนั้น วิธีการเรียกว่าอะไร

▢ เครื่องมือสร้าง

▢ ผู้ฝึกสอน

▢ เครื่องมือสร้าง

▢ พิมพ์เขียว

คำถามที่ 2

ข้อความใดเกี่ยวกับอินเทอร์เฟซและชั้นเรียนนามธรรมต่อไปนี้ไม่ถูกต้อง

▢ คลาสนามธรรมมีเครื่องมือสร้างได้

▢ อินเทอร์เฟซต้องไม่มีเครื่องมือสร้าง

สร้างอินสแตนซ์อินเทอร์เฟซและชั้นเรียนนามธรรมโดยตรงได้

▢ พร็อพเพอร์ตี้นามธรรมต้องใช้โดยคลาสย่อยของนามธรรม

คำถามที่ 3

ข้อใดต่อไปนี้ไม่ใช่ตัวปรับแต่งระดับการเข้าถึง Kotlin สําหรับพร็อพเพอร์ตี้ เมธอด ฯลฯ

internal

nosubclass

protected

private

คำถามที่ 4

พิจารณาคลาสข้อมูลนี้:
data class Fish(val name: String, val species:String, val colors:String)
ข้อใดต่อไปนี้ไม่ใช่โค้ดที่ถูกต้องสําหรับการสร้างและทําลายออบเจ็กต์ Fish

val (name1, species1, colors1) = Fish("Pat", "Plecostomus", "gold")

val (name2, _, colors2) = Fish("Bitey", "shark", "gray")

val (name3, species3, _) = Fish("Amy", "angelfish", "blue and black stripes")

val (name4, species4, colors4) = Fish("Harry", "halibut")

คำถามที่ 5

สมมติว่าคุณเป็นเจ้าของสวนสัตว์ที่มีสัตว์นานาชนิดที่ต้องดูแลทั้งหมด ข้อใดต่อไปนี้ไม่ใช่ส่วนหนึ่งของการใช้เคอร์เซอร์ข้อความ

interface สําหรับอาหารประเภทต่างๆ ที่สัตว์กิน

▢ คลาส abstract Caretaker ซึ่งคุณสามารถสร้างผู้ดูแลได้หลายประเภท

interface เพื่อให้น้ําสะอาดแก่สัตว์

▢ คลาส data สําหรับรายการในกําหนดการให้อาหาร

ดําเนินการต่อในบทเรียนถัดไป: 5.1 ส่วนขยาย

ดูภาพรวมของหลักสูตร รวมถึงลิงก์ไปยัง Codelab อื่นๆ ได้ที่ "Kotlin Bootcamp สําหรับโปรแกรมเมอร์: ยินดีต้อนรับสู่หลักสูตร"