สวัสดี! พูดคุยเกี่ยวกับคลาสนามธรรมใน 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()
วิธีการ ดังนั้นหากเราเดาไม่ถูก ก็จะไม่สามารถปิดอุปกรณ์ได้ "ความเข้าใจผิด" นี้เมื่อไม่มีความชัดเจนว่าควรดำเนินการพฤติกรรมใด เป็นเหตุผลที่แน่ชัดว่าทำไมผู้สร้างของ Java ปฏิเสธการสืบทอดหลายรายการ คุณจะได้เรียนรู้ว่าคลาส Java สามารถใช้อินเทอร์เฟซได้หลากหลาย
GO TO FULL VERSION