การใช้งาน การสืบทอด Inheritance class ในภาษา Dart เบื้องต้น

เขียนเมื่อ 4 ปีก่อน โดย Ninenik Narkdee
dart inheritance class การสืบทอด class oop ใน ภาษา dart

คำสั่ง การ กำหนด รูปแบบ ตัวอย่าง เทคนิค ลูกเล่น การประยุกต์ การใช้งาน เกี่ยวกับ dart inheritance class การสืบทอด class oop ใน ภาษา dart

ดูแล้ว 6,950 ครั้ง


เนื้อหาต่อไปนี้ เราจะพูดถึงการสืบทอดของ class หรือที่เรียกว่า 
Inheritance ซึ่งยังอยู่ในโหมดของ OOP ในภาษา Dart 
สามารถทบทวนเนื้อหาเกี่ยวกับการใช้งาน class เบื้องต้น
ในภาษา Dart ได้ที่บทความ  http://niik.in/942
 
 
สามารถทดสอบการเขียนโปรแกรมผ่านเว็บไซต์ DartPad 
 
 

การสืบทอด Inheritance

    Inheritance หรือการสืบทอด เป็นวิธีการที่ทำให้ object หนึ่งสามารถใช้งาน property และ method จาก parent
class  ที่ทำการสืบทอดได้ หรือเข้าใจอย่างง่ายคือ class หนึ่งๆ สามารถสืบทอด property และ method จาก อีก class หนึ่งได้
สมมติเช่น class A สืบทอด class B เราจะเรียก class A และ class B ได้เป็นดังนี้
    class B ก็คือ SUPER class หรือ BASE class หรือ PARENT class
    class A ก็คือ SUB class หรือ CHILD class 
 
    โดยในการใช้งานการสืบทอด จะใช้ extends keyword ในการกำหนด อย่างกรณีตามตัวอย่างด้านบน ก็จะได้เป็น
 
class B{ }
class C{ }
// class A สืบทอดจาก class B
class A extends B{ }
 
    เราสามารถทำการสืบทอด class ได้เพียง class เดียวเท่านั้น กล่าวคือ  ไม่สามารถสืบทอด class A จาก class B 
และ class C พร้อมกันได้
 
// class A extends B,C{}  // error
 
    เพื่อให้เข้าใจการสืบทอดง่ายขึ้น ขอยกตัวอย่างเกียวกับ Animal class, Dog class และ Cat class ประกอบคำอธิบาย
 
class Animal{
  String color; // สี
  
  void eat(){} // กินอาหารได้
}
class Dog{
  String color; // สี
  String breed; // สายพันธ์
  
  void bark(){} // เห่าได้
}
class Cat{
  String color;  // สี
  int age; // อายุ
  
  void meaw(){}  // ร้องเหมียวได้
}
 
    class ทั้ง 3 ล้วนมี property และ method ของตัวเองซึ่งเป็นได้ทั้ง instance variable หรือ instance method
    โดยทั้ง Dog และ Cat มี property ที่ชื่อว่า color ซึ่งสามารถใช้ property นี้จาก Animal class ได้ ดังนั้น เราสามารถทำการสืบทอด
Animal class ไปยัง Dog / Cat class ได้ดังนี้
 
class Animal{
  String color; // สี

  void eat(){} // กินอาหารได้
}
class Dog extends Animal{
  String breed; // สายพันธ์
   
  void bark(){} // เห่าได้
}
class Cat extends Animal{
  int age; // อายุ

  void meaw(){}  // ร้องเหมียวได้
}
 
    เมื่อทำการสืบทอด class จะทำให้ Child class ได้รับ หรือสามารถเรียกใช้ property และ method ของ Parent class ได้ นั่นคือ Dog และ Cat 
จะมี color และ eat()  ที่เป็น variable และ method ของ Animal class ที่ได้รับสืบทอดมานั่นเอง
    ดูตัวอย่างการเรียกใช้งาน
 
void main () {
  var dog = Dog();
  dog.breed = 'Pug';  
  dog.color = 'black'; // color property จาก Animal class
  dog.eat(); // eat() method จาก Animal class
  dog.bark();
  
  var cat = Cat();
  cat.age = 4;
  cat.color = 'white'; // color property จาก Animal class
  cat.eat(); // eat() // method จาก Animal class
  cat.meaw();
}
 
    ก่อนไปรายละเอียดต่อไป ขอทบทวนอีกครั้งว่า ในภาษา Dart ทุกอย่างล่วนเป็น object และทุกๆ class ในภาษา Dart ก็ล้วนสืบทอดมาจาก
Object class ซึ่งเป็น Super class อีกที ดังนั้น ทกๆ class จะได้รับการสืบทอด property ต่อไปนี้มาได้ ได้แก่
  •     toString() // method ที่คืนค่าข้อความที่อธิบายถึง object นั้นๆ
  •     hashCode // ตัวเลขค่า Hash Code หรือรหัสเฉพาะตัวของ object นั้นๆ ไว้ใช้ในการเปรียบเทียบค่า
  •     opperator หรือตัวดำเนินการ ( == ) สำหรับเปรียบเทียบ สอง object ใดๆ 
 
    เรามาทดสอบลองเรียกใช้งาน property และ method ทั้ง 3 ร่วมกับ Dog และ Cat object  คำอธิบายแสดงในโค้ด จะได้เป็นดังนี้
 
void main () {
  var dog = Dog();
  print(dog.hashCode); // hashCode property จาก Object super class
  // output: 790618802 ค่าตัวเลขแสดงสภาวะของ object ไว้ใช้ในการเปรียบเทียบ ==
  // ค่า hashCode จะเปลี่ยนแปลงทุกครั้ง  
  print(dog.toString()); // toString() method จาก Object super class
  // output: Instance of 'Dog'

  var cat = Cat();
  print(cat.hashCode); // hashCode property จาก Object super class
  // output: 130547424 ค่าตัวเลขแสดงสภาวะของ object ไว้ใช้ในการเปรียบเทียบ ==
  // ค่า hashCode จะเปลี่ยนแปลงทุกครั้ง
  print(cat.toString()); // toString() method จาก Object super class
  // output: Instance of 'Cat'
  
  if(dog == cat){
    print("Same");
  }else{
    print("not same");
  }
  // output: not same
  
  var dog2 = dog;
  if(dog == dog2){
    print(dog.hashCode); // ค่าจะเท่ากับ hashCode ของ dog2
    print(dog2.hashCode); // ค่าจะเท่ากับ hashCode ของ dog
    print("Same");
  }else{
    print("not same");
  }  
  // output:
// 790618802
// 790618802
// Same  
 
}
 

 

การกำหนด Overriding

    การ Overriding เป็นวิธีที่ให้ Child class สามารถที่จะกำหนด property และ method ที่ทำการสืบทอดจาก Parent class ให้มีคุณลักษณะ หรือการ
ทำงานที่แตกต่างจาก Parent class ได้ ยกตัวอย่าง ดังนี้
 
void main () {
  var dog = Dog();
  print(dog.color); // output: black
  dog.eat(); // output: Animal is eating....
}
class Animal{
  String color = 'black'; 

  void eat(){ 
    print('Animal is eating....'); 
  } 
}
class Dog extends Animal{
  String breed; 

  void bark(){ 
    print('Dog is barking....'); 
  } 

}
 
    ตัวอย่างโค้ดข้างต้น เราจะเห็นว่า Dog class ทำการสืบทอดจาก Animal class ทำให้ Dog class มี color และ eat() ที่ได้ทำการสืบทอดมา
ดังนั้น เมื่อเราทำการเรียกใช้คำสั่งแสดงข้อมูล และทดสอบการทำงาน ก็จะได้ค่า color เท่ากับ black และ eat() method ก็ทำการแสดงข้อความ
ว่า Animal is eating.... ออกมา
    ทีนี้ เราต้องการกำหนดค่าให้กับ color และ eat() ของ Dog class เอง โดยไม่ใช้ค่าจากที่ทำการสืบทอดมา เราเรียกวิธีการนี้ว่า การ Overriding
โดยทำการกำหนดค่าเป็นดังนี้
 
void main () {
  var dog = Dog();
  print(dog.color); // output: white
  dog.eat(); // output: Dog is eating....
}
class Animal{
  String color = 'black'; 

  void eat(){ 
    print('Animal is eating....'); 
  } 
}
class Dog extends Animal{
  String breed; 
  
  @override
  String color = 'white';

  @override
  void eat(){
    print('Dog is eating....'); 
  }
  
  void bark(){ 
    print('Dog is barking....'); 
  } 

}
 
    โดยการ override เราต้องกำหนด property และ method ให้เป็นประเภทข้อมูลเดียวกันกับใน parent class อย่าง color ก็ต้องเป็น String
เช่นเดียวกัน โดยทั้ง color และ eat() เรามีการกำหนดค่าในรูปแบบเดียวกัน แต่ให้มีค่าและการทำงานที่ต่างไปจาก parent class ข้างต้น
เรากำหนด color เป็น white และ กำหนด eat() ให้ทำการพิมพ์ข้อความ Dog is eating..... แทน 
    สังเกตว่า ในการทำการ overriding เรามีการใช้งาน meatada @override เพื่อเป็นการระบุหมายเหตุกำกับเพิ่มเติมให้ทราบว่า property และ
method นั้นๆ เป็นการ override ค่าจาก parent อีกที
 
    ถึงตอนนี้เกิดคำถามว่า เราสามารถเรียกใช้ color property และ eat() method ใน parent class ได้ไหม ในเมื่อทำการ override ไปแล้ว  
คำตอบก็คือ เราสามารถ เรียกใช้ property และ method ของ parent class ที่ทำการ override ไปแล้วด้วย การใช้ super keyword ตามด้วย property
หรือ method ที่จะใช้งาน โดย super ก็คือ parent class ที่เราทำการสืบทอดมานั่นเอง ดูตัวอย่างการใช้งาน ตามตัวอย่างโค้ดด้านล่าง
 
void main () {
  var dog = Dog();
  print(dog.color); // output: white
  dog.eat(); 
  // output
// Animal is eating....
// Dog is eating....
// Animal is eating....
// black  
}
class Animal{
  String color = 'black'; 

  void eat(){ 
    print('Animal is eating....'); 
  } 
}
class Dog extends Animal{
  String breed; 
  
  @override
  String color = 'white';

  @override
  void eat(){
    super.eat(); // output: Animal is eating....
    print('Dog is eating....'); // output: Dog is eating....
    super.eat(); // output: Animal is eating....
    print(super.color); // output: black
  }
  
  void bark(){ 
    print('Dog is barking....'); 
  } 

}
 
 
 

การสืบทอดกับการใช้งาน Constructor

    จากตัวอย่างการใช้งาน Animal และ Dog class ข้างต้น เราจะเห็นว่า class ทั้งสองมีการใช้งาน default constructor หรือก็คือส่วนที่ทำงาน
ทันที เมื่อมีการสร้าง object จาก class สามารถทบทวนเนื้อหาเกี่ยวกับการใช้งาน constructor เพิ่มเติมได้ที่บทความ http://niik.in/942
    เราจะทดลอง กำหนด default constructor ให้กับ class ที่มีการสืบทอด เพื่อดูทำงานดังนี้
 
void main () {
  var dog = Dog(); // สร้าง instance object
  // output 
// Animal default constructor
// Dog default constructor  
}
class Animal{
  String color;

  // default constructor
  Animal(){
    print("Animal default constructor");
  }
  
  void eat(){ 
    print('Animal is eating....'); 
  } 
}
class Dog extends Animal{
  String breed; 
  
  // default constructor
  Dog(){
    print("Dog default constructor");
  }
  
  void bark(){ 
    print('Dog is barking....'); 
  } 

}
 
    สังเกตว่า ทันทีที่มีการสร้าง object ขึ้นมา constructor ของ class ที่มีการสืบทอดกัน จะเริ่มทำงานทันที ตามตัวอย่างเราลองให้ทำการ print
ข้อความเพื่อดูว่า เมื่อทำการสร้าง Dog object จาก Dog class ที่สืบทอดมาจาก Animal class จะเกิดอะไรขึ้น
    ผลที่ได้ก็คือ default constructor ของ Animal class ที่เป็น parent class จะเริ่มทำงานก่อน แล้วตามด้วย default constructor ของ Dog class
ตามลำดับ ทั้งนี้เนื่องจากใน default constructor ของ child class มีการเรียกใช้งาน default constructor ของ parent class เป็นค่าเริ่มต้นในรูปแบบ
 
  // default constructor แบบกำหนดการเรียกใช้งาน constructor ของ parent class แบบชัดเจน
  Dog() : super(){  // มีค่าเท่ากับ Dog() ที่ไม่กำหนดการเรียกใช้งาน constructor ของ parent class
    print("Dog default constructor");
  }
 
    นั่นหมายความว่า Dog() constructor จะมีการเรียกใช้งาน super() ซึ่งเป็น ของ Animal class ที่เป็น parent อัตโนมัติโดยที่เราจะกำหนดการเรียกใช้งาน
: super() หรือไม่ก็ตาม ดังนั้น ผลลัพธ์ของ Dog() และ Dog() : super() จึงมีค่าเท่ากัน คือมีการเรียกใช้งาน default constructor ของ parent class ก่อนแล้ว
ทำการเรียกใช้งาน default contructor ของ child class ตามลำดับ
 
    มาดูตัวอย่างต่อ สมมติว่า parent class มีการกำหนดเป้น parameter constructor เราจะทำการส่งค่าจาก child class อย่างไร 
ดูตัวอย่างโค้ดด้านล่างประกอบ
 
void main () {
  // มีการส่งค่า argument 2 ค่าในการสร้าง dog object
  var dog = Dog('Poodle','white'); // สร้าง instance object
}
class Animal{
  String color;

  // parameter constructor
  // มีการกำหนด parameter 1 ค่า คือ color
  Animal(String color){
    this.color = color;
    print("Animal parameter constructor");
  }
  
}
class Dog extends Animal{
  String breed; 
  
  // parameter constructor
  // มีการกำหนด parameter 2 ค่า และทำการส่งค่าตัวแปร color ไปยัง
  // parameter constructor ของ parent class โดยใช้ super(color)
  Dog(String breed, String color) : super(color){
    print("Dog parameter constructor");
  }
  
}
 
    จากตัวอย่างข้างต้น จะเห็นว่า ทั้ง parnent class และ child class มีการใช้งาน parameter constructor โดยใน parent class มีการกำหนด 
parameter 1 ค่าคือ color ในขณะที่ child class มีการกำหนด parameter 2 ค่าคือ breed และ color โดยมีการส่งต่อค่า parameter color ไปยัง
constructor ของ parent class โดยใช้คำสั่ง  : super(color)
 
    ต่อไป มาดูตัวอย่างเพิ่มเติม เกี่ยวกับการใช้งาน named constructor
 
void main () {
  // มีการส่งค่า argument 2 ค่าในการสร้าง dog object
  var dog = Dog('Poodle','white'); // สร้าง instance object
  
  // สร้าง instance object จาก named constructor
  var dog2 = Dog.myNameConstructor(); 
}
class Animal{
  String color;

  // parameter constructor
  // มีการกำหนด parameter 1 ค่า คือ color
  Animal(String color){
    this.color = color;
    print("Animal parameter constructor");
  }
  
  // กำหนด named constructor ใช้ชื่ออะไรก็ได้ตามต้องการ 
  Animal.myNameConstructor(){
    print("Animal named constructor");
  }
  
  // กำหนด named constructor แบบมี paramter
  Animal.myNameConstructor2(String color){
    print("Animal named constructor with parameter");
  }  
  
}
class Dog extends Animal{
  String breed; 
  
  // parameter constructor
  // มีการกำหนด parameter 2 ค่า โดยเรียกใช้งาน named constructor
  // ของ parent class แบบไม่มีการส่งค่า parameter โดยใช้ super.myNameConstructor()
  Dog(String breed, String color) : super.myNameConstructor(){
    print("Dog parameter constructor");
  }
  
  // ตัวอย่างกรณี named paramter ของ parent class มีการใช้งาน parameter จะได้เป็น
//   Dog(String breed, String color) : super.myNameConstructor(color){
//     print("Dog parameter constructor");
//   }  
  
  // named constructor
  // ไม่มีการกำหนด paramter และเรียกใช้งาน named constructor
  // ของ parent class แบบไม่มีการส่งค่า parameter โดยใช้ super.myNameConstructor()
  Dog.myNameConstructor() : super.myNameConstructor(){
    print("Dog named constructor");
  }
  
}
 
 
    ตัวอย่างข้างต้น ใน parent class มีการกำหนด parameter constructor , named constructor with paramter และ named constructor 
without parameter โดยในกรณีที่ parent class มีการกำหนด parameter constuctor เราจะกำหนด Dog constructor แบบไม่มี parameter ไม่ได้
ทั้งนี้เพราะ เราต้องส่งค่าเข้าไปใช้งานใน Animal constructor ดูกรณีต่างๆ เพิ่มเติมด้านล่าง
 
  // error เพราะ parent มีการใช้งาน parameter ต้องส่งค่าเข้าไป
//   Dog(){   }
//   Dog() : super(){}
 
    อย่างน้อยต้องส่งค่า color เข้าไป 
 
  // ไม่ error มีการส่งค่าไปให้ parent ที่มีการใช้งาน parameter 
  Dog(String color) : super(color){}
 
    กรณีใช้งาน named constructor แบบไม่มี parameter
 
  // ไม่ error เพราะ parent มีการใช้งาน named constructor แบบไม่มีการกำหนด parameter 
  // จึงไม่จำเป็นต้องส่งค่าใดๆ เข้าไป
  Dog() : super.myNameConstructor(){}
 
    กรณีใช้งาน named constructor แบบมี paramter
 
  // error มีการใช้งาน named constructor ใน parent แบบมี paramter
  // ต้องส่งค่า paramter เข้าไป
  Dog() : super.myNameConstructor2(){}
 
 
  // ไม่ error มีการใช้งาน named constructor ใน parent แบบมี paramter
  // และมีการส่งค่า paramter เข้าไป  
  Dog(String color) : super.myNameConstructor2(color){}
 
 
    สามารถทบทวนเนื้อหาเกี่ยวกับการใช้งาน constructor เพิ่มเติมได้ที่บทความ http://niik.in/942  
 
    หวังว่าจะเป็นแนวทางทำความเข้าใจ เกี่ยวกับการใช้งานการสืบทอดของ class เบื่องต้น เพื่อทำความเข้าใจในส่วนอื่นๆ ต่อไป


กด Like หรือ Share เป็นกำลังใจ ให้มีบทความใหม่ๆ เรื่อยๆ น่ะครับ



อ่านต่อที่บทความ









เนื้อหาที่เกี่ยวข้อง









URL สำหรับอ้างอิง





คำแนะนำ และการใช้งาน

สมาชิก กรุณา ล็อกอินเข้าระบบ เพื่อตั้งคำถามใหม่ หรือ ตอบคำถาม สมาชิกใหม่ สมัครสมาชิกได้ที่ สมัครสมาชิก


  • ถาม-ตอบ กรุณา ล็อกอินเข้าระบบ
  • เปลี่ยน


    ( หรือ เข้าใช้งานผ่าน Social Login )







เว็บไซต์ของเราให้บริการเนื้อหาบทความสำหรับนักพัฒนา โดยพึ่งพารายได้เล็กน้อยจากการแสดงโฆษณา โปรดสนับสนุนเว็บไซต์ของเราด้วยการปิดการใช้งานตัวปิดกั้นโฆษณา (Disable Ads Blocker) ขอบคุณครับ