สวัสดี! พูดคุยเกี่ยวกับคลาสนามธรรมใน Java
"ความเข้าใจผิด" นี้เมื่อไม่มีความชัดเจนว่าควรดำเนินการพฤติกรรมใด เป็นเหตุผลที่แน่ชัดว่าทำไมผู้สร้างของ Java ปฏิเสธการสืบทอดหลายรายการ คุณจะได้เรียนรู้ว่าคลาส Java สามารถใช้อินเทอร์เฟซได้หลากหลาย

ทำไมชั้นเรียนถึงเรียกว่า "นามธรรม"?
คุณคงจำได้ว่าสิ่งที่เป็นนามธรรมคืออะไร — เราได้พูดถึงไปแล้วก่อนหน้านี้ :) หากคุณลืม ก็ไม่ต้องกังวล โปรดจำไว้ว่านี่คือหลักการของ OOPที่กล่าวว่า เมื่อออกแบบคลาสและสร้างออบเจกต์ คุณควรแสดงเฉพาะคุณสมบัติหลักของเอนทิตีและละทิ้งคุณสมบัติรอง ตัวอย่างเช่น หากเรากำลังออกแบบSchoolTeacher
ชั้นเรียนความสูงอาจไม่ใช่คุณสมบัติที่จำเป็นสำหรับครู คุณลักษณะนี้ไม่สำคัญสำหรับครู แต่ถ้าเรากำลังสร้างBasketballPlayer
คลาสความสูงจะเป็นหนึ่งในคุณสมบัติที่สำคัญที่สุด คลาสนามธรรมเป็น "ชิ้นงานหยาบ" ที่เป็นนามธรรมที่สุดสำหรับกลุ่มชนชั้นในอนาคต ไม่สามารถใช้ชิ้นงานโดยตรงได้ — มัน "หยาบ" เกินไป แต่มันกำหนดสถานะลักษณะและพฤติกรรมบางอย่างที่คลาสในอนาคต - ผู้สืบทอดของคลาสนามธรรม - จะมี
ตัวอย่างคลาสนามธรรมในภาษาจาวา
พิจารณาตัวอย่างง่ายๆ กับรถยนต์:
public abstract class Car {
private String model;
private String color;
private int maxSpeed;
public abstract void gas();
public abstract void brake();
public String getModel() {
return model;
}
public void setModel(String model) {
this.model = model;
}
public String getColor() {
return color;
}
public void setColor(String color) {
this.color = color;
}
public int getMaxSpeed() {
return maxSpeed;
}
public void setMaxSpeed(int maxSpeed) {
this.maxSpeed = maxSpeed;
}
}
นี่คือลักษณะของคลาสนามธรรมที่ง่ายที่สุด อย่างที่คุณเห็น ไม่มีอะไรพิเศษ :) ทำไมเราถึงต้องการสิ่งนี้ ประการแรก ให้คำอธิบายที่เป็นนามธรรมที่สุดเกี่ยวกับสิ่งที่เราต้องการ นั่นคือรถยนต์ คำ หลัก ที่เป็นนามธรรมหมายถึงบางสิ่งที่นี่ ในโลกแห่งความเป็นจริงไม่มีสิ่งที่เรียกว่า "รถ" มีทั้งรถบรรทุก รถแข่ง ซีดาน คูเป้ และเอสยูวี คลาสนามธรรมของเราเป็นเพียง "พิมพ์เขียว" ที่เราจะใช้เพื่อสร้างคลาสรถเฉพาะในภายหลัง
public class Sedan extends Car {
@Override
public void gas() {
System.out.println("The sedan is accelerating!");
}
@Override
public void brake() {
System.out.println("The sedan is slowing down!");
}
}
ในหลาย ๆ ด้าน สิ่งนี้คล้ายกับที่เราพูดถึงในบทเรียนเรื่องมรดก ในกรณีนั้นเรามีCar
คลาสที่มีวิธีการไม่เป็นนามธรรม แต่วิธีแก้ปัญหาดังกล่าวมีข้อเสียหลายประการซึ่งได้รับการแก้ไขในคลาสนามธรรม ก่อนอื่นไม่สามารถสร้างอินสแตนซ์ของคลาสนามธรรมได้:
public class Main {
public static void main(String[] args) {
Car car = new Car(); // Error! The Car class is abstract!
}
}
ผู้สร้างของ Java สร้าง "คุณสมบัติ" นี้โดยตั้งใจ โปรดจำไว้อีกครั้ง: คลาสนามธรรมเป็นเพียงพิมพ์เขียวสำหรับคลาส "ปกติ" ในอนาคต คุณไม่ต้องการสำเนาพิมพ์เขียวใช่ไหม ในทำนองเดียวกัน ไม่จำเป็นต้องสร้างอินสแตนซ์ของคลาสนามธรรม :) และถ้าคลาสนั้นCar
ไม่ใช่นามธรรม เราก็สามารถสร้างอินสแตนซ์ของคลาสนั้นได้ง่ายๆ:
public class Car {
private String model;
private String color;
private int maxSpeed;
public void go() {
// ...some logic
}
public void brake() {
// ...some logic
}
}
public class Main {
public static void main(String[] args) {
Car car = new Car(); // This is okay. The car is created.
}
}
ในปัจจุบันโปรแกรมของเรามีรถยนต์ที่เข้าใจยากบางประเภท มันไม่ใช่รถบรรทุก ไม่ใช่รถแข่ง และไม่ใช่รถเก๋ง แต่ก็ไม่ชัดเจนว่ามันคืออะไร มันคือ "รถ" ที่ไม่มีอยู่จริง ตัวอย่างเดียวกันนี้ใช้กับสัตว์ได้ ลองนึกดูว่าโปรแกรมของคุณมีAnimal
วัตถุ (" แค่สัตว์ ") ไม่ทราบแน่ชัดว่าเป็นชนิดใด อยู่ในตระกูลใด หรือมีลักษณะอย่างไร มันคงจะแปลกที่จะเห็นหนึ่งในโปรแกรม ไม่มี "สัตว์" ในธรรมชาติ เฉพาะสุนัข แมว สุนัขจิ้งจอก ตัวตุ่น ฯลฯ คลาสนามธรรมช่วยเราจาก " แค่วัตถุ " พวกเขาให้สถานะพื้นฐานและพฤติกรรมแก่เรา เช่น รถยนต์ทุกคันต้องมีรุ่นสีและความเร็วสูงสุดและต้องสามารถใช้แก๊สและเบรกได้ด้วย แค่นั้นแหละ. เป็นพิมพ์เขียวนามธรรมทั่วไปที่คุณจะใช้ในภายหลังเพื่อออกแบบชั้นเรียนที่คุณต้องการ หมายเหตุ:ทั้งสองเมธอดในคลาสนามธรรมยังเป็นabstractซึ่งหมายความว่าไม่มีการนำไปใช้เลย เหตุผลก็เหมือนกัน: คลาสนามธรรมไม่ได้สร้าง "พฤติกรรมเริ่มต้น" สำหรับ "แค่รถยนต์" พวกเขาแค่ระบุว่ารถทุกคันต้องสามารถทำอะไรได้บ้าง ที่กล่าวว่า ถ้าคุณต้องการพฤติกรรมเริ่มต้น คุณสามารถใช้เมธอดในคลาสนามธรรมได้ Java ไม่ได้ห้ามสิ่งนี้:
public abstract class Car {
private String model;
private String color;
private int maxSpeed;
public void gas() {
System.out.println("Accelerating!");
}
public abstract void brake();
// Getters and setters
}
public class Sedan extends Car {
@Override
public void brake() {
System.out.println("The sedan is slowing down!");
}
}
public class Main {
public static void main(String[] args) {
Sedan sedan = new Sedan();
sedan.gas();
}
}
เอาต์พุตคอนโซล:
Accelerating!
อย่างที่คุณเห็น เราได้ใช้วิธีหนึ่งในคลาสนามธรรม แต่ไม่ใช่อีกวิธีหนึ่ง ด้วยเหตุนี้ พฤติกรรมของSedan
คลาสของเราจึงถูกแบ่งออกเป็นสองส่วน: หากเราเรียกgas()
เมธอดของมัน พฤติกรรมนั้นจะถูก "ดึงขึ้นมา" จากคลาสพาเร นต์นามธรรม Car
และเราจะนำbrake()
เมธอดไปใช้ในSedan
คลาส สะดวกและคล่องตัวสุดๆ แต่คลาสของเราตอนนี้ไม่เป็นนามธรรมแล้วใช่ไหม ? ท้ายที่สุดมันใช้จริงครึ่งหนึ่งของวิธีการ ความจริงก็คือ - และนี่คือคุณสมบัติที่สำคัญมาก - คลาสเป็นนามธรรมหากวิธีการใดวิธีหนึ่งเป็นนามธรรม. วิธีหนึ่งจากสองหรือหนึ่งในพัน — ไม่สำคัญ เรายังสามารถนำวิธีการทั้งหมดไปใช้ได้โดยไม่ทิ้งสิ่งที่เป็นนามธรรม ผลลัพธ์จะเป็นคลาสนามธรรมโดยไม่มีวิธีการนามธรรม โดยหลักการแล้วสิ่งนี้เป็นไปได้ — คอมไพเลอร์จะไม่สร้างข้อผิดพลาดใด ๆ — แต่เป็นการดีกว่าที่จะไม่ทำเช่นนี้ เนื่องจากจะทำให้คำว่านามธรรมขาดความหมายไป เพื่อนร่วมโปรแกรมของคุณจะประหลาดใจมากที่เห็นสิ่งนี้:/ กล่าวคือถ้าเมธอดถูกทำเครื่องหมายเป็นนามธรรม แต่ละคลาสที่สืบทอดมาจะต้องนำไปใช้หรือประกาศเป็นนามธรรม มิฉะนั้นคอมไพเลอร์จะส่งข้อผิดพลาด. แน่นอน แต่ละคลาสสามารถสืบทอดคลาสนามธรรมได้เพียงคลาสเดียว ดังนั้นจึงไม่มีความแตกต่างระหว่างคลาสนามธรรมและคลาสปกติในแง่ของการสืบทอด ไม่สำคัญว่าเราจะสืบทอดคลาสนามธรรมหรือคลาสปกติ — สามารถมีคลาสพาเรนต์ได้เพียงคลาสเดียวเท่านั้น
เหตุใด Java จึงไม่มีการสืบทอดหลายคลาส
เราได้กล่าวไปแล้วว่าไม่มีการสืบทอดหลายรายการใน Java แต่เราไม่ได้เจาะลึกว่าทำไม ลองทำกันตอนนี้ ความจริงก็คือ ถ้า Java มีการสืบทอดหลายรายการ คลาสลูกจะไม่สามารถตัดสินใจได้ว่าจะเลือกพฤติกรรมใด สมมติว่าเรามีสองคลาส:Toaster
และNuclearBomb
:
public class Toaster {
public void on() {
System.out.println("The toaster is on. We're toasting!");
}
public void off() {
System.out.println("The toaster is off!");
}
}
public class NuclearBomb {
public void on() {
System.out.println("Boom!");
}
}
อย่างที่คุณเห็นทั้งสองคลาสมีon()
เมธอด สำหรับเครื่องปิ้งขนมปัง วิธีการเริ่มต้นทำขนมปังปิ้ง แต่ในกรณีของระเบิดนิวเคลียร์ จะทำให้เกิดระเบิดขึ้น อ๊ะ:/ ตอนนี้ลองนึกดูว่าคุณได้ตัดสินใจ (อย่าถามฉันว่าทำไม!) เพื่อสร้างบางสิ่งระหว่างนั้น นี่คือชั้นเรียนของคุณ: MysteriousDevice
! รหัสนี้ใช้ไม่ได้แน่นอน เรานำเสนอเป็นเพียงตัวอย่างของ "สิ่งที่อาจเป็น":
public class MysteriousDevice extends Toster, NuclearBomb {
public static void main(String[] args) {
MysteriousDevice mysteriousDevice = new MysteriousDevice();
mysteriousDevice.on(); // And what should happen here? Will we get toast or a nuclear apocalypse?
}
}
มาดูกันว่ามีอะไรบ้าง อุปกรณ์ลึกลับนี้มาจากทั้ง Toaster และ NuclearBomb ในเวลาเดียวกัน ทั้งสองมีon()
วิธี ด้วยเหตุนี้ จึงไม่ชัดเจนว่าควรดำเนินการใดหากเราเรียกon()
ใช้MysteriousDevice
ออบเจกต์ วัตถุจะไม่เข้าใจ และเหนือสิ่งอื่นใด NuclearBomb ไม่มีoff()
วิธีการ ดังนั้นหากเราเดาไม่ถูก ก็จะไม่สามารถปิดอุปกรณ์ได้ 
GO TO FULL VERSION