สวัสดี! ในปาฐกถาครั้งก่อนๆ เราได้พบเห็นแนวคิดเรื่องมรดกโดยสังเขปมาแล้วหลายครั้ง วันนี้เราจะพูดถึงหัวข้อนี้ด้วย แต่ก็ไม่ได้ลึกซึ้งเกินไป จะมีการบรรยายโดยละเอียดเกี่ยวกับเรื่องนี้ในภายหลัง แต่วันนี้เราจะมาดูตัวอย่างเชิงปฏิบัติและทำความคุ้นเคยกับโอเปอเรเตอร์ที่น่าสนใจตัวหนึ่งในภาษา Java
มรดกจาวา
ดังนั้นมรดกที่แท้จริงคืออะไร? การสืบทอดเป็นกลไกในการเขียนโปรแกรม รวมถึง Java ที่ให้คุณอธิบายคลาสใหม่ตามคลาสที่มีอยู่ คลาสลูกหลานจะสามารถเข้าถึงฟิลด์และวิธีการของคลาสพาเรนต์ได้ เหตุใดสิ่งนี้จึงจำเป็น? ตัวอย่างเช่น ลองจินตนาการว่าคุณต้องสร้างรถยนต์หลายคลาสในโปรแกรม: รถบรรทุก, รถแข่ง, ซีดาน, รถกระบะ ฯลฯ แม้จะไม่ได้เริ่มเขียนโค้ด แต่คุณคงทราบดีว่าคลาสเหล่านี้มีอะไรเหมือนกันหลายอย่าง เช่น รถยนต์ทุกคันมีชื่อรุ่น ปีที่ผลิต ขนาดเครื่องยนต์ ความเร็วสูงสุด ฯลฯ (ไม่ต้องพูดถึงว่าทุกคันมีล้อและอะไหล่อื่นๆ) ในสถานการณ์เช่นนี้ คุณสามารถ:- สร้างฟิลด์เหล่านี้ในแต่ละคลาสและเพิ่มลงในคลาสรถใหม่เมื่อถูกสร้างขึ้น
- ย้ายฟิลด์ทั่วไปสำหรับเครื่องทั้งหมดไปยังคลาสพาเรนต์
Car
และคลาสทั้งหมดของประเภทเฉพาะของเครื่องจะสืบทอดจากCar
การใช้คำว่าขยาย
public class Car {
private String model;
private int maxSpeed;
private int yearOfManufacture;
public Car(String model, int maxSpeed, int yearOfManufacture) {
this.model = model;
this.maxSpeed = maxSpeed;
this.yearOfManufacture = yearOfManufacture;
}
}
public class Truck extends Car {
public Truck(String model, int maxSpeed, int yearOfManufacture) {
super(model, maxSpeed, yearOfManufacture);
}
}
public class Sedan extends Car {
public Sedan(String model, int maxSpeed, int yearOfManufacture) {
super(model, maxSpeed, yearOfManufacture);
}
}
อย่างน้อยที่สุด เราได้หลีกเลี่ยงการทำซ้ำโค้ดโดยไม่จำเป็น และนี่คือสิ่งที่เราควรมุ่งมั่นเสมอเมื่อเขียนโปรแกรม นอกจากนี้ เรามีโครงสร้างคลาสที่เรียบง่ายและเข้าใจได้: ฟิลด์ที่ใช้ร่วมกับเครื่องจักรทั้งหมดจะอยู่ในคลาสเดียว ตัวอย่างเช่น หากรถบรรทุกมีฟิลด์เฉพาะที่รถคันอื่นไม่มี ก็สามารถประกาศไว้ในคลาสTruck
ได้ เช่นเดียวกับวิธีการต่างๆ รถยนต์ทุกคันมีพฤติกรรมบางอย่างที่เหมือนกันซึ่งสามารถอธิบายได้ เช่น สตาร์ทรถ เติมแก๊ส/เบรก ฯลฯ วิธีการทั่วไปเหล่านี้สามารถจัดอยู่ในคลาสทั่วไปCar
และพฤติกรรมเฉพาะของแต่ละประเภทสามารถอธิบายได้ในคลาสสืบทอด
public class Car {
public void gas() {
//...gas
}
public void brake() {
//...brake
}
}
public class F1Car extends Car {
public void pitStop() {
//...only racing cars make pit stops
}
public static void main(String[] args) {
F1Car formula1Car = new F1Car();
formula1Car.gas();
formula1Car.pitStop();
formula1Car.brake();
}
}
เราได้ย้ายวิธีการทั่วไปของรถทุกคันมาไว้ในชั้นเรียนCar
แล้ว แต่ในคลาสผู้สืบทอดF1Car
ซึ่งอธิบายรถแข่ง Formula 1 - ป้ายหยุด (หยุดเพื่อการบำรุงรักษารถอย่างเร่งด่วน) ซึ่งทำในการแข่งรถเท่านั้นและมีความโดดเด่นด้วยพฤติกรรมเฉพาะ
ตัวดำเนินการอินสแตนซ์ Java
ในการตรวจสอบว่าวัตถุถูกสร้างขึ้นตามคลาสหรือไม่ มีตัวดำเนินการพิเศษใน Javainstanceof
- มันจะคืนค่าtrue
หากการทดสอบเป็นจริงหรือfalse
หากผลลัพธ์เป็นเท็จ มาดูกันว่ามันทำงานอย่างไรโดยใช้คลาสรถของเราเป็นตัวอย่าง:
public class Truck extends Car {
public static void main(String[] args) {
Truck truck = new Truck();
System.out.println(truck instanceof Car);
}
}
ผลลัพธ์: จริง เช็คโดยใช้ตัวดำเนินinstanceof
การส่งคืนtrue
เนื่องจากเรามีวัตถุของคลาสTruck
และรถบรรทุกทั้งหมดเป็นรถยนต์ คลาสTruck
เป็นผู้สืบทอดของคลาสCar
ดังนั้นรถบรรทุกทั้งหมดจึงถูกสร้างขึ้นโดยมีพื้นฐานมาจากรถยนต์ระดับกลางทั่วไป ให้ความสนใจกับตัวดำเนินการinstanceof
: มันเขียนโดยไม่มีจุด เนื่องจากเป็นตัวดำเนินการ ไม่ใช่วิธีการ (“อินสแตนซ์ของวัตถุของคลาส”) เรามาลองแตกต่างออกไป:
public static void main(String[] args) {
Car car = new Car();
System.out.println(car instanceof Truck);
}
ผลลัพธ์: false คลาสCar
และวัตถุของคลาสไม่ได้มาจากคลาสTruck
รถบรรทุกทั้งหมดเป็นรถยนต์ แต่ไม่ใช่รถยนต์ทุกคันที่เป็นรถบรรทุก วัตถุCar
ไม่ได้ถูกสร้างขึ้นตามTruck
คลาส อีกตัวอย่างหนึ่ง:
public static void main(String[] args) {
Car car = new Car();
Truck truck = new Truck();
System.out.println(car instanceof Object && truck instanceof Object);
}
ผลลัพธ์: จริง ตรรกะที่นี่ก็เรียบง่ายเช่นกัน: คลาสทั้งหมดใน Java รวมถึงคลาสที่คุณสร้างขึ้นนั้นมาจากคลาสObject
(แม้ว่าคุณจะไม่ได้เขียนส่วนขยาย Object ในคลาสเหล่านั้น - กลไกนี้มีนัยอยู่ในคลาสเหล่านั้น) เหตุใดสิ่งนี้จึงอาจมีประโยชน์และภายใต้สถานการณ์ใด การใช้โอเปอเรเตอร์ที่พบบ่อยที่สุดinstanceof
คือการแทนที่equals()
เมธอด ตัวอย่างเช่น นี่คือวิธีการนำไปใช้equals
ในชั้นเรียนString
:
public boolean equals(Object anObject) {
if (this == anObject) {
return true;
}
if (anObject instanceof String) {
String anotherString = (String) anObject;
int n = value.length;
if (n == anotherString.value.length) {
char v1[] = value;
char v2[] = anotherString.value;
int i = 0;
while (n-- != 0) {
if (v1[i] != v2[i])
return false;
i++;
}
return true;
}
}
return false;
}
ก่อนที่จะเปรียบเทียบสตริงกับอ็อบเจ็กต์ที่ส่งผ่าน วิธีการจะตรวจสอบ: อ็อบเจ็กต์ที่ส่งผ่านนั้นเป็นสตริงจริงหรือไม่ จากนั้นเขาก็เริ่มเปรียบเทียบคุณสมบัติของวัตถุสองชิ้น หากไม่มีการตรวจสอบนี้ คุณสามารถส่งออบเจ็กต์ใดๆ ที่มีค่าและความยาว ไปยังเมธอด และเปรียบเทียบกับสตริงได้ ซึ่งแน่นอนว่าอาจผิดได้
GO TO FULL VERSION