สวัสดี! ในบทเรียนที่ผ่านมา เราได้พบกับอินเทอร์เฟซและเข้าใจว่ามีไว้เพื่ออะไร หัวข้อของวันนี้จะสะท้อนถึงหัวข้อก่อนหน้า พูดคุยเกี่ยวกับคลาสนามธรรมใน Java
ทำไมชั้นเรียนถึงเรียกว่า 'นามธรรม'
คุณคงจำได้ว่า 'สิ่งที่เป็นนามธรรม' คืออะไร เราได้อธิบายไปแล้ว :) ถ้าลืมก็อย่ากลัว ข้อควรจำ: เป็นหลักการของ OOPที่กล่าวว่าเมื่อออกแบบคลาสและสร้างออบเจกต์ เราควรระบุเฉพาะคุณสมบัติหลักของเอนทิตีและละทิ้งคุณสมบัติรอง ตัวอย่างเช่น หากเรากำลังออกแบบSchoolTeacher
คลาส เราแทบจะไม่ต้องการคุณสมบัติ ' ความสูง ' เลย แท้จริงแล้วคุณสมบัตินี้ไม่เกี่ยวข้องกับครู แต่ถ้าเรากำลังสร้างBasketballPlayer
ชั้นเรียน การเติบโตจะเป็นคุณลักษณะที่สำคัญ ดังนั้นจงฟัง คลาสนามธรรมเป็นนามธรรมอย่างที่เป็นมา — เป็น 'ช่องว่าง' ที่ยังไม่เสร็จสำหรับกลุ่มชั้นเรียนในอนาคต ไม่สามารถใช้ช่องว่างได้ตามที่เป็นอยู่ มัน 'ดิบ' เกินไป แต่มันอธิบายสถานะบางอย่างและพฤติกรรมทั่วไปที่จะถูกครอบงำโดยคลาสในอนาคตที่สืบทอดคลาสนามธรรม
ตัวอย่างของคลาส Java ที่เป็นนามธรรม
พิจารณาตัวอย่างง่ายๆ กับรถยนต์:
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 gas() {
// Some logic
}
public void brake() {
// Some logic
}
}
public class Main {
public static void main(String[] args) {
Car car = new Car(); // Everything is fine. A 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("Gas!");
}
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();
}
}
เอาต์พุตของคอนโซล: “Gas!” อย่างที่คุณเห็น เราได้ปรับใช้เมธอดแรกในคลาสนามธรรม ไม่ใช่วิธีที่สอง ด้วยเหตุนี้Sedan
พฤติกรรมของคลาสของเราจึงแบ่งออกเป็นสองส่วน: หากคุณเรียกใช้gas()
เมธอด 'เพิ่มขึ้น' จนถึงCar
คลาสแม่ที่เป็นนามธรรม แต่เราแทนที่เมธอดbrake()
ในSedan
คลาส สิ่งนี้กลายเป็นสิ่งที่สะดวกและยืดหยุ่นมาก แต่ตอนนี้คลาสของเราไม่เป็นนามธรรมแล้ว ท้ายที่สุด เมธอดครึ่งหนึ่งถูกนำไปใช้ นี่ เป็นคุณสมบัติที่สำคัญมาก - คลาสเป็นนามธรรมหากอย่างน้อยหนึ่งในวิธีการนั้นเป็นนามธรรม. วิธีใดวิธีหนึ่งจากสองวิธี หรืออย่างน้อยหนึ่งวิธีจากทั้งหมดพันวิธี — มันไม่สร้างความแตกต่าง เรายังสามารถนำวิธีการทั้งหมดไปใช้โดยไม่ปล่อยให้เป็นนามธรรม จากนั้นจะเป็นคลาสนามธรรมโดยไม่มีวิธีการนามธรรม โดยหลักการแล้ว สิ่งนี้เป็นไปได้ และคอมไพเลอร์จะไม่สร้างข้อผิดพลาด แต่ควรหลีกเลี่ยงจะดีกว่า: คำว่านามธรรมจะสูญเสียความหมายไป และเพื่อนโปรแกรมเมอร์ของคุณจะประหลาดใจมาก :/ ในขณะเดียวกันหากวิธีการถูกทำเครื่องหมายไว้ ด้วยคำว่านามธรรม แต่ละคลาสย่อยจะต้องดำเนินการหรือประกาศเป็นนามธรรม มิฉะนั้น คอมไพเลอร์จะสร้างข้อผิดพลาด แน่นอน แต่ละคลาสสามารถสืบทอดคลาสนามธรรมได้เพียงคลาสเดียว ดังนั้นในแง่ของการสืบทอด ไม่มีความแตกต่างระหว่างคลาสนามธรรมและคลาสธรรมดา ไม่สำคัญว่าเราจะสืบทอดคลาสนามธรรมหรือคลาสธรรมดา สามารถมีคลาสพาเรนต์ได้เพียงคลาสเดียวเท่านั้น
เหตุใด Java จึงไม่มีการสืบทอดหลายคลาส
เราได้กล่าวไปแล้วว่า Java ไม่มีการสืบทอดหลายรายการ แต่เรายังไม่ได้สำรวจว่าทำไม ลองทำกันตอนนี้ ความจริงก็คือ หาก Java มีการสืบทอดหลายรายการ คลาสลูกจะไม่สามารถตัดสินใจได้ว่าควรเลือกพฤติกรรมใด สมมติว่าเรามีสองคลาส —Toaster
และNuclearBomb
:
public class Toaster {
public void on() {
System.out.println("The toaster is on. Toast is being prepared!");
}
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 Toaster, NuclearBomb {
public static void main(String[] args) {
MysteriousDevice mysteriousDevice = new MysteriousDevice();
mysteriousDevice.on(); // So what should happen here? Do we get toast or a nuclear apocalypse?
}
}
เรามาดูกันดีกว่าว่ามีอะไรบ้าง อุปกรณ์ลึกลับนี้สืบทอด Toaster และ NuclearBomb พร้อมกัน ทั้งสองมีon()
วิธีการ ดังนั้น หากเราเรียกon()
ใช้เมธอด ก็ไม่ชัดเจนว่าควรเรียกใช้เมธอดใดบนMysteriousDevice
ออบเจกต์ ไม่มีทางที่วัตถุจะรู้ได้เลย และเหนือสิ่งอื่นใด: NuclearBomb ไม่มีoff()
วิธีการ ดังนั้นหากเราเดาไม่ถูก ก็จะไม่สามารถปิดการใช้งานอุปกรณ์ได้ เป็นเพราะ 'ความสับสน' ที่วัตถุไม่รู้ว่าจะแสดงพฤติกรรมใด ผู้สร้าง Java จึงละทิ้งการสืบทอดหลายรายการ อย่างไรก็ตาม คุณจะจำได้ว่าคลาส Java สามารถใช้หลายอินเทอร์เฟซได้ อย่างไรก็ตาม ในการศึกษาของคุณ คุณได้พบคลาสนามธรรมอย่างน้อยหนึ่งคลาสแล้ว!
public abstract class Calendar implements Serializable, Cloneable, Comparable<Calendar>
มันเป็นเพื่อนเก่าของคุณCalendar
ชั้นเรียน เป็นนามธรรมและมีลูกหลายคน หนึ่งในนั้นGregorianCalendar
คือ คุณใช้มันไปแล้วในบทเรียนเกี่ยวกับวันที่ :) ทุกอย่างชัดเจนเพียงพอ มีเพียงคำถามเดียว: อะไรคือความแตกต่างพื้นฐานระหว่างคลาสนามธรรมและอินเทอร์เฟซ เหตุใดพวกเขาจึงเพิ่มทั้งสองอย่างลงใน Java แทนที่จะจำกัดเพียงภาษาเดียว ท้ายที่สุดนั่นก็เพียงพอแล้ว เราจะพูดถึงเรื่องนี้ในบทเรียนหน้า ! จนกระทั่ง :)
GO TO FULL VERSION