meaw meaw...
Class and Object

homearticlesbooksshowcasequiz
By MongTaLa Meng
  1. เกริ่นนำ
ในบทความนี้เราจะเริ่มคุยกันถึงเรื่องของ Class และ Object กันนะครับ เพื่อที่จะปูพื้นฐานให้คุณผู้อ่านเกี่ยวกับการโปรแกรมแบบออปเจ็คโอเรียนเต็ดนั้น ผู้เขียนจะขอคุยคร่าว ๆ เกี่ยวกับมันในหัวข้อแรก ๆ นะครับ ผู้เขียนขออนุมาณเอาว่า คุณผู้อ่านได้ผ่านการเขียนโปรแกรมในบางภาษา เช่น ภาษาซี หรือภาษาอื่น ๆ มาบ้างแล้ว ส่วนคุณผู้อ่านที่มีความเข้าใจลึกซึ้งเกี่ยวกับเรื่อง Class และ Object อยู่แล้ว ก็สามารถข้ามบทความนี้ไปได้เลยนะครับ

2. การเขียนโปรแกรมในยุคก่อน ๆ
สมมุติว่าคุณผู้อ่านกำลังเริ่มหัดเขียนโปรแกรมด้วยภาษาซีใหม่ ๆ ถ้าเราต้องการแสดงค่าเฉลี่ยบนหน้าจอคอมพิวเตอร์ของค่าสามค่าสองชุด 123, 321, 453 และ 24, 350, 289 ก็อาจจะเขียนโปรแกรมได้ดังนี้

โปรแกรมที่ 1
1    int a[3] = {123, 321, 453};
2    int b[3] = {24, 350, 289};
3
4    float aAve = (a[0]+a[1]+a[2])/3;
5    printf("Average value of %d, %d, %d is equal to %f\n", a[0], a[1], a[2], aAve);
6
7    float bAve = (b[0]+b[1]+b[2])/3;
8    printf("Average value of %d, %d, %d is equal to %f\n", b[0], b[1], b[2], bAve);
ค่าเฉลี่ยของเลขสองชุดดูไม่มีปัญหาอะไร เราสามารถก๊อปปี้สองบรรทัดที่ทำการคำนวณค่าเฉลี่ยมาแปะด้านล่าง แล้วแก้ไขตัวแปรนิดหน่อย แล้วเราก็ได้ค่าออกมานะครับ แต่ถ้าเกิดเราต้องการค่าเฉลี่ยของเลยสักร้อยชุดละครับ ปัญหาที่เราสามารถมองเห็นได้อย่างชัดเจนก็คือ
1. เราต้องก๊อปปี้เก้าสิบเก้าครั้ง
2. เราต้องแก้ตัวแปรเก้าสิบเก้าครั้ง
3. การตรวจสอบความผิดพลาดเป็นไปอย่างยุ่งยาก เนื่องจากเราต้องดูทีละบรรทัดว่าผิดหรือเปล่า

3. Function
ผู้ที่ได้ออกแบบภาษาในยุคแรก ๆ ก็ได้คิดค้นวิธีแก้ปัญหาแบบนี้ไว้แล้ว Function ก็ได้ถือกำเนิดขึ้นมาเพื่อทำให้การทำงานที่ซ้ำ ๆ กันง่ายขึ้น ในภาษาอื่น ๆ ก็มีสิ่งที่ทำงานคล้าย ๆ Function อยู่ แต่มีชื่อเรียกต่างกัน เช่น Procedure, Subroutine หรือ Method ซึ่งในภาษาจาวา เราก็เรียกมันว่า Method นั่นเองครับ

ฟังก์ชันนั้นมีประโยชน์ในการช่วยให้ผู้เขียนโปรแกรมสร้างการทำงานบางส่วนของโปรแกรมเก็บไว้ เพื่อใช้ได้อีกในหลาย ๆ ส่วนของโปรแกรม การเขียนเพียงครั้งเดียวแล้วเรียกใช้หลายครั้งนั้น เป็นการประหยัดเวลาในการเขียนทั้งยังเป็นการช่วยให้โปรแกรมดูง่ายขึ้นและสามารถแก้ไขได้สะดวก เรามาลองดูกันนะครับว่า Function ช่วยในการคำนวณค่าเฉลี่ยของเลขหลาย ๆ ชุดได้อย่างไร

โปรแกรมที่ 2
1     void printAve(int[] intarr,  int size) {
2       int sum = 0;
3       float ave = 0.0;
4    
5       for(int i=0; i<size; i++) {
7         sum += intarr[i];
8       }
9    
10      ave = (float) sum/size;
11      printf("Average value of ");
12      for (i=0; i<size; i++) {
13        printf("%d, ", intarr[i]);
14      }
15      printf("is equal to %f\n", ave);
16    }
17
18    int a[3]={123,321,453};
19    int b[3]={24,350,289};
20    printAve(a, 3);
21    printAve(b, 3);
หลังจากที่คุณผู้อ่านเห็นการใช้ฟังก์ชันข้างบนแล้ว คงจะเห็นด้วยกับผู้เขียนนะครับว่า เราสามารถทำให้โปรแกรมอ่านง่ายและแก้ไขสะดวกได้อย่างไร ถ้าเรามีตัวเลขอยู่หนึ่งร้อยชุดที่จะต้องทำการหาค่าเฉลี่ย เราก็ไม่ต้องคำนวณค่าเฉลี่ยและเขียน printf ถึงหนึ่งร้อยครั้ง ถ้าเกิดเรามาพบทีหลังว่าเราคำนวณค่าเฉลี่ยผิด เราก็แก้เพียงที่เดียว คือในตัวฟังก์ชัน printAve ไม่ต้องไปแก้ทั้งหมดหนึ่งร้อยที่ และถ้าเราทดสอบแล้วว่าฟังก์ชัน printAve นี้ทำงานได้อย่างถูกต้อง เราก็สามารถมั่นใจได้ว่ามันจะทำงานได้อย่างถูกต้อง ทุกครั้งที่เราเรียกใช้มัน เพราะฉะนั้นเราควรจะแบ่งการทำงานทั้งหมดของโปรแกรมให้เป็นส่วน ๆ โดยการใช้ฟังก์ชัน ซึ่งจะทำให้การอ่านและแก้ไขเป็นไปด้วยความสะดวก

คุณผู้อ่านอาจจะสงสัยนะครับว่า สิ่งที่อยู่ในวงเล็บหลังชื่อของฟังก์ชัน printAve ในบรรทัดที่ 1 นั้นคืออะไร นั่นคือตัวแปรที่เราส่งผ่านเข้าไปให้กับฟังก์ชันหรือ function arguments นั่นเอง เปรียบเสมือนกับว่าเราสามารถกำหนดได้ว่าเราอยากให้ฟังก์ชันได้รับค่าอะไรบ้างเพื่อเอาไปใช้งาน คุณผู้อ่านจะเห็นว่ามันถูกแยกเป็นคู่ ๆ กั้นด้วยลูกน้ำ แต่ละคู่ก็คือชนิดของ argument และชื่อของ argument นั่นเอง ตัวฟังก์ชันก็จะมองเห็นค่าที่ผ่านเข้ามาเป็นชนิดนั้นและชื่อนั้น การผ่าน argument นี้เองคือสิ่งที่ช่วยให้ฟังก์ชันทำงานได้หลาย ๆ ครั้งโดยที่ไม่ต้องแก้ไขโปรแกรมมากนัก

4. Object-Oriented Programming
ถึงแม้ว่าฟังก์ชันจะเข้ามามีบทบาทในการช่วยแบ่งสัดส่วนของโปรแกรมให้อ่าน วิเคราะห์ และแก้ไขได้ง่ายขึ้น แต่นั่นก็ยังไม่ทำให้ผู้เขียนโปรแกรมส่วนใหญ่พอใจ เมื่อโปรแกรมใหญ่ ๆ และซับซ้อนมากขึ้นเรื่อย ๆ ผู้เขียนโปรแกรมก็จะต้องแบ่งโปรแกรมให้เป็นสัดส่วนมากขึ้น นั่นหมายความว่าจำนวนฟังก์ชันก็จะมากขึ้น การผ่านตัวแปรเข้าไปหาฟังก์ชันก็จะมากขึ้น จำนวนของตัวแปรก็จะต้องเพิ่มมากขึ้น ความระมัดระวังมิให้การเก็บตัวแปรผิดพลาดก็ต้องมากขึ้นตาม สิ่งเหล่านี้ได้ทำให้เกิดรูปแบบของการเขียนโปรแกรมแบบใหม่ขึ้นมาซึ่งมีชื่อเรียกว่า Object-Oriented Programming หรือ OOP

5. Object คืออะไร
Object ใน OOP นั้นถ้าพูดกันแบบสั้น ๆ ก็คือการรวมตัวกันของ data และ operations บน data เหล่านั้น สำหรับการเขียนโปรแกรมแบบ OOP แล้ว ตัว object มีคุณสมบัติเด่น ๆ ดังนี้

Classification and Instance
Definition ของ Class นั้นก็คือชนิดของ object นั่นเอง หรือพูดอีกนัยหนึ่งก็คือ object เป็นวัตถุชิ้นหนึ่งหรือ instance ที่อยู่ในคลาสนั้น ๆ ยกตัวอย่างเช่น นกสองตัวที่เกาะอยู่บนกิ่งไม้คนละกิ่งเปรียบเสมือนสอง objects ที่อยู่ในคลาสของนก คลาสจะบ่งบอกว่า object ในคลาสนั้นทำอะไรได้บ้าง และทุก instance ในคลาสนั้นจะต้องสามารถทำทุกสิ่งที่คลาสนั้นบอกว่าทำได้

Encapsulation
การสื่อสารกับ object หรือการใช้ operation ของ object นั้นสามารถทำได้โดยผ่านการเรียกใช้ method ของ object นั้น ๆ เท่านั้น object ไม่ควรจะให้ส่วนอื่นของโปรแกรมเข้ามาอ่านหรือแก้ไข data ที่อยู่ภายในตัวมันโดยตรง

Inheritance
ในหลักการของ OOP แล้วนั้น เราสามารถสร้างคลาสหนึ่งที่มาจากอีกคลาสหนึ่งได้โดยการทำ interitance คลาสที่เป็นต้นแบบคือ Superclass และคลาสที่ถูกสร้างขึ้นมาทีหลังจะถูกเรียกว่า Subclass ความสัมพันธ์ของ superclass และ subclass นั้นคือ subclass  จะมีทุก  operation ที่ superclass มีแต่ operation ต่าง ๆ ของ subclass อาจจะมีประพฤติกรรมที่แตกต่างออกไปจาก  superclass ได้และ subclass สามารถสร้าง operation ต่าง ๆ เพิ่มเติมให้กับตัวเองได้ด้วย operation เพิ่มเติมเหล่านี้จะไม่ได้มีผลต่อ superclass แต่อย่างใด สรุปแล้วก็คือ subclass จะมีคุณสมบัติไม่ด้อยไปกว่า superclass

Polymorphism
ถ้าเราเปรียบนกเป็นคลาสซึ่งมี operation บิน ส่งเสียงร้อง และหาอาหาร นกอินทรีย์และนกกระจอกก็จะเปรียบเสมือนคลาสที่ได้มาจากการ inheritance จากคลาสนก เพราะฉะนั้นนกอินทรีย์และนกกระจอกจะสามารถบิน ส่งเสียงร้อง และหาอาหารได้ แต่ว่านกอินทรีย์นั้นจะบินได้สูงและนานกว่านกกระจอก นกอินทรีย์ส่งเสียงร้องได้ดังและไกลกว่านกกระจอก และอาหารที่นกทั้งสองกินก็ไม่เหมือนกันดังนั้น operation ทั้งสามของนกทั้งสองชนิดนี้ก็จะมีพฤติกรรมที่แตกต่างกัน แต่ถ้าถามว่านกอินทรีย์และนกกระจอกยังคงเป็นนกอยู่หรือไม่   คำตอบก็คือใช่เพราะนกทั้งสองยังคงบิน ส่งเสียงร้อง และหาอาหารได้ หลักการของ polymorphism ก็คือคลาสที่มี operations แบบเดียวกันสามารถถูกมองให้เป็นคลาส generalization เดียวกันได้ ในตัวอย่างเรื่องนกนี้คือ polymorphism ด้วยการ inheritance นกเป็น generalization class ส่วนนกอินทรีย์และนกกระจอกเป็นคลาสที่อยู่ในกลุ่มนี้ ในภาษาจาวาเรายังมีการใช้ polymorphism ด้วยวิธีอื่น ๆ อีกแต่เราจะยังไม่พูดถึงในบทความนี้นะครับ

6. Class และ Objects ในจาวา
ผู้เขียนเชื่อว่าคุณผู้อ่านที่ไม่มีพื้นฐานเกี่ยวกับการเขียนโปรแกรมแบบ  OOP มาก่อนคงจะไม่ได้ความกระจ่างชัดหลังจากที่ได้อ่านหัวข้อต่าง ๆ ที่ผ่าน ๆ มาแล้วของบทความนี้ ผู้เขียนขอแนะนำให้ไปหาหนังสือเกี่ยวกับเรื่องนี้อ่านเพิ่มเติมนะครับ เพื่อความง่ายในการอธิบายเกี่ยวกับการเขียน class และ object ด้วยภาษาจาวาผู้เขียนจะขออธิบายจากตัวอย่างง่าย ๆ ข้างล่างนี้ครับ

โปรแกรมที่ 3
1     public class Instructor  {
2       private String name = null;
3       private String dept = null;
4       private Scheduler scdlr = null;
5
6       public Instructor(String name, String dept) {
7         this.name = name;
8         this.dept = dept;
9       }
10
11      public Instructor(String name, String dept, Scheduler s) {
12        this.name = name;
13        this.dept = dept;
14        scdlr = s;
15      }
16
17      public void setScheduler(Scheduler s) {
18        scdlr = s;
19      }
20
21      public String getName() {
22        return name;
23      }
24
25      public String getDept() {
26        return dept;
27      }
28
29      public Schedule getClassSched() {
30        return ( hasScheduler() ) ? scdlr.getClassSched(this) : null;
31      }
32
33      private boolean hasScheduler() {
34        return ( scdlr != null ) ? true : false;
35      } 
36    }
37
38    public class Scheduler {
39      public Schedule getClassSched(Instructor instr) {
40        String instrName = instr.getName();
41        if ( instrName.equals("Physics") ) {
42          return new PhysicsSchedule();
43        } else if ( instrName.equals("Mathematics") )  {
44          return new MathSchedule();
45        }
46      }
47    }
48
49    public class Schedule {
50      public void printSched() {
51      }
52    }
53
54    public class PhysicsSchedule extends Schedule {
55      public void printSched() {
56        System.out.println("Physics Department Schedule:");
57        System.out.println("  M T W Th F");
58        System.out.println("  1 0 0 1  1");
59      }
60    }
61
62    public class MathSchedule extends Schedule {
63      public void printSched() {
64        System.out.println("Mathematics Department Schedule:");
65        System.out.println("  M T W Th F");
66        System.out.println("  0 1 1 0  0");
67      }
68    }

7. Class
ในภาษาจาวานั้นคลาสประกอบไปด้วย ชนิดของคลาส, ชื่อของคลาส, ตัวแปรของคลาส และ methods ชนิดของคลาสนั้นอาจจะเป็น public หรือ private ถ้าเป็น Public class หมายถึงคลาสที่มองเห็นได้โดยคลาสที่อยู่ภายนอกหรือภายใน package แต่ถ้าเป็น Private class คลาสที่อยู่นอก package จะไม่สามารถเรียกใช้คลาสนี้ได้ คลาส Instructor จากโปรแกรมที่ 3 ข้างบนเป็นคลาสแบบ public

ตัวแปรของคลาสนั้นก็แบ่งเป็นชนิดเหมือนกัน แบ่งได้สามชนิด ถ้าเป็นตัวแปรแบบ private ตัวแปรนั้นจะถูกมองเห็นภายในคลาสเท่านั้น ยกตัวอย่างเช่น method getClassSched() ของคลาส Scheduler ไม่สามารถอ้างถึงตัวแปร name ของคลาส Instructor ได้โดยตรงแต่ต้องเอาค่าของ name โดยผ่านการเรียก method getName() ของคลาสนั้น  แต่ถ้าตัวแปร name ถูก declare ไว้เป็นแบบ public แล้วล่ะก็ Instructor จะสามารถอ้างถึง name ได้โดย

...
public class Scheduler {
  public Schedule getClassSched(Instructor instr) {
    String instrName = instr.name;
    if ( instrName.equals("Physics") ) {
      return new PhysicsSchedule();
    else if ( instrName.equals("Mathematics") ) {
      return new MathSchedule();
    }
  }
}
...
การอ้างถึงตัวแปรแบบ public ของอีก object หนึ่งสามารถทำได้โดยใช้ชื่อ object นั้นแล้วตามด้วยจุดและชื่อตัวแปร แต่ถ้าเป็นการอ้างถึงตัวแปรที่ถูก declare ไว้ในคลาสก็สามารถอ้างถึงชื่อตัวแปรได้โดยตรง ยกตัวอย่างเช่น Instructor สามารถอ้างถึง name หรือ dept ได้โดยไม่ต้องใส่อะไรเพิ่มเติม ตัวแปรอีกแบบหนึ่งที่สำคัญก็คือตัวแปรแบบ  protected ซึ่งมีแต่ subclasses หรือตัวมันเองเท่านั้นที่สามารถอ้างถึงได้

8. Variable Initialization
โดยทั่วไปแล้วเราควรจะ initialize ตัวแปรแต่ละตัวที่เรา declared ขึ้นมาเพื่อป้องกันความผิดพลาดที่เกิดขึ้น จากการนำตัวแปรที่เราไม่รู้ว่ามีค่าเริ่มต้นอะไรไปใช้ ถ้าตอนที่เรา declare object นั้นไม่มีค่าเริ่มต้นก็ให้ initialize ให้เป็น null ในตัวอย่างข้างบนเรา initialize name และ dept ให้เป็น null การ initialization นี้จะเกิดขึ้นหลังจากการ instantiation ของ object ก่อนที่จะมีการเรียก constructor methods

9. Method
Method ในภาษาจาวานั้นก็เปรียบได้กับ function ในภาษาซีนั่นเอง โดยทั่วไปแล้วเราควรจะ define ให้ method ทำอะไรอย่างหนึ่งเท่าที่เราต้องการ การสร้าง method ให้ใหญ่และซับซ้อนมาก ๆ จะทำให้วัตถุประสงค์ของ OOP นั้นถูกบดบังลงไปนะครับ องค์ประกอบสำคัญของ method นั้นมีดังนี้

ชื่อของ method
ชื่อของ method นั้นมีกฏเกณฑ์ในการตั้งเหมือนกับชื่อของตัวแปรนั่นเอง หลักการตั้งชื่อ method ที่จะกล่าวถึงนี้มิใช่กฏบังคับ แต่ผู้เขียนโปรแกรมจาวาโดยส่วนมากใช้กันเพื่อทำให้โปรแกรมอ่านง่าย ชื่อมักจะตั้งให้เป็นคำกิริยาหลาย ๆ คำซึ่งแสดงถึงหน้าที่ของ method นั้น ๆ คำแรกจะถูกเขียนด้วยตัว lowercase ทั้งหมดและตัวอักษรตัวแรกของคำถัดไปแต่ละคำจะเป็นตัว uppercase และที่เหลือจะเป็นตัว lowercase ตัว method getClassSched() ในตัวอย่างข้างบนก็ใช้กฏนี้

method arguments
ผู้เขียนได้อธิบายเกี่ยวกับ argument ไว้แล้วบางส่วนในหัวข้อที่ 3 argument นี้อาจจะเป็นตัวแปรพื้นฐานของจาวา หรืออาจจะเป็น object ก็ได้ เราสามารถกำหนด method ให้รับ argument ได้เท่าที่เราต้องการ

method body
ตัว body นี้เองที่เป็นตัวกำหนดอย่างแท้จริงว่า method นี้ทำหน้าที่อะไร ภายใน method body เราสามารถ declare ตัวแปรขึ้นมาเพื่อใช้งานชั่วคราวซึ่ง scope ของตัวแปรเหล่านี้จะอยู่ภายใน method นี้เท่านั้น ภายใน method  body เราสามารถเรียกใช้ method อื่นของ object นั้นหรือของ object อื่นที่อยู่ภายใน scope ของ body นั้นได้ด้วย ขอบเขตของ method body นั้นจะถูกกำหนดด้วยเครื่องหมายปีกกาเปิดและปิดหนึ่งคู่

return value
ภายหลังจาก method ทำงานจนถึงบรรทัดสุดท้ายแล้วก็จะ return กลับไปหาส่วนของโปรแกรมที่ call มัน ถ้าเราอยากให้ method ส่งค่าคืนไปยังผู้ call ก็สามารถใส่ return statement เข้าไปได้ หลังจากที่ return statement ได้ถูก execute แล้ว control จะหลุดออกจาก method นั้นทันที โปรแกรมส่วนที่กระทำการ call ก็สามารถรับค่า return นั้นได้โดยการใส่ ตัวแปรตามด้วยเครื่องหมายเท่ากับไ้ว้ด้านซ้ายของ method call ในตัวอย่างด้านบนเราจะเห็นได้ว่าคลาส Scheduler ได้เรียกใช้ method getName() ของ instr object ซึ่ง method นี้ return object ของคลาส String และ ค่า return ได้ถููกเก็บไว้ในตัวแปรชื่อ instrName ด้านซ้ายของชื่อ method จะต้ัองระบุว่า return ตัวแปรชนิดใดและจะต้องตรงกับตัวแปรที่ return จริงด้วย ถ้าไม่มีค่า return ก็ต้องใส่ คำว่า void ไว้ตรงตำแหน่งของชนิดรีเทอร์น

เช่นเดียวกันกับ variable ตัว method ก็ถูกจัดตาม scope ได้เป็นสามชนิดคือ private, public และ protected
private method สามารถถูกเรียกได้เฉพาะจาก method อื่น ๆ ภายใน class เดียวกันเท่านั้น subclass ไม่สามารถเรียกหรือมองเห็น private method ของ superclass ได้ยกตัวอย่างเช่น คลาส Schedule ไม่สามารถเรียก method hasScheduler() ของคลาส Instructor ได้ public method ตรงข้ามกับ private method อย่างสิ้นเชิง ทุกคลาสสามารถเรียก public method ของอีกคลาสหนึ่งได้ ส่วน protected method ก็สามารถถูกเรียกได้เฉพาะจากตัวคลาสนั้นเองหรือ subclasses เท่านั้น

ส่วนการเรียกใช้ method นั้นคุณผู้อ่านก็คงจะเห็นกันมามากแล้วในทุก ๆ ตัวอย่างเลย วิธีการเรียกใช้ method ของ object ตัวใหนก็ให้เอา object นั้นมาเขียนต่อด้วยจุดแล้วก็ตามด้วยชื่อ method พร้อมด้วยชุดของ arguments ที่อยู่ในวงเล็บ เราไม่ต้องใส่ชนิดของ arguments ลงไปนะครับ ถ้าเราอยากจะนำค่า return ที่ได้จาก method กลับมาใช้ก็สามารถเอาตัวแปรมารับโดยใช้ assignment operator ได้  ในตัวอย่างโปรแกรมที่ 3 ในบรรทัดที่ 40 นั้นได้มีการเรียกใช้ method getName() บน object ชื่อ instr ซึ่งเราได้เก็บค่า return ไว้ใช้ต่อในตัวแปรชื่อ instrName

10. Overloading a Method
เราสามารถมี method ที่มีชื่อเดียวกันมากกว่าหนึ่งอันได้แต่ว่าแต่ละ method ต้องมี signature ต่างกัน
Method signature ก็คือ pattern ของ arguments ที่อยู่ใน method นั้น ยกตัวอย่างเช่น

methodA(AClass a, BClass b, CClass c);
มี signature คือ AClass, BClass และ CClass (3 arguments) ถ้าจำนวน arguments ต่างกันก็จะถือว่ามี signature ไม่เหมือนกัน แต่ถ้าจำนวน arguments เท่ากันแต่อย่างน้อยหนึ่งใน arguments ที่อยู่ในตำแหน่งเดียวกันต่างชนิดกันก็จะถือว่ามี signature ไม่เหมือนกันเช่นกัน เพราะฉะนั้นทั้ง 5  methods ข้างล่างนี้มี method signatures ไม่เหมือนกันเลยจึงสามารถอยู่ใน class เดียวกันได้
demoMethod1();
demoMethod1(AClass a);
demoMethod1(BClass b);
demoMethod1(AClass a, BClass b);
demoMethod1(Aclass a, int integer);
จุดที่ควรระวังในการ overload method ก็คือควรจะให้แต่ละ method ที่มีชื่อเหมือนกันทำงานในความหมายหรือลักษณะเดียวกัน เพื่อป้องกันความสับสนของผู้เรียกใช้ methods เหล่านี้

11. Reference of an object
ทุกครั้งที่เรา declare ตัวแปรที่เป็นคลาสขึ้นมาอันหนึ่งเราไม่ได้สร้าง object ขึ้นมาทันทีแต่เราได้สร้างตัวแปรซึ่งจะเก็บค่า reference ของ object ของคลาสนั้นขึ้นมา เราสามารถเอาตัวแปรนี้ไปเก็บค่า reference ของ object ใดก็ได้ที่มีอยู่ในคลาสเดียวกับตัวมัน โดยการใช้ assignment operator การให้ตัวแปรเก็บค่า reference ของ object นั้นเราสามารถพูดได้อีกนัยหนึ่งว่าตัวแปรกำลังอ้างถึง object ตัวนั้นอยู่ ยกตัวอย่างเช่น

โปรแกรมที่ 4
1     public class ClassA {
2       private String name = null;
3
4       public ClassA(String n) {
5         name = n;
6       }
7
8       public String getName() {
9         return name;
10      }
11
12    public static void main() {
13      ClassA a1 = null;
14      ClassA a2 = null
15      a1 = new ClassA("First");
16      System.out.println("created "+ a1.getName());
17      a2 = a1;
18      System.out.println("a2 = a1");
19      System.out.println("a1 refers to "+ a1.getName());
20      System.out.println("a2 refers to "+ a2.getName());
21      }
22    }
ในบรรทัดที่ 13 และ 14 เราได้ declare ตัวแปรของคลาส ClassA ขึ้นมาสองตัวคือ a1 และ a2 ซึ่งถูก initialize ให้เป็น null หลังจากนั้นในบรรทัดที่ 15 เราได้ให้ a1 อ้างถึง object ตัวใหม่ซึ่งเพิ่งถูกสร้างขึ้นด้วย new operator (โปรดดูเกี่ยวกับ new operator ในหัวข้อที่ 13) และในบรรทัดที่ 9 เราได้ assign ค่า reference ซึ่งอยู่ใน a1 ให้กับ a2 เพราะฉะนั้นเราสามารถพูดได้ว่าเราได้ทำให้ a2 อ้างถึง object ตัวเดียวกับที่ a1 อ้างอยู่ซึ่งก็คือ object ที่ชื่อว่า First คำถามก็คือเราได้สร้าง object ตัวใหม่ขึ้นมาหรือเปล่า? คำตอบก็คือไม่ใช่ เราไม่ได้สร้าง object ขึ้นมาใหม่แต่เรากำลังทำให้ a2 อ้างไปถึง object ตัวเดียวกันกับที่ a1 อ้างอยู่

12. Constructor
ทุก ๆ ครั้่งที่ object ถูก instantiate จะมี method หนึ่งที่ถูกเรียกเสมอซึ่ง method นั้นก็คือ constructor method นั่นเอง constructor นั้นจะไม่สามารถถูกเรียกโดยตรงโดยโปรแกรมเมอร์ได้ แต่จะถูกเรียกโดยอัตโนมัติหลังจากที่ object ถูก instantiate ขึ้นมา วัตถุประสงค์ที่มี constructor ขึ้นมานี้ก็เพืิ่อให้โปรแกรมเมอร์เขียนสิ่งที่ต้องทำหลังจากที่ object ถูกสร้างในทันทีโดยที่ไม่ต้องเขียน method เพื่อที่ initialize ต่างหาก และถ้าทำแบบนั้นโปรแกรมเมอร์เองก็ต้องเสียเวลาเรียก initialize method ทุกครั้งหลังจากที่สร้าง object ใหม่ขึ้นมา

ข้อบังคับของ constructor คืิอชื่อของ constructor ต้องเหมือนกับชื่อของคลาสนั้น ๆ แต่ signature ของ constructor จะเป็นอย่างไรก็ได้ จะมีหรือไม่มี arguments ก็ได้และ constructor จะต้องไม่มีค่า return value

13. Instantiating objects
การ instantiate object ก็คือการสร้าง object ขึ้นมาใหม่นั่นเอง new operator ก็คือ operator ที่ช่วยในการ instantiate object ใหม่ขึ้นมานั่นเอง ชื่อคลาสที่ตามหลัง new operator นี้จะเป็นตัวบ่งชี้ว่าเรากำลัง instantiate object ของคลาสอะไรขึ้นมาและ constructor ที่มี signature เดียวกับ arguments ที่อยู่ในวงเล็บหลังชื่อคลาสจะถูกเรียกโดยอัตโนมัติหลังจากที่ object ถูก instantiate

new operator จะ return ค่า reference ของ object ที่เพิ่งถูกสร้างออกมา และถ้าเราไม่เก็บค่านั้นไว้ในตัวแปรล่ะก็เราก็จะไม่มีโอกาสเรียกใช้ object ตัวนั้นอีก ยกตัวอย่างเช่น

1    ClassA a = new ClassA("First");
2    new ClassA("Second");
ข้อแตกต่างของบรรทัดที่ 1 และบรรทัดที่ 2 คือเราสามารถเรียก a.getName() ได้แต่เราไม่สามารถเอา object ในบรรทัดที่สองไปใช้งานในบรรทัดถัด ๆ มาได้เลย เนื่องจากเราไม่มีตัวแปรที่อ้างถึงมัน ถ้าเราอยากจะสร้าง object ของคลาส ๆ หนึ่งพร้อมกันหลาย ๆ ตัวก็สามารถทำได้ โดยใส่เครื่องหมายวงเล็บเหลี่ยมเข้าไปด้านหลังของชื่อคลาสและใส่จำนวน object เข้าไปด้านในวงเล็บนั้น แต่มีข้อแม้ว่าตัวแปรที่จะมาอ้างถึง object เหล่านี้จะต้องเป็น array ชนิดเดียวกันด้วย ยกตัวอย่างเช่น
String s[] = new String[20];
ในบรรทัดข้างบนนี้ได้สร้าง object ของคลาส String ขึ้นมา 20 ตัว ถ้า s เป็น array ของ Integer คอมไพเลอร์ก็จะฟ้องข้อผิดพลาดขึ้นมา

14. Inheritance
เราสามารถสร้าง subclass ในภาษาจาวาได้โดยใช้ extends keyword ในบรรทัดที่ 54 ของโปรแกรมที่ 3 นั้นได้บอกว่าคลาส PhysicsSchedule นั้นเป็น subclass ของคลาส Schedule คลาสที่ถูกสร้างขึ้นโดยการใช้ inheritance นั้นจะมี methods เหมือนกับ superclass ทุกประการรวมทั้งประพฤติกรรมของ method เหล่านั้นด้วย แต่เราสามารถเปลี่ยนประพฤติกรรมของ method เหล่านั้นได้โดยการเขียน body ของ method เหล่านั้นขึ้นมาใหม่ จาวาจะถือว่าเราจะไม่เรียกใช้ body ของ method ใน superclass แต่จะมาใช้ของตัวมันเองที่ีมีการปรับเปลี่ยนแทน การเปลี่ยนแปลง body ของ method ใน subclass นี้เราเรียกว่า method overriding ในโปรแกรมตัวอย่างที่ 3 เราจะสังเกตเห็นว่าคลาส PhyshicsSchedule และ MathSchedule ได้ override method printSched()

จาวานั้นไม่สามารถทำ multiple inheritance ได้ซึ่งนี่เป็นหนึ่งในหลายสิ่งที่ทำให้จาวาต่างจาก C++ การทำ multiple inheritance หมายถึงการสร้าง subclass ขึ้นมาตัวหนึ่งโดยให้มี superclass มากกว่าหนึ่งอัน ยกตัวอย่างเช่น Child3 extends Child1, Child2 จาวาไม่อนุญาตให้คลาสใดก็ตาม extends มาจากสองคลาสพร้อมกัน

15. Polymorphism
ผลลัพธ์อีกอันหนึ่งที่ได้มาจากการทำ inheritance ก็คือ ตัวแปรชนิดของ superclass สามารถอ้างถึง object ของ subclass ได้ ยกตัวอย่างเช่น RootClass เป็นคลาสที่อยู่สูงสุดใน tree ส่วน FirstChild extends RootClass และ SecondChild extends FirstChild

1     // Normal assignment
2     RootClass rc = new RootClass();
3     FirstChild fc = new FirstChild();
4     SecondChild sc = new SecondChild();
5     // Polymorphism
6     fc = new SecondChild();
7     rc = new FirstChild();
8     rc = new SecondChild();
9     // The following lines are prohibited
10    fc = new RootClass();
11    sc = new RootClass();
12    sc = new FirstChild();
ในบรรทัดที่ 2 - 4 นั้นเป็นการ assign object ที่เป็นคลาสชนิดเดียวกันกับตัวแปร ส่วนบรรทัดที่ 6 - 9 นั้นแสดงให้เห็นว่าตัวแปรของคลาสที่อยู่สูงกว่าสามารถอ้างถึง object ของคลาสที่อยู่ต่ำกว่าใน inheritance tree ได้ เราจะเห็นได้ว่าในบรรทัดที่ 8 ตัวแปร rc ซึ่งเป็นตัวแปรของคลาส RootClass ซึ่งอยู่สูงกว่าคลาส SecondChild ถึงสองระดับสามารถอ้างถึง object ของคลาส SecondChild ได้ ในทางกลับกันเราไม่สามารถทำให้ตัวแปรที่อยู่ต่ำกว่าอ้างถึง object ที่มีคลาสสูงกว่าได้ดังในบรรทัดที่ 10 - 12 คอมไพเลอร์จะฟ้อง error ออกมา คุณสมบัตินี้มีประโยชน์อย่างไรล่ะครับ เรามาดูในรายละเอียดของโปรแกรมที่ 3 กันนะครับ

Instructor นั้นสามารถรู้ Schedule ของตัวเองได้โดยการเรียก method getClassSched() ซึ่งรีเทอร์น object ของคลาส Schedule กลับมา Scheduler จะเป็นตัวกำหนดว่า คุณครูจะได้ตารางสอนแบบใหนนะครับ ใน method getClassSched() ของ Scheduler นั้นได้กำหนดว่าคุณครูที่อยู่ใน Physics Department จะต้องได้ ตารางสอนของแผนก Physics ไป และคุณครูใน Mathematics Department ก็ควรจะได้ตารางสอนของแผนก Mathematics ไป ซึ่งถ้าสังเกตให้ดีเราจะเห็นว่าค่ารีเทอร์นของ method นั้นเป็น Schedule แต่เรากลับรีเทอร์นได้ทั้ง PhysicsSchedule และ MathSchedule ทั้งนี้เป็นเพราะทั้งสองคลาสได้ extends มาจาก Schedule ส่วนคลาสที่นำ Schedule ที่รีเทอร์นนี้ไปใช้ก็ไม่จำเป็นต้องรู้ว่ามันเป็น PhysicsSchedule หรือ MathSchedule เพียงแต่มันรู้ว่า object ที่รีเทอร์นกลับมานั้นมี methods ที่เหมือนกับ Schedule แต่ประพฤติกรรมจริง ๆ ของแต่ละ method นั้นจะถูกกำหนดโดย object ที่แท้จริง เพราะฉะนั้นประโยชน์ข้อแรกคือ ถ้าเราเพิ่ม Schedule ของแผนกใหม่ขึ้นมายกตัวอย่างเช่น EngineerSchedule เราก็ไม่จำเป็นต้องแตะต้องคลาส Instructor เลยแม้แต่นิดเดียว เพียงแต่เราต้อง define คลาสใหม่นี้ขึ้นมาและเพิ่มลอจิกของการเลือกตารางสอนลงในคลาส Schedule

ในตัวอย่างนี้คุณผู้อ่านอาจจมองได้ว่า PhysicsSchedule (subclass) และ MathSchedule (subclass) ได้ถูกมองเป็นสิ่งเดียวกันคือ Schedule (superclass) นั่นเป็นสาเหตุที่ว่าทำไมเราถึงเรียกคุณสมบัตินี้ว่า Polymorphism คำว่า poly หมายถึงหลายอย่าง คำว่า morph หมายถึงรูป พอรวมกันคำว่า Polymorphism ก็หมายถึง หลายรูป ซึ่งในความหมายของเราก็คือ คลาส ๆ หนึ่งที่สามารถเปลี่ยนแปลงได้หลายรูปแบบนั่นเอง

Resources
สำหรับคุณผู้อ่านที่สนใจเกี่ยวกับเรื่อง Class และ Object สามารถศึกษารายละเอียดเพิ่มเติมได้จาก
Thinking in Java หนังสือ Java Tutorial ของ Bruce Eckel มีให้ดาวโหลดฟรี online
Artima.com เจ้าของเวป (Bill Venners) เป็นคนเขียนบทความเองทั้งหมด เนื้อหาดีมากเหมาะสำหรับการปูพื้นฐาน


Copyright © 2000-2002 www.jarticles.com