บทที่ 7 inheritance - mwit.ac.thcs/courseware/java/2_2556/tech30201_ch7_2_2556.pdf ·...

11
1 ายวิชา 30201 การเขียนโปรแกรมเชิงวัตถุ โรงเรียนมหิดลวิทยานุสรณ์ บทที7 การสืบทอด (Inheritance) หลักการห่อหุ้ม (Encapsulation) และการพ้องรูป (Polymorphism) เนื้อหา 1. การสืบทอด (Inheritance) 2. หลักการห่อหุ้ม (Encapsulation) 3. การพ้องรูป (Polymorphism) 4. หลักการ Abstraction 5. Final Methods และ Final Class จุดประสงค์การเรียนรู1. อธิบายแนวคิดของการสืบทอดและยกตัวอย่างได้ 2. อธิบายความหมายของคลาสแม่ได้ 3. เขียนโปรแกรมประกาศคลาสแม่ได้ 4. อธิบายความหมายของคลาสลูกได้ 5. เขียนโปรแกรมสร้างคลาสใหม่จากคลาสเดิมที่มีอยู่แล้วได้ 6. อธิบายแนวคิดของหลักการห่อหุ้มและยกตัวอย่างได้ 7. เขียนโปรแกรมโดยใช้หลักการห่อหุ้มได้ 8. อธิบายแนวคิดของการพ้องรูปและยกตัวอย่างได้ 9. เขียน Overriding Method เพื่อใช้งานได้ 10. อธิบายความหมายของ FinalClass ได้ ในบทที7 นีเราจะพูดถึงหลักการพื้นฐานของการเขียนโปรแกรมเชิงวัตถุ ซึ่งประกอบไปด้วย 4 หลักการ คือ หลักการห่อหุ้ม (Encapsulation), ลักการสืบทอด (Inheritance), การพ้องรู (Polymorphism) และ หลักการ Abstraction 1. การสืบทอด (Inheritance) การเขียนโปรแกรมที่ดีนั้น ควรจะมีการนําโค้ดกลับมาใช้ใหม่ให้ได้มากที่สุดเท่าที่จะทําได้ เพื่อลดความ ซ้ําซ้อนของตัวโค้ด การสืบทอดก็เป็นการนําโค้ดกลับมาใช้ใหม่รูปแบบหนึ่ง กล่าวคือ เราสามารถนําคลาสที่ถูก ประกาศไว้แล้วมาปรับปรุงแก้ไขให้เกิดเป็นคลาสใหม่ได้ โดยที่ไม่ต้องเริ่มต้นเขียนใหม่ทั้งหมด คลาสที่ถูกนํามา

Upload: others

Post on 10-Mar-2020

4 views

Category:

Documents


0 download

TRANSCRIPT

Page 1: บทที่ 7 Inheritance - mwit.ac.thcs/courseware/java/2_2556/TECH30201_ch7_2_2556.pdf · และการพ้องรูป (Polymorphism) เนื้อหา 1. การสืบทอด

1

รายวิชา ง30201 การเขียนโปรแกรมเชิงวัตถ ุ โรงเรียนมหิดลวิทยานุสรณ ์

บทที ่7 การสืบทอด (Inheritance)

หลักการห่อหุ้ม (Encapsulation) และการพ้องรูป (Polymorphism)

เนื้อหา 1. การสืบทอด (Inheritance) 2. หลักการห่อหุ้ม (Encapsulation) 3. การพ้องรูป (Polymorphism) 4. หลักการ Abstraction 5. Final Methods และ Final Class

จุดประสงค์การเรียนรู ้

1. อธิบายแนวคิดของการสืบทอดและยกตัวอย่างได้ 2. อธิบายความหมายของคลาสแม่ได ้3. เขียนโปรแกรมประกาศคลาสแม่ได้ 4. อธิบายความหมายของคลาสลูกได ้5. เขียนโปรแกรมสร้างคลาสใหม่จากคลาสเดิมที่มีอยู่แล้วได้ 6. อธิบายแนวคิดของหลักการห่อหุ้มและยกตัวอย่างได้ 7. เขียนโปรแกรมโดยใช้หลักการห่อหุ้มได้ 8. อธิบายแนวคิดของการพ้องรูปและยกตัวอย่างได ้9. เขียน Overriding Method เพื่อใช้งานได้ 10. อธิบายความหมายของ FinalClass ได้

ในบทที่ 7 นี้ เราจะพูดถึงหลักการพื้นฐานของการเขียนโปรแกรมเชิงวัตถุ ซึ่งประกอบไปด้วย 4 หลักการ คือ หลักการห่อหุ้ม (Encapsulation), หลักการสืบทอด (Inheritance), การพ้องรูป (Polymorphism) และ หลักการ Abstraction

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

Page 2: บทที่ 7 Inheritance - mwit.ac.thcs/courseware/java/2_2556/TECH30201_ch7_2_2556.pdf · และการพ้องรูป (Polymorphism) เนื้อหา 1. การสืบทอด

2

รายวิชา ง30201 การเขียนโปรแกรมเชิงวัตถ ุ โรงเรียนมหิดลวิทยานุสรณ ์

ใช้เป็นต้นแบบเรียกว่าคลาสแม่ (superclass) และคลาสที่ปรับปรุงมาจากคลาสแม่เรียกว่าคลาสลูก (subclass) โดยปกติแล้วคลาสลูกจะมี attribute และ method ที่เพิ่มเติมขึ้นมาจากคลาสแม่ จึงเหมาะสําหรับ ใช้สร้างวัตถุที่มีความเฉพาะเจาะจงมากกว่าคลาสแม่ ตัวอย่างเช่น หากเรามีคลาส BankAccount อยู่แล้ว เราสามารถนําคลาสนี้มาปรับปรุงเพื่อสร้างเป็นคลาสลูก SavingAccount, CheckingAccount, CertificateOfDeposit ได้โดยที่คลาสลูกนี้จะสืบทอด attribute และ method มาจากคลาสแม่ได้เลย และ ยังสามารถเพิ่ม attribute หรือ method ใหม่ๆที่เฉพาะเจาะจงของคลาสนั้นๆเข้าไปได้ด้วย นอกจากนี้เรายัง สามารถนําคลาสเหล่านี้ไปสร้างคลาสลูกต่อไปได้เรื่อยๆ หากเราเขียนความสัมพันธ์ระหว่างคลาสแม่และคลาสลูกออกมาเป็นแผนภูมิ เราจะได้แผนภูมิต้นไม้ แบบมีลําดับขั้นดังตัวอย่างต่อไปนี้

จะเห็นว่าคลาสแม่นั้นสามารถมีคลาสลูกได้หลายคลาส แต่ว่าคลาสลูกแต่ละคลาสจะสามารถสืบทอดมาจาก คลาสแม่ได้เพียงแค่คลาสเดียวเท่านั้น

เนื่องจากคลาสลูกนั้นมีความจําเพาะเจาะจงมากกว่าคลาสแม ่เราอาจกล่าวได้ว่าออบเจกต์ของคลาส ลูกนั้นก็เป็นออบเจกต์ของคลาสแม่ด้วยเช่นกัน ตัวอย่างเช่น SavingAccount เป็นคลาสลูกของคลาส BankAccount ดังนั้นออบเจกต์ของคลาส SavingAccount จึงเป็นออบเจกต์ของคลาส BankAccount ด้วย แต่ในทางกลับกัน ออบเจกต์ของคลาส BankAccount นั้นไม่จําเป็นที่จะต้องเป็นออบเจกต์ของคลาส SavingAccount (พูดอย่างง่ายๆได้ว่า บัญชีออมทรัพย์เป็นบัญชีธนาคาร แต่บัญชีธนาคารไม่จําเป็นต้องเป็น บัญชีออมทรัพย์) ในตัวอย่างต่อไปนี้ เราจะสร้างคลาสชื่อ BankAccount ขึ้นมาสําหรับเก็บข้อมูลบัญชีธนาคาร ซึ่ง ประกอบไปด้วย attribute 3 ตัวคือชื่อลูกค้า (customerName), เลขบัญชี (accountNumber) และยอดเงิน คงเหลือ (balance) โดยมีเมธอดสําหรับฝากเงิน (deposit) และถอนเงิน (withdraw) และ accessor method (getCustomerName, getAccountNumber, getBalance) และ mutator method (setCustomerName, setAccountNumber, setBalance) สําหรับแต่ละ attribute รวมถึง constructor สําหรับสร้างออบเจกต์ โดยเราสามารถเขียนเป็นโค้ดได้ดังนี้ /* BankAccount.java */

1 2 3 4 5 6

public class BankAccount { private String customerName; private String accountNumber; private double balance; public BankAccount(String name, String number, double b) {

Page 3: บทที่ 7 Inheritance - mwit.ac.thcs/courseware/java/2_2556/TECH30201_ch7_2_2556.pdf · และการพ้องรูป (Polymorphism) เนื้อหา 1. การสืบทอด

3

รายวิชา ง30201 การเขียนโปรแกรมเชิงวัตถ ุ โรงเรียนมหิดลวิทยานุสรณ ์

7 8 9

10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54

customerName = name; accountNumber = number; balance = b; } // Accessor methods public String getCustomerName() { return customerName; } public String getAccountNumber() { return accountNumber; } public double getBalance() { return balance; } // Mutator methods public void setCustomerName(String name) { customerName = name; } public void setAccountNumber(String number) { accountNumber = number; } public void setBalance(double b) { balance = b; } public void withdraw(double amount) { if (amount < 0) { System.out.println("The amount to withdraw can't be negative."); } else if (balance-amount < 0) { System.out.println("Sorry, balance can't be negative."); } else { balance -= amount; } } public void deposit(double amount) { if (amount < 0) { System.out.println("The amount to deposit can't be negative."); } else { balance += amount; } } }

ตัวอย่างที ่1: คลาส BankAccount

หากเราต้องการจะสร้างคลาสสําหรับเก็บข้อมูลบัญชีกระแสรายวัน (CheckingAccount) ซึ่งอนุญาต

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

Page 4: บทที่ 7 Inheritance - mwit.ac.thcs/courseware/java/2_2556/TECH30201_ch7_2_2556.pdf · และการพ้องรูป (Polymorphism) เนื้อหา 1. การสืบทอด

4

รายวิชา ง30201 การเขียนโปรแกรมเชิงวัตถ ุ โรงเรียนมหิดลวิทยานุสรณ ์

เพื่อลดการเขียนโค้ดซ้ําซ้อน โดยปรับปรุงจากคลาส BankAccount ตรงส่วนที่เราต้องการเท่านั้น ดังตัวอย่าง ต่อไปนี้ */ CheckingAccount.java */

1 2 3 4 5 6 7 8 9

10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30

public class CheckingAccount extends BankAccount { private double overdraftFee; public CheckingAccount(String name, String number, double b, double fee) { super(name, number, b); overdraftFee = fee; } public double getOverdraftFee() { return overdraftFee; } public void setOverdraftFee(double fee) { if (fee > 0) overdraftFee = fee; } @Override public void withdraw(double amount) { double b = getBalance(); if (amount < 0) { System.out.println("The amount to withdraw can't be negative."); } else if (b-amount < 0) { setBalance(b - amount - overdraftFee); } else { setBalance(b - amount); } } }

ตัวอย่างที ่2: คลาส CheckingAccount ซึ่งเป็นคลาสลูกของคลาส BankAccount

การสืบทอดนั้นทําได้โดยใช้คีย์เวิร์ด extends ตามด้วยชื่อคลาสที่เราต้องการสืบทอด เช่นบรรทัดที่ 1

ในตัวอย่างที่ 2 คลาส CheckingAccount จะได้รับ attribute และ method มาจากคลาส BankAccount โดยที่ไม่ต้องเขียนซ้ํา (แต่มีข้อจํากัดคือถ้าหาก attribute ที่สืบทอดมานั้นมี access modifier ที่คลาสแมเ่ป็น private เราจะไม่สามารถอ้างถึง attribute โดยตรงที่คลาสลูกได้ แต่จะต้องใช้งาน ผ่าน accessor และ mutator method ของ attribute นั้นๆ เช่นตัวอย่างที ่2 บรรทัดที่ 19, 24, 27) นอกจากนี้เรายังเพิ่ม attribute ชื่อ overdraftFee เข้าไปให้กับคลาส CheckingAccount ด้วย ในการสืบทอดนั้น constructor ของคลาสแม่จะไม่ถ่ายทอดให้กับคลาสลูก เพราะฉะนั้นคลาสลูกจะ ต้องสร้าง constructor ของตัวเองขึ้นมาเอง แต่ constructor ของคลาสลูกจะต้องเรียก constructor ของ คลาสแม่เป็นอันดับแรกเสมอ เพื่อกําหนดค่าเริ่มต้นให้กับ attribute ที่สืบทอดมา ซึ่งทําได้โดยใช้คีย์เวิร์ด super(…) โดยใส่ argument ที่ต้องการส่งให้ constructor ของคลาสแม่ไว้ภายในวงเล็บ (ตัวอย่างที ่2 บรรทัดที่ 5) ถ้าหากเราไม่ทําการเรียก constructor ของคลาสแม่ด้วยตัวเอง จาวาจะเรียก default

Page 5: บทที่ 7 Inheritance - mwit.ac.thcs/courseware/java/2_2556/TECH30201_ch7_2_2556.pdf · และการพ้องรูป (Polymorphism) เนื้อหา 1. การสืบทอด

5

รายวิชา ง30201 การเขียนโปรแกรมเชิงวัตถ ุ โรงเรียนมหิดลวิทยานุสรณ ์

constructor ของคลาสแม่ให้กับเรา (ซึ่งในตัวอย่างนี้จะเกิด compilation error เพราะว่าในคลาส BankAccount ไม่มี default constructor) สําหรับคลาส BankAccount นั้น จะไม่อนุญาตให้ถอนเงินเกินยอดเงินคงเหลือได้ แต่สําหรับคลาส CheckingAccount นั้น จะอนุญาตให้ทําได้แต่มีค่าธรรมเนียมเพิ่ม โดยแก้ไขเมธอด withdraw ใหม ่การแก้ ไขเมธอดของคลาสแม่ในคลาสลูกนั้นเรียกว่า Method Overriding เช่นตัวอย่างที่ 2 บรรทัดที่ 17-29 ซึ่งทําได ้โดยการเขียนเมธอดนั้นใหม่ที่คลาสลูก โดยที่ method signature (ชื่อเมธอด ชนิดและจํานวน parameter) นั้นยังเหมือนเดิม ในตัวอย่างที่ 2 จะเห็นได้ว่าบรรทัดที่ 18 ของตัวอย่างที ่2 นั้นเหมือนกับบรรทัดที่ 34 ใน ตัวอย่างที ่1 ส่วนจะใส่คําว่า @Override (บรรทัดที่ 17) หรือไม่ใส่ก็ได้ แต่แนวทางปฏิบัติที่ดีควรจะใส่ไว้ เพื่อ ป้องกันการ overloading โดยไม่ตั้งใจ (เช่น หากเราใส่ชนิดข้อมูลของ parameter ผิด จะกลายเป็นการสร้าง เมธอดใหม่ขึ้นมา จึงกลายเป็นการ overloading แทนที่จะเป็นการ overriding อย่างที่ตั้งใจไว้ในตอนแรก) ในตัวอย่างต่อไปเป็นการนําคลาส BankAccount และ CheckingAccount ไปสร้างเป็นออบเจกต์ /* AccountTest.java */

1 2 3 4 5 6 7 8 9

10 11 12 13 14

public class AccountTest { public static void main(String[] args) { BankAccount b = new BankAccount("Alice", "1234", 10); CheckingAccount c = new CheckingAccount("Bob", "0248", 10, 5); System.out.println("BankAccount:"); b.withdraw(50.0); System.out.println(b.getBalance()); System.out.println("CheckingAccount:"); c.withdraw(50.0); System.out.println(c.getBalance()); } } ตัวอย่างที ่3: คลาส AccountTest สําหรับทดสอบ BankAccount และ CheckingAccount

ผลลัพท์ที่ได้จากโปรแกรมนี้คือ BankAccount: Sorry, balance can't be negative. 10.0 CheckingAccount: -45.0 จะสังเกตเห็นได้ว่า เมื่อเรียก b.withdraw(50.0) ในบรรทัดที่ 7 นั้น เมธอด withdraw ที่ถูกเรียกคือเมธอด withdraw ของคลาส BankAccount แต่เมื่อเรียก c.withdraw(50.0) ในบรรทัดที่ 11 นั้น เมธอด withdraw ที่ถูกเรียกคือเมธอด withdraw ของคลาส CheckingAccount

2. หลักการห่อหุ้ม (Encapsulation) หลักการห่อหุ้ม หมายถึง การรวมข้อมูลและการกระทํากับข้อมูลนั้นๆ(เมธอด)เข้าไว้ด้วยกัน (ในที่นี้คือ คลาส) โดยที่ผู้ใช้จะไม่สามารถเข้าถึงข้อมูลได้เองโดยตรง แต่จะต้องเข้าถึงผ่านเมธอดที่กําหนดไว้เท่านั้น

Page 6: บทที่ 7 Inheritance - mwit.ac.thcs/courseware/java/2_2556/TECH30201_ch7_2_2556.pdf · และการพ้องรูป (Polymorphism) เนื้อหา 1. การสืบทอด

6

รายวิชา ง30201 การเขียนโปรแกรมเชิงวัตถ ุ โรงเรียนมหิดลวิทยานุสรณ ์

ในตัวอย่าง 1.1 และ 1.2 เราใช้หลักการห่อหุ้มโดยกําหนดให้ access modifier ของ attribute เป็น private แล้วสร้าง accessor method และ mutator method ขึ้นมา หากผู้ใช้จะต้องการเข้าถึง attribute ก็จะต้อง เรียกใช้ accessor method สําหรับ attribute นั้นๆ หรือถ้าผู้ใช้ต้องการเปลี่ยนแปลงค่าของ attribute ก็จะ ต้องทําผ่าน mutator method เท่านั้น ข้อดีของการใช้หลักการห่อหุ้มในการสร้างคลาสก็คือ

1) ผู้ใช้สามารถใช้งานได้โดยไม่ต้องสนใจรายละเอียดว่าคลาสนั้นมีรายละเอียดต่างๆ (เช่นชื่อ attribute) อย่างไร แต่สนใจแค่ว่าจะใช้งานคลาสนั้นได้อย่างไร

2) ผู้ใช้ที่ต้องการเปลี่ยนค่าของ attribute ต้องกระทําผ่าน mutator method เท่านั้น ทําให้สามารถ ป้องกันไม่ให้ผู้ใช้เปลี่ยนค่า attribute เป็นค่าที่ไม่ถูกต้องได้ เนื่องจากเราสามารถเช็คเงื่อนไขได้ที่ mutator method ก่อน หากค่าที่ต้องการกําหนดไม่ผิดเงื่อนไขจึงจะกําหนดค่าให้กับ attribute นั้น (เช่น ตัวอย่างที ่2 บรรทัดที่ 14)

3) หากมีการแก้ไขคลาสนั้นๆในภายหลัง (เช่นการเปลี่ยนชื่อตัวแปรใหม่) เราไม่จําเป็นต้องปรับแก้โค้ดใน ส่วนของการใช้งานคลาสถ้าหากผู้ใช้ใช้งานผ่าน accessor และ mutator method

ในการสืบทอดนั้น หาก attribute ที่อยู่ในคลาสแม่มี access modifier เป็น private คลาสลูกจะ ไม่ไดส้ืบทอด attribute นั้นๆมาโดยตรง เราจึงไม่สามารถใช้งาน attribute นั้นผ่านชื่อของ attribute ได้ ถ้าต้องการจะใช้งาน attribute นั้น จะต้องใช้งานผ่าน public method ที่สืบทอดมาจากคลาสแม ่โดยถ้า ต้องการเข้าถึงค่าของ attribute ก็ให้ใช้ accessor method และถ้าต้องการเปลี่ยนแปลงค่าก็ให้ใช้ mutator method สําหรับ attribute นั้นๆ

2.1 Protected Access Modifier ในบทที่ 2 เราได้ทราบไปแล้วว่า access modifier มีทั้งหมด 4 ชนิด คือ public, protected, package (default), และ private หากตั้ง access modifier ของ attribute เป็น private แล้ว คลาสลูกจะ ไม่สามารถเข้าถึง attribute ที่สืบทอดมาจากคลาสแม่ได้โดยตรง แต่จะต้องเข้าถึงผ่าน accessor และ mutator method เท่านั้น ในทางกลับกันหากเราตั้ง access modifier เป็น protected คลาสลูกจะ สามารถเข้าถึง attribute ที่สืบทอดมาจากคลาสแม่ได้โดยตรง อย่างไรก็ตาม แนวทางปฏบิัติที่ดีนั้นควรจะตั้ง access modifier ของ attribute ให้เป็น private แล้วเรียกใช้ผ่าน public method ของคลาสแม่แทน ปัญหาที่อาจเกิดขึ้นจากการตั้ง access modifier เป็น protected คือ

1) คลาสลูกสามารถเปลี่ยนแปลงค่าของ attribute ได้เองโดยไม่ต้องผ่าน mutator method ของคลาส แม่ จึงสามารถใส่ค่าที่ไม่ถูกต้องได้โดยไม่ผ่านการกรองจาก mutator method ก่อน

2) ถ้าหากมีการเปลี่ยนแปลงที่คลาสแม่ (เช่นเปลี่ยนชื่อ attribute) จะต้องแก้ไขโค้ดที่คลาสลูกด้วย 3) คลาสอื่นๆที่อยู่ใน package เดียวกับคลาสที่ attribute นั้นอยู่สามารถเข้าถึง attribute นั้นได้ด้วย

(protected นั้นมีระดับการเข้าถึงอยู่ระหว่าง public และ package) ซึ่งคลาสอื่นๆนั้นอาจจะไม่ได้มี ความเกี่ยวข้องกับคลาสนั้นเลยก็ได้

Page 7: บทที่ 7 Inheritance - mwit.ac.thcs/courseware/java/2_2556/TECH30201_ch7_2_2556.pdf · และการพ้องรูป (Polymorphism) เนื้อหา 1. การสืบทอด

7

รายวิชา ง30201 การเขียนโปรแกรมเชิงวัตถ ุ โรงเรียนมหิดลวิทยานุสรณ ์

3. การพ้องรูป (Polymorphism) การพ้องรูป คือ การที่ตัวแปรอ้างอิงที่ถูกสร้างขึ้นมาจากคลาสแม่นั้น สามารถถูกนําไปสร้างเป็น ออบเจกต์ของคลาสลูกได้ โดยที่ออบเจกต์ที่ถูกสร้างขึ้นมานั้นจะมีชนิดเสมือนกับที่ออบเจกต์ถูกสร้างจาก ตัวแปรอ้างอิงของคลาสลูก ดังตัวอย่างต่อไปนี้ /* SuperClass.java */

1 2 3 4 5

public class SuperClass { public void print() { System.out.println("Printed from SuperClass."); } }

/* SubClass.java */

1 2 3 4 5 6 7 8 9

10

public class SubClass extends SuperClass { @Override public void print() { System.out.println("Printed from SubClass."); } public void print2() { System.out.println("This is a new method in SubClass."); } }

/* PolymorphismTest */

1 2 3 4 5 6 7 8 9

10 11 12 13

public class PolymorphismTest { public static void main(String[] args) { SuperClass a = new SuperClass(); SuperClass b = new SubClass(); SubClass c = new SubClass(); a.print(); b.print(); c.print(); ((SubClass) b).print2(); } }

ตัวอย่างที ่4: การทดสอบหลักการพ้องรูป ผลลัพท์ที่ได้จากโปรแกรมนี้คือ Printed from SuperClass. Printed from SubClass. Printed from SubClass. This is a new method in SubClass.

Page 8: บทที่ 7 Inheritance - mwit.ac.thcs/courseware/java/2_2556/TECH30201_ch7_2_2556.pdf · และการพ้องรูป (Polymorphism) เนื้อหา 1. การสืบทอด

8

รายวิชา ง30201 การเขียนโปรแกรมเชิงวัตถ ุ โรงเรียนมหิดลวิทยานุสรณ ์

จะเห็นว่า บรรทัดที่ 4 ของคลาส PolymorphismTest เราสร้างตัวแปร b โดยมีชนิดข้อมูลเป็น SuperClass แต่สร้างออบเจกต์ให้กับ b โดยใช้ constructor ของ SubClass ได้ (อย่างที่ได้กล่าวไปแล้วว่าชนิดข้อมูลของ คลาสลูกก็เป็นชนิดข้อมูลของคลาสแม่ด้วย จึงสามารถนําตัวแปรอ้างอิงของคลาสแม่ไปสร้างเป็นออบเจกต์ ของคลาสลูกได้) และเมื่อเรียกใช้เมธอด print ผ่านตัวแปร b เมธอดที่ถูกเรียกใช้คือเมธอด print ที่อยู่ใน SubClass แทนที่จะเป็นเมธอดที่อยู่ใน SuperClass หลักการพ้องรูปทําให้เราสามารถเขียนโปรแกรมที่มีความยืดหยุ่นสูง เช่นการที่เราสร้างตัวแปรแบบ อ้างอิงของคลาสแม่นั้น เราสามารถนําไปสร้างเป็นออบเจกต์ของตัวคลาสแม่เอง หรือของคลาสลูกทุกคลาสที่ สืบทอดมาจากคลาสแม่ได้ โดยที่จาวาจะเลือกเมธอดที่เหมาะสมให้เองเวลาเราเรียกใช้งานเมธอดตามชนิดของ ตัวออบเจกต์จริงๆ ไม่ใช่ตามชนิดของตัวแปรอ้างอิง อย่างไรก็ตาม หากต้องการใช้งานเมธอดที่มีอยู่เฉพาะในคลาสลูกแต่ไม่มีอยู่ในคลาสแม่ (เช่นเมธอด print2 ในตัวอย่างที่ 4 คลาส SubClass) เราจะไม่สามารถเรียกใช้งานโดยใช้คําสั่ง b.print2(); //Compilation error ได้โดยตรง เพราะว่า b เป็นตัวแปรอ้างอิงของคลาส SuperClass ซึ่งไม่มีเมธอด print2 ดังนั้น เราจะต้องทํา การเปลี่ยนชนิดของตัวแปรอ้างอิง b ให้กลายเป็นชนิด SubClass ก่อน แล้วจึงเรียกใช้เมธอด print2 เช่น ในบรรทัดที่ 11 ของคลาส PolymorphismTest การเปลี่ยนชนิดตัวแปรอ้างอิงของคลาสแม่ไปเป็นตัวแปร อ้างอิงของคลาสลูกนั้นเรียกว่าการ downcasting ซึ่งสามารถทําได้หากออบเจกต์นั้นเป็นออบเจกต์ของคลาส ลูกอยู่แล้ว (เช่น b) แต่จะไม่สามารถทําได้หากออบเจกต์นั้นไม่ใช่ของคลาสลูก (เราไม่สามารถ downcast ตัวแปร a ให้เป็น SubClass ได้)

4. หลักการ Abstraction โดยปกติแล้ว เราสร้างคลาสขึ้นมาเพื่อนําคลาสนั้นไปสร้างออบเจกต์ต่อ แต่ในบางกรณีเราก็ต้องการ สร้างคลาสขึ้นมาเพื่อใช้เป็นโครงร่างสําหรับให้คลาสลูกนําไปเติมเต็มในส่วนรายละเอียดอีกทีหนึ่ง คลาสที่ถูก สร้างขึ้นมาในลักษณะนี้จะเรียกว่าคลาสนามธรรม (abstract class) ดังตัวอย่างต่อไปนี้ สมมติเราต้องการสร้างคลาส Animal เพื่อใช้แทนสัตว์ โดยที่ attribute นั้นประกอบด้วยชื่อ อายุ และพฤติกรรม (หรือก็คือเมธอด) ของสัตว์ เช่นการเปล่งเสียงร้อง แต่เนื่องจากสัตว์แต่ละชนิดนั้นมีเสียงร้องที่ ต่างกันออกไป เราจึงยังไม่สามารถลงรายละเอียดให้กับเมธอดสําหรับเปล่งเสียงร้องได ้ซึ่งคลาสลูกแต่ละคลาส ที่สืบทอดไปจากคลาส Animal นั้นจะต้องใส่รายละเอียดให้กับเมธอดสําหรับเปล่งเสียงร้องเอง เมธอดใน ลักษณะนี้จะเรียกว่า เมธอดนามธรรม (abstract method)

การสร้าง abstract class นั้นทําได้โดยวางคีย์เวิร์ด abstract ไว้ก่อนคําว่า class ดังนี้

Animal'

Dog' Cat' Cow'

Page 9: บทที่ 7 Inheritance - mwit.ac.thcs/courseware/java/2_2556/TECH30201_ch7_2_2556.pdf · และการพ้องรูป (Polymorphism) เนื้อหา 1. การสืบทอด

9

รายวิชา ง30201 การเขียนโปรแกรมเชิงวัตถ ุ โรงเรียนมหิดลวิทยานุสรณ ์

/* Animal.java */ 1 2 3 4 5 6 7 8 9

10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25

public abstract class Animal { private String name; private int age; public Animal(String n, int a) { name = n; age = a; } public void setName(String n) { name = n; } public void setAge(int a) { age = a; } public String getName() { return name; } public int getAge() { return age; } public abstract void makeNoise(); //abstract method }

ตัวอย่างที ่5: Abstract Class Abstract class นั้นสามารถมี instance variable, instance method และ constructor ได้ เหมือนกับคลาสทั่วๆไป เพียงแต่เราไม่สามารถนํา constructor ของ abstract class ไปใช้สร้างออบเจกต์ได้ หน้าที่ของ constructor สําหรับ abstract class นั้นมีเพื่อให้ constructor ของคลาสลูกสามารถกําหนดค่า เริ่มต้นให้กับ attribute ที่ได้รับสืบทอดมาจาก abstract class เท่านั้น เมธอด makeNoise นั้นเป็น abstract method เราจึงยังไม่ต้องสร้างส่วน body ของเมธอดขึ้นมา จึงมีการใช้เครื่องหมาย ; (semicolon) แทนที่จะเป็นวงเล็บปีกกา { } เหมือนเมธอดทั่วๆไป (concrete method) หลังจากสร้างคลาส Animal แล้ว เราสามารถนําคลาสนี้ไปใช้สืบทอดเพื่อให้ได้คลาส Dog, Cat, Cow ดังนี้ /* Dog.java */

1 2 3 4 5 6 7 8 9

public class Dog extends Animal { public Dog(String n, int a) { super(n,a); } public void makeNoise() { System.out.println("Woof woof!"); } }

Page 10: บทที่ 7 Inheritance - mwit.ac.thcs/courseware/java/2_2556/TECH30201_ch7_2_2556.pdf · และการพ้องรูป (Polymorphism) เนื้อหา 1. การสืบทอด

10

รายวิชา ง30201 การเขียนโปรแกรมเชิงวัตถ ุ โรงเรียนมหิดลวิทยานุสรณ ์

/* Cat.java */ 1 2 3 4 5 6 7 8 9

public class Cat extends Animal { public Cat(String n, int a) { super(n,a); } public void makeNoise() { System.out.println("Meow~"); } }

/* Cow.java */

1 2 3 4 5 6 7 8 9

public class Cow extends Animal { public Cow(String n, int a) { super(n,a); } public void makeNoise() { System.out.println("Mooooo"); } }

ตัวอย่างที ่6: การนำ abstract class มาใช้สร้างเป็นคลาสทั่วๆไป (concrete class) เราสามารถที่จะสร้างตัวแปรอ้างอิงให้มีชนิดตาม abstract class ได้ แต่ constructor ที่เรียกใช้ใน การสร้างออบเจกต์นั้นจะต้องเป็น constructor ของ concrete class ดังตัวอย่างต่อไปนี้ /* AbstractClassTest.java */

1 2 3 4 5 6 7 8 9

10 11

public class AbstractClassTest { public static void main(String[] args) { Animal cat = new Cat("Mocha", 2); Animal dog = new Dog("Tofu", 4); Animal cow = new Cow("Steak", 8); cat.makeNoise(); dog.makeNoise(); cow.makeNoise(); } }

ตัวอย่างที ่7: การสร้างออบเจกต์ด้วยตัวแปรอ้างอิงของ abstract class

ผลลัพท์ที่ได้จากการรันโปรแกรมนี้คือ Meow~ Woof woof! Mooooo จะเห็นได้ว่าจาวาจะเลือกเมธอดที่เหมาะสมให้กับเราเองตามหลักการพ้องรูปที่กล่าวไปแล้ว

Page 11: บทที่ 7 Inheritance - mwit.ac.thcs/courseware/java/2_2556/TECH30201_ch7_2_2556.pdf · และการพ้องรูป (Polymorphism) เนื้อหา 1. การสืบทอด

11

รายวิชา ง30201 การเขียนโปรแกรมเชิงวัตถ ุ โรงเรียนมหิดลวิทยานุสรณ ์

การใช้ abstract class ในการสืบทอดนั้น เป็นเหมือนกับข้อผูกมัดว่าคลาสใดๆก็ตามที่จะมาสืบทอด คลาสที่เป็น abstract นั้น จะต้องนิยาม abstract method ที่ตัวคลาสนั้นๆด้วย ถ้าหากว่าคลาสนั้นยังไม ่สามารถที่จะนิยาม abstract method ทุกตัวที่สืบทอดมาได้ คลาสนั้นยังคงที่จะต้องประกาศเป็น abstract class อยู ่

5. Final Methods และ Final Class ในบทที่ผ่านมา เราใช้คีย์เวิร์ด final กับตัวแปรเพื่อทําให้ตัวแปรนั้นเป็นค่าคงที่ ไม่สามารถเปลี่ยน

แปลงค่าในภายหลังได้ ในทํานองเดียวกัน เราสามารถใช้คีย์เวิร์ด final กับเมธอดเพื่อทําให้เมธอดนั้นไม่ สามารถถูก override ในคลาสลูกได้ เราจะเรียกเมธอดประเภทนี้ว่า final method ทําได้โดยใส่คําว่า final ไว้ก่อนหน้า return type ของเมธอด

เราจะใช้ final method เมื่อเราไม่ต้องการให้คลาสลูกทําการเปลี่ยนแปลงใดๆกับเมธอดที่เราได้สร้าง ไว้แล้ว เพื่อเป็นการบังคับว่าคลาสลูกใดๆก็ตามที่เรียกใช้งานเมธอดนี้จะทํางานแบบเดียวกันเสมอ

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