Lec05 :: การสืบทอด (inheritance) โดย อ. นัฐพงศ์ ส่งเนียม http://www.siam2dev.com xnattapong@hotmail.com เขียนโปรแกรม Java เบื้องต้น
หัวข้อ การสืบทอด การเข้าใช้แบบ protected พอลิมอร์ฟิซึม การสืบทอดในภาษาจาวา คลาส Object
การสืบทอด
สัตว์ต่างๆ คลาสแมว คลาสปลา คลาสลิง แอตทริบิวต์ ได้แก่ อายุ และ ความหิว เมธอด ได้แก่ กิน() และ นอน() คลาสปลา แอตทริบิวต์ ได้แก่ อายุ ความหิว และชื่อ (เช่น ปลานีโม) คลาสลิง เมธอดได้แก่ กิน() นอน() และเก็บลูกมะพร้าว()
แอททริบิวท์และเมธอดที่คล้ายกัน
การสืบทอด แก้ปัญหาการเขียนโปรแกรมซ้ำซ้อนกันได้ เมธอดหรือแอตทริบิวต์ที่คลาสต่างๆ มีร่วมกันจะถูกนำไปใส่ในคลาสแม่ คลาสลูกจะสืบทอดเมธอดและแอตทริบิวต์คลาสแม่โดยอัตโนมัติ
การทดสอบการสืบทอด แต่งประโยคโดยใช้คำว่า “เป็น” เข้าช่วย ลิงเป็นสัตว์ สัตว์เป็นลิง ลิงไซบอร์ก เป็น ลิงและหุ่นยนต์ รถยนต์เป็นพาหนะ เครื่องยนต์เป็นรถยนต์
คลาสสัตว์ แมว ปลา และลิง เป็นสัตว์ สัตว์ทุกตัวสามารถกินและนอนได้ เราจึงนำแอตทริบิวต์และเมธอดที่สัตว์ต่างๆมีร่วมกัน ไปใส่ในคลาสสัตว์ class สัตว์ { // แอตทริบิวต์ อายุ; ความหิว; // เมธอด กิน() {… } นอน() {… } }
การสืบทอด รูปแบบ ตัวอย่าง class คลาสลูก extends คลาสแม่ } class ปลา extends สัตว์ { class ลิง extends สัตว์ {
วัตถุในคลาสลูกสามารถทำงานตามเมธอดของคลาสแม่ได้ ลิง ล = new ลิง(); ล.กิน();
เพิ่มเมธอดในคลาสลูก class ลิง extends สัตว์ { // เมธอดที่เพิ่มเข้ามา เก็บลูกมะพร้าว() { … } } ลิง ล = new ลิง(); ล.เก็บลูกมะพร้าว(); สัตว์ ส = new สัตว์(); ส.เก็บลูกมะพร้าว();
เพิ่มแอททริบิวท์ในคลาสลูก class ปลา extends สัตว์ { // แอตทริบิวต์ที่เพิ่มเข้ามา ชื่อ; }
พอลิมอร์ฟิซึม (Polymorphism)
การโอเวอร์ไรด์เมธอด class ปลา extends สัตว์ { // แอตทริบิวต์ ชื่อ; // เมธอด นอน() { // วิธีการนอนของปลา … }
พอลิมอร์ฟิซึม
ความหมาย poly แปลว่าหลายหรือมาก morphism นั้นมาจากคำว่า morph ซึ่งแปลว่ารูปร่าง รวมกันแล้วหมายถึงความสามารถที่สิ่งหนึ่งจะมีได้หลายรูปร่าง ซึ่งเมื่อใช้คำนี้กับการโปรแกรมเชิงวัตถุ ก็จะหมายถึงการที่คำสั่งแบบเดียวกันสามารถถูกแปลได้หลายแบบ
พอลิมอร์ฟิซึมกับการนำกลับมาใช้ใหม่ พอลิมอร์ฟิซึมสนับสนุน การนำกลับมาใช้ใหม่ (reuse) ถ้าเราได้เขียนโปรแกรมที่ใช้งานได้กับสัตว์ โปรแกรมของเราย่อมใช้ได้กับแมว ปลา และลิงนอกจากนั้นถ้ามีคนสร้างคลาสอีกัวน่าขึ้นมาใหม่ โปรแกรมที่เราเขียนก็สามารถใช้ได้กับคลาสอีกัวน่าเช่นกัน
การสืบทอดในภาษาจาวา
คลาสรูปร่างและคลาสที่สืบทอด
คลาสรูปร่าง รูปร่างเป็นรูปที่อยู่ในระนาบสองมิติ มีพื้นที่ มีสี เมธอด getArea() ใช้คำนวณหาพื้นที่ของรูปร่าง มีสี public enum Color { Red, Green, Blue } เมธอด getColor()
คลาสรูปร่าง public class Shape { public Shape() { color = Color.Red; } public double getArea() { return 0; } public void setColor(Color c) { color = c; } public Color getColor() { return color; } private Color color; }
ทดสอบคลาสรูปร่าง public class TestShape { public static void main(String[] args) { Shape s1 = new Shape(); System.out.println(s1.getColor()); System.out.println(s1.getArea()); Shape s2 = new Shape(); s2.setColor(Color.Blue); System.out.println(s2.getColor()); System.out.println(s2.getArea()); }
คอมไพล์และรัน
คลาสสี่เหลี่ยมผืนผ้า class Rectangle extends Shape { ... public double getArea() { return width * height; }
คลาส Object
เมธอด toString() public class Rectangle extends Shape { ... public String toString() { String str = "Rectangle"; str += " color=" + getColor(); str += " width=" + width; str += " height=" + height; str += " area=" + getArea(); return str; }
เมธอด toString() เป็นเมธอดที่ถูกโอเวอร์ไรด์จากคลาส Object
คลาส Object แม่ของทุกคลาส เมธอดที่น่าสนใจ toString() equals() clone() hashCode()
เมธอด equals() public boolean equals(Object otherObject) { if (otherObject instanceof Rectangle) { Rectangle otherRect = (Rectangle) otherObject; boolean equalWidth = width == otherRect.width; boolean equalHeight = height == otherRect.height; return equalWidth && equalHeight; } return false;
เมธอด hashCode() จะส่งจำนวนเต็มที่เป็นรหัสแฮช เพื่อการค้นหาข้อมูลที่รวดเร็ว public int hashCode() { return (int) (width + height * 17); }
เมธอด clone() สร้างวัตถุที่เหมือนกับวัตถุที่ได้รับข้อความ public Object clone() { Rectangle clone = new Rectangle(width, height); return clone; }
ทดสอบเมธอดที่โอเวอร์ไรด์จากคลาส Object public class TestRectangle3 { public static void main(String[] args) { Rectangle r1 = new Rectangle(2, 5); System.out.println(r1); System.out.println(r1.hashCode()); Rectangle r2 = new Rectangle(5, 2); System.out.println(r1.equals(r2)); Rectangle r3 = (Rectangle) r1.clone(); System.out.println(r1.equals(r3)); }
สรุปเมธอดในคลาสสี่เหลี่ยมผืนผ้า
การห้ามโอเวอร์ไรด์
คลาสสี่เหลี่ยมจัตุรัส public class Square extends Rectangle { public Square( double w) { super(w,w); } ...
ไฟนอลคลาส ไม่สามารถถูกสืบทอดได้ public final class Square
ไฟนอลเมธอด ไม่สามารถถูกโอเวอร์ไรด์ได้ public class Shape { public final Color getColor() { return color; } ...
การเข้าใช้แบบ protected
การเข้าใช้แบบ protected การเข้าใช้แบบนี้มีความเข้มงวดน้อยกว่าแบบ private ซึ่งห้ามคลาสอื่นใดเข้าใช้ แต่ก็อิสระน้อยกว่าแบบ public ที่ใครๆก็สามารถเข้าใช้ได้
การเข้าใช้แบบ protected
คลาสวงกลม public class Circle extends Shape { protected double radius; public Circle(double r) { radius = r; } public double getRadius() { return radius; ...
คำนวณพื้นที่ของวงแหวน public class Ring1 extends Circle { public double getArea() { double outerArea = Math.PI * radius * radius; double innerArea = Math.PI * innerRadius * innerRadius; return outerArea - innerArea; } ...
คำนวณพื้นที่ของวงแหวน public class Ring2 extends Circle { public double getArea() { double outerArea = super.getArea(); double innerArea = Math.PI * innerRadius * innerRadius; return outerArea - innerArea; } ...
แอบสแตรกท์คลาส (abstract class)
คลาสที่ประกาศขึ้นมาลอยๆ (Abstract Class) เมธอด getArea() ในคลาส Shape ไม่ควรส่งค่าศูนย์หรือค่าใดๆกลับมา ทั้งนี้เพราะว่าเราไม่ทราบวิธีคำนวณหาพื้นที่ของวัตถุ Shape public abstract double getArea(); เมธอดที่ถูกประกาศขึ้นมาลอยๆ (abstract) นี้ต้องอยู่ในคลาสที่เป็น abstract ด้วยเช่นกัน คลาส Shape เขียนใหม่ได้ดังนี้ public abstract class Shape { ... }
การใช้งาน abstract class ไม่สามารถสร้างวัตถุในคลาสนี้ได้ Shape s = new Shape(); แต่อ้างถึงวัตถุในคลาสลูกได้ Shape s; s = new Rectangle(3,2); s = new Square(5);
พอลิมอร์ฟิซึม public class TestPolymorphism { public static void main(String[] args) { Shape shape; shape = new Rectangle(10, 20); System.out.println(shape.getArea()); shape = new Circle(10); }
การนำมาใช้อีก public class ShapeArray { public static double sumArea(Shape[] shapes) { double sum = 0; for( Shape s : shapes) { sum += s.getArea(); } return sum;
การนำมาใช้อีก Shape[] s = new Shape[3]; s[0] = new Triangle(10); s[1] = new Rectangle(10,20); s[2] = new Pentagon(20); double d = ShapeArray.sumArea(s);
ข้อดีข้อเสียของการสืบทอด
ข้อดีของการสืบทอด การนำกลับมาใช้ใหม่ ความเป็นมาตรฐานเดียวกัน ถ้าเราต้องการสร้างคลาสที่มีความสามารถคล้ายๆกับคลาสที่มีอยู่แล้ว เราอาจจะใช้การสืบทอดแทนที่จะเขียนขึ้นมาใหม่หมด ความเป็นมาตรฐานเดียวกัน คลาสพื้นฐานเป็นการกำหนดโครงแบบในการระบุความสามารถของวัตถุในคลาสลูก เข้าใจสาระสำคัญได้ง่าย
ข้อเสียของการสืบทอด โปรแกรมทำงานช้าลง มีค่าใช้จ่าย (overhead) ในค้นหาและเรียกใช้เมธอดของคลาสที่สืบทอด แต่ค่าใช้จ่ายดังกล่าวถือว่าน้อยมากเมื่อเทียบกับประโยชน์ที่ได้รับจากการสืบทอด โปรแกรมมีขนาดใหญ่ขึ้น แต่หน่วยความจำราคาไม่แพง ความซับซ้อนเพิ่มขึ้น ผู้ใช้ต้องหาตามคลาสต่างๆ ที่อยู่ในผังการสืบทอดจนกว่าจะพบคลาสที่อิมพลีเมนต์เมธอดนั้น ปัญหาที่กล่าวถึงนี้มีชื่อว่าปัญหาลูกดิ่ง (Yo-yo problem)
สรุป
สรุป การสืบทอดในภาษาจาวาใช้แนวความคิดเดียวกับการถ่ายทอดคุณลักษณะจากพ่อและแม่ คลาสลูกสืบทอดความสามารถจากคลาสแม่ การทดสอบการสืบทอดทำได้โดยการแต่งประโยคที่มีคำว่าเป็น การสืบทอดทำให้เกิดการนำไปใช้ใหม่ได้ คลาสลูกสามารถใช้เมธอดในคลาสแม่ได้ ถ้าเมธอดนั้นเป็น public หรือ protected
สรุป เมธอดที่ใช้กับคลาสแม่ได้ ก็สามารถใช้กับคลาสลูกได้ คลาสลูกสามารถโอเวอร์ไรด์เมธอดในคลาสแม่ได้ พอลิมอร์ฟิซึมคือการที่คำสั่งเดียวกันสามารถทำให้เกิดการทำงานที่ต่างกันขึ้นกับชนิดของวัตถุ แอ็บสแตร็กต์เมธอดคือเมธอดที่ไมได้ระบุวิธีการทำงาน แอ็บสแตร็กต์เมธอดต้องอยู่ในแอ็บสแตร็กต์คลาส แอ็บสแตร็กต์คลาสเป็นเหมือนกับโครงที่กำหนดให้คลาสลูกต้องอิมพลีเมนต์แอ็บสแตร็กต์เมธอด