Lab03: Inheritance

latest update  16.18   30 June 2009  เรื่องการใช้งาน error ของเราเอง เพื่อให้ทำการบ้านได้ เพิ่ิมเข้ามาแล้ว

เป้าหมายของแล็บนี้คือ
  • ให้นิสิตทดลองโค้ดที่เล่นกับ inheritance หลายๆแบบ
  • ให้ตัวอย่างซึ่งนิสิตสามารถเอาไปทดลองต่อได้อีก
  • ให้เห็นตัวอย่างของการใช้ type ที่ต่างกับตัว object
  • หลักการคือ ทดลองทำ ค้นหาเหตุผล แล้วอธิบายออกมาให้ได้ โดยเขียนคำตอบลงใน text file

หมายเหตุ :text file ให้เขียนใน Notepad หรือ word  อย่าไปทำใน Eclipse เพราะ Eclipse ของบางคนใช้ภาษาไทยไม่ได้นะ

Inheritance

แล็บนี้จะให้ทำเป็นขั้นๆไปเรื่อยๆ เอาให้ทุกคนเข้าใจหลักการ Inheritance ให้ได้ ก่อนอื่นมาทวน inheritance กันหน่อย

 

เริ่มทำ

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

 

public class Bicycle {

     

    // the Bicycle class has three fields

    public int cadence;

    public int gear;

    public int speed;

     

    // the Bicycle class has one constructor

    public Bicycle(int startCadence, int startSpeed, int startGear) {

        gear = startGear;

        cadence = startCadence;

        speed = startSpeed;

    }

     

    // the Bicycle class has four methods

    public void setCadence(int newValue) {

        cadence = newValue;

    }

     

    public void setGear(int newValue) {

        gear = newValue;

    }

     

    public void applyBrake(int decrement) {

        speed -= decrement;

    }

     

    public void speedUp(int increment) {

        speed += increment;

    }

     

}

 

คราวนี้มาลองทำ MountainBike ซึ่งเป็นสับคลาสของ Bicycle ลอกโค้ดข้างล่างนี้ไปเลย:

 

public class MountainBike extends Bicycle {

     

    // the MountainBike subclass adds one field

    public int seatHeight;

 

    // the MountainBike subclass has one constructor

    public MountainBike(int startHeight, int startCadence,

                        int startSpeed, int startGear) {

        super(startCadence, startSpeed, startGear);

        seatHeight = startHeight;

    }

     

    // the MountainBike subclass adds one method

    public void setHeight(int newValue) {

        seatHeight = newValue;

    }

}

 

ตกลง MountainBike มีเมธอดกี่เมธอด ตัวแปรกี่ตัวแปร จงแจกแจงอธิบายมาใน text file

ลองเปลี่ยน ตัวแปรของ Bicycle และ MountainBike ให้เป็น private กับ protected ดู แล้วลองเขียน main ขึ้นมาใน MountainBike ให้อ่านค่าตัวแปรเหล่านั้นโดยตรง

เมื่อเรียกเมธอดที่เปลี่ยนค่าและเมธอดที่อ่านค่า (เขียนเมธอดที่ขาดไป ให้ครบ ให้เป็น public ทั้งหมด) ตัวแปร โดยเรียกใน main ของ MountainBike  ถามว่า เกิดอะไรขึ้นบ้าง จงเขียนอธิบายมาใน text file


คราวนี้ลองสร้างคลาสใหม่เลย แล้วเอา main ของ MountainBike ที่พึ่งเขียนไปข้างบนนี้ ไปใส่คลาสใหม่  คลาสนั้นคอมไพล์และรันได้หรือไม่  อย่าลืมอธิบายให้ครบทั้งในกรณี private และ protected

ลองเปลี่ยนเมธอด ที่เปลี่ยนค่าตัวแปร ที่เราเรียกใช้ใน main ให้เป็น private แล้วสังเกต ว่าใน MountainBike เมธอดใช้ได้หรือไม่  ในคลาสใหม่ เมธอดใช้ได้หรือไม่ (ลองกับทั้งตัวแปรที่เป็น private และ protected)
ลองเปลี่ยนเมธอด ที่เปลี่ยนค่าตัวแปร ที่เราเรียกใช้ใน main ให้เป็น protected 
แล้วสังเกต ว่าใน MountainBike เมธอดใช้ได้หรือไม่  ในคลาสใหม่ เมธอดใช้ได้หรือไม่ (ลองกับทั้งตัวแปรที่เป็น private และ protected)



ตามปกติ เวลาเราสร้างออบเจ็กต์ขึ้นมา เราก็จะให้ไทป์ของมันตรงกับก้อนออบเจ็กต์จริงๆ เช่น

 

    public MountainBike myBike = new MountainBike();

 

** ก้อนออบเจ็กต์ คือตัวที่สร้างขึ้นด้วยคำสั้ง new ส่วนไทป์นั้น ในที่นี้คือ MountainBike ที่เรานิยามต่อจากคำว่า public

MountainBike นั้น extend มาจาก Bicycle ส่วน Bycycle ก็มาจาก Object (ไม่ได้เขียน extend แต่ว่าเป็นที่่เข้าใจกัน) ดังนั้น MountainBike คันหนึ่งก็ถือเป็นทั้ง Bicycle และ Object ดังนั้นจึงใช้ในที่ที่สามารถใช้ Bicycle และ Object ได้เลย 


จงเขียนโค้ดส่วนนี้ลงใน main ของ MountainBike สังเกตว่าคอมไพล์ได้หรือไม่ บันทึกผลลงใน text file  ถ้าไม่ได้ ต้องบอกสาเหตุด้วย

Object a = new MountainBike();  

ถ้ามีปัญหา ให้เขียนโค้ดแก้ซะ เขียนบอกว่าแก้โค้ดตรงไหนบ้างใน text file

 

แต่ในทางกลับกัน Bicycle คันหนึ่ง ไม่จำเป็นต้องเป็น MountainBike สิ่งที่โค้ดตีความว่าไม่ใช่ MountainBike จึงนำมาใส่ในตัวแปรที่เป็น MountainBike ไม่ได้ 

 
 จงเขียนโค้ดส่วนนี้ต่อลงไปใน main แล้่วดูซิว่ามีอะไรเกิดขึ้น 

    MountainBike myBike = a;

 

จะสังเกตได้ว่าคอมไพล์เลอร์แจ้ง error ออกมา เพราะสำหรับคอมไพเลอร์แล้ว a เป็นวัตถุชนิด Object ซึ่ง Object นั้น ไม่จำเป็นที่จะต้องเป็น MountainBike ดังนั้นจึงใช้ไม่ได้ เราแก้ error นี้ได้ด้วยการ สั่งเปลี่ยนไทป์เลย 

 

จงแก้ให้เป็น

    MountainBike myBike = (MountainBike)a;

การทำอย่างนี้เรียกว่าการ cast ซึ่งจะเป็นการทำ runtine check ว่า a ต้องถูกทำเป็น Mountainbike คอมไพเลอร์จะได้ไม่ต้องห่วงอะไรอีก ถ้าตอนรัน a ไม่ได้เป็นออบเจ็กต์ MountainBike จริงๆละก็ จะมี exception เกิดขึ้น (exception คือ error ที่เกิดตอนรันโปรแกรม)

ลองรันโปรแกรมนี้ดู หลังจากนั้น ทดลองเปลี่ยน new MountainBike() เป็น new Bicycle()  แล้วลองรันใหม่  เกิดอะไรขึ้น รายงานสิ่งที่เกิดและสาเหตุมาใน text file  

เปลี่ยนโค้ดให้กลับเป็นตามเดิม เพื่อไม่ให้เกิด exception

Note: เราสามารถตรวจออบเจ็กต์ได้ว่าเป็นชนิดไหนแน่ โดยใช้ โอเปอร์เรเตอร์ instanceof ตัวอย่างเช่น

 

        if (a instanceof MountainBike) {

           MountainBike myBike = (MountainBike)a;

        }

 ในโค้ดข้างบนนี้ เราสามารถแน่ใจได้ว่า จะไม่มี exception ที่เกิดจากการ cast ไม่ดี แน่นอน 


 Overriding and Hiding Methods

Instance Methods(เมธอดของออบเจ็กต์)

Instance method สำหรับสับคลาส ที่มี signature (ชื่อเมธอด จำนวนพารามิเตอร์ ไทป์ของพารามิเตอร์) และ return type เหมือนกับ instance method ที่นิยามใน superclass จะ override เมธอดของ  superclass 

Class Methods(เมธอดของคลาส)

ถ้าสับคลาสนิยาม class method ที่มี signature เหมือนกับในซุปเปอร์คลาส เรากล่าวได้ว่า เมธอดของสับคลาสนั้น hide เมธอดใน superclass

 

ถ้าเป็น override ตัวเมธอดที่เรียกใช้จะเป็นของตัวสับคลาส แต่ถ้าเป็น hide แล้วละก็ เมธอดที่เรียกใช้จะขึ้นอยู่กับว่าซุปเปอร์คลาสหรือสับคลาสเป็นคนเรียก 

เอาล่ะ ถึงเวลาทำแล็บต่อแล้ว ให้ทำดังต่อไปนี้

  1.       เขียนโค้ดดังนี้

public class Animal {

    public static void testClassMethod() {

        System.out.println("The class method in Animal.");

    }

    public void testInstanceMethod() {

        System.out.println("The instance method in Animal.");

    }

}

 

  1.       สร้างคลาส Cat ซึ่งเป็น subclass ของ Animal ดังต่อไปนี้

public class Cat extends Animal {

    public static void testClassMethod() {

        System.out.println("The class method in Cat.");

    }

    public void testInstanceMethod() {

        System.out.println("The instance method in Cat.");

    }

 

    public static void main(String[] args) {

        Cat myCat = new Cat();

        Animal myAnimal = myCat;

        Animal.testClassMethod();

        myAnimal.testInstanceMethod();

    }

}

 

ลองรันดู ทดลองเปลี่ยน Cat กับ Animal ดูตามที่ต่างๆ ผลจากการรันบอกอะไรเราบ้างเกี่ยวกับการเรียก class method และ instance method (เขียนอธิบายการทดลองแต่ละอย่างให้ละเอียดชัดเจน) จงเขียนรายงานต่อใน text file

 

หลังจากนั้น เพิ่มเมธอด a() เข้าไปในคลาส Cat และเปลียนโค้ดของเมนตามนี้


public void a(){

}
   
    public static void main(String[] args) {

        Cat myCat = new Cat();

        Animal myAnimal = myCat;

        Animal.testClassMethod();

        myAnimal.a();

    }

ดูว่าเกิดอะไรขึ้น คิดว่าทำไมถึงเกิดเหตุการณ์เช่นนี้ เขียนรายงานมาใน text file

ต่อไป ลองเพิ่มเมธอด b ให้ Animal ให้เป็นโค้ดดังนี้


public void b(){
    System.out.println("The instance method b in Animal.");
}

แล้วลองเรียก myAnimal.b(); ดู แทนการเรียก myAnimal.a(); 

เกิดอะไรขึ้นจงอธิบาย และให้เหตุผลขอสิ่งที่เกิดขึ้น ใน text file

์Note: ถ้าเป็นฟิลด์ หรือ instance variable นั้น ฟิลด์ที่ชื่อเหมือนฟิลด์ในซุปเปอร์คลาสจะ hide ฟิิลด์ของซุปเปอร์คลาส แม้ว่าไทป์จะไม่เหมือนกันก็ตาม ดังนั้นเราจะไม่สามารถเข้าถึงฟิลด์ของซุปเปอร์คลาสได้จากภายในสับคลาส นอกเสียจากจะใช้ีคีย์เวิร์ด super




Using the Keyword super

Accessing Superclass Members

ถ้าเมธอดของซุปเปอร์คลาสถูก override เราก็ยังเรียกเมธอดที่ถูก override ได้ โดยการใช้คีย์เวิร์ด super  

ให้ทำดังต่อไปนี้

  1. สร้างคลาสใหม่ ดังรูป 

public class Superclass {

 

    public void printMethod() {

        System.out.println("Printed in Superclass.");

    }

}

 

  1. สร้างสับคลาสที่ override เมธอด printMethod ดังรูป

 

public class Subclass extends Superclass {

 

    public void printMethod() { //overrides printMethod in Superclass

        super.printMethod();

        System.out.println("Printed in Subclass");

    }

 

    public static void main(String[] args) {

        Subclass s = new Subclass();

        s.printMethod();  

    }

}

 

รัน main นี้ดู แล้วอธิบาย ถึงสิ่งที่เกิดขึ้น และสาเหตุของสิ่งที่เกิดขึ้น มาใน text file เดียวกับอธิบาย Animal

 


วิธีการส่ง
แล็บนี้ยังไม่ต้องส่ง จะมีเฉลยขึ้นตอนวันศุกร์ให้

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

ก่อนอื่น มาดูสิ่งที่ต้องอ่านซะก่อน




คราวนี้มาถึงโจทย์การบ้านบ้่าง
1. ให้เอาคลาส Bicycle มาเขียนเพิ่มเติม โดยแก้เมธอดต่างๆ ให้

จากนั้นเขียนคลาสสำหรับทดสอบเมธอดต่างๆของ Bicycle ว่าทำงานถูกต้องหรือไม่  อย่าลืมว่าเมธอดสำหรับทดสอบ ต้องครอบคลุมความเป็นไปได้ให้ดีที่สุด

2. ให้เขียน Class ชื่อว่า Fraction (แปลเป็นไทยว่าเศษส่วน) โดยใน Class นี้มี Attribute 2 ตัว คือ numerator (เศษ) และ denominator (ส่วน) เวลาเอาออบเจ็กต์ของคลาสนี้ไปใช้ ต้องทำเลขให้เป็นเศษส่วนอย่างต่ำ เช่น 2/4 ต้องออกมาเป็น 1/2 นะ และ input เป็น จำนวนเต็มบวกเท่านั้น

มี Method คือ

2.1 public double value() ให้คืนค่าออกมาเป็นทศนิยม ให้ถูกต้องสองตำแหน่ง
          2.2
public Fraction add(Fraction f)  คืนค่าผลบวกของเศษส่่วนสองตัว

2.3 public Fraction subtract(Fraction f)  คืนค่าผลลบ

2.4 public Fraction multiply(Fraction f)  คืนค่าผลคูณ  อย่าลืมว่าต้องเป็นเศษส่วนอย่างต่ำ

2.5 public Fraction divide(Fraction f) – หากส่วนเป็น 0 ให้ร้องว่า “error – zero by division”   ตัวอย่างการเขียน JUnit ของข้อนี้ อยู่นี่ 

ถ้าใครอยากรู้เพิ่มเรื่อง exception หรื error ที่เราสร้างได้เองในจาวา  อ่านตรงนี้

2.6 public boolean GreaterThan(Fraction f) หาก เศษส่วนตัวที่ต้องการเทียบมากกว่า f ให้ตอบ  true เช่น  t เป็น 2/3 และ f เป็น ½ จะได้ว่า t.GreaterThan(f) = true

2.7 public boolean Equals(Fraction f) หาก เศษส่วนตัวที่ต้องการเทียบมีค่าเท่ากับ f ให้ตอบ  true

          2.8 public String printFraction(Fraction f) – ให้คืน String อยู่ในรูป x/y  ไม่ต้องมีเว้นวรรคนะ เขียนเลข และเครื่องหมาย “/” ต่อกันมาเลย

เขียนคลาสสำหรับทดสอบคลาสนี้มาด้วย

การส่งการบ้าน
ส่งจาร์ไฟล์ที่รวม source ทั้งหมด มาทางเมล์ progmethcp@gmail.com ภายในวันพุธที่ 1 กรกฎาคม เวลา 24.00น.

ไฟล์จะต้องตั้งชื่อว่า JUnit_xxxxxxxxxx.jar โดย xxxxxxxxxx นั้นเป็นเลขประจำตัวนิสิต ส่วนใน subject ของเมล์ ให้ใส่ JUnit_ xxxxxxxxxx โดย xxxxxxxxxx นั้นเป็นเลขประจำตัวนิสิต