สวัสดี! พูดคุยเกี่ยวกับคลาสนามธรรมใน Javaตัวอย่างที่เป็นรูปธรรมของคลาสนามธรรมใน Java - 1

ทำไมชั้นเรียนถึงเรียกว่า "นามธรรม"?

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