โค้ดยิม/จาวาบล็อก/สุ่ม/ตัวอย่างการสืบทอดคลาสที่ซ้อนกัน
John Squirrels
ระดับ
San Francisco

ตัวอย่างการสืบทอดคลาสที่ซ้อนกัน

เผยแพร่ในกลุ่ม
สวัสดี! วันนี้เราจะพิจารณากลไกสำคัญ: การสืบทอดในคลาสที่ซ้อนกัน คุณเคยคิดไหมว่าคุณจะทำอย่างไรถ้าคุณต้องการให้คลาสที่ซ้อนกันสืบทอดคลาสอื่น ถ้าไม่เชื่อฉัน: สถานการณ์นี้อาจทำให้สับสนได้เพราะมีความแตกต่างมากมาย
  1. เรากำลังสร้างคลาสที่ซ้อนกันสืบทอดบางคลาสหรือไม่? หรือเรากำลังทำให้บางคลาสสืบทอดคลาสที่ซ้อนกัน?
  2. คลาสลูก/พาเรนต์เป็นคลาสสาธารณะทั่วไป หรือเป็นคลาสซ้อนกัน
  3. สุดท้าย เราใช้คลาสซ้อนกันประเภทใดในสถานการณ์เหล่านี้
มีคำตอบที่เป็นไปได้มากมายสำหรับคำถามเหล่านี้ หัวของคุณจะหมุน :) อย่างที่คุณทราบ เราสามารถแก้ปัญหาที่ซับซ้อนได้โดยการแบ่งออกเป็นส่วนที่ง่ายกว่า มาทำกันเถอะ ให้เราพิจารณาแต่ละกลุ่มของคลาสที่ซ้อนกันจากสองมุมมอง: ใครสามารถสืบทอดคลาสที่ซ้อนกันแต่ละประเภทได้ และใครที่สามารถสืบทอดได้ เริ่มจากคลาสที่ซ้อนกันแบบคงที่

คลาสที่ซ้อนกันแบบคงที่

ตัวอย่างการสืบทอดคลาสที่ซ้อนกัน - 2กฎการสืบทอดของพวกเขานั้นง่ายที่สุด ที่นี่คุณสามารถทำทุกสิ่งที่คุณต้องการ คลาสที่ซ้อนกันแบบคงที่สามารถสืบทอด:
  • ชั้นเรียนธรรมดา
  • คลาสซ้อนแบบสแตติกที่ประกาศในคลาสภายนอกหรือบรรพบุรุษของมัน
จำตัวอย่างจากบทเรียนของเราเกี่ยวกับคลาสที่ซ้อนกันแบบสแตติก
public class Boeing737 {

   private int manufactureYear;
   private static int maxPassengersCount = 300;

   public Boeing737(int manufactureYear) {
       this.manufactureYear = manufactureYear;
   }

   public int getManufactureYear() {
       return manufactureYear;
   }

   public static class Drawing {

       public static int getMaxPassengersCount() {

           return maxPassengersCount;
       }
   }
}
มาลองเปลี่ยนรหัสและสร้างDrawingคลาสซ้อนแบบสแตติกและลูกหลานของมันBoeing737Drawing
public class Boeing737 {

   private int manufactureYear;
   private static int maxPassengersCount = 300;

   public Boeing737(int manufactureYear) {
       this.manufactureYear = manufactureYear;
   }

   public int getManufactureYear() {
       return manufactureYear;
   }

   public static class Drawing {

   }

   public static class Boeing737Drawing extends Drawing {

       public static int getMaxPassengersCount() {

           return maxPassengersCount;
       }
   }
}
อย่างที่คุณเห็นไม่มีปัญหา เรายังสามารถดึงDrawingคลาสออกมาและทำให้เป็นคลาสสาธารณะทั่วไปแทนที่จะเป็นคลาสที่ซ้อนกันแบบสแตติก — จะไม่มีอะไรเปลี่ยนแปลง
public class Drawing {

}

public class Boeing737 {

   private int manufactureYear;
   private static int maxPassengersCount = 300;

   public Boeing737(int manufactureYear) {
       this.manufactureYear = manufactureYear;
   }

   public int getManufactureYear() {
       return manufactureYear;
   }

   public static class Boeing737Drawing extends Drawing {

       public static int getMaxPassengersCount() {

           return maxPassengersCount;
       }
   }
}
เราเข้าใจสิ่งนี้ แต่คลาสใดที่สามารถสืบทอดคลาสซ้อนแบบคงที่ได้ ในทางปฏิบัติ! ซ้อน/ไม่ซ้อน คงที่/ไม่คงที่ — ไม่สำคัญ ที่นี่เราทำให้Boeing737DrawingคลาสภายในสืบทอดDrawingคลาสซ้อนแบบคงที่:
public class Boeing737 {

   private int manufactureYear;
   private static int maxPassengersCount = 300;

   public Boeing737(int manufactureYear) {
       this.manufactureYear = manufactureYear;
   }

   public int getManufactureYear() {
       return manufactureYear;
   }

   public static class Drawing {

   }

   public class Boeing737Drawing extends Drawing {

       public int getMaxPassengersCount() {

           return maxPassengersCount;
       }
   }
}
คุณสามารถสร้างอินสแตนซ์Boeing737Drawingเช่นนี้:
public class Main {

   public static void main(String[] args) {

      Boeing737 boeing737 = new Boeing737(1990);
      Boeing737.Boeing737Drawing drawing = boeing737.new Boeing737Drawing();
      System.out.println(drawing.getMaxPassengersCount());

   }

}
แม้ว่าBoeing737Drawingคลาสของเราจะสืบทอดคลาสสแตติก แต่ก็ไม่สแตติก! เป็นผลให้จะต้องมีอินสแตนซ์ของคลาสภายนอกเสมอ เราสามารถลบBoeing737DrawingคลาสออกจากBoeing737คลาสและทำให้เป็นคลาสสาธารณะอย่างง่าย ไม่มีอะไรเปลี่ยนแปลง มันยังคงสามารถสืบทอดDrawingคลาสที่ซ้อนกันแบบคงที่ได้
public class Boeing737 {

   private int manufactureYear;
   public static int maxPassengersCount = 300;

   public Boeing737(int manufactureYear) {
       this.manufactureYear = manufactureYear;
   }

   public int getManufactureYear() {
       return manufactureYear;
   }

   public static class Drawing {

   }
}

public class Boeing737Drawing extends Boeing737.Drawing {

   public int getMaxPassengersCount() {

       return Boeing737.maxPassengersCount;

}
จุดสำคัญเพียงอย่างเดียวคือในกรณีนี้เราต้องทำให้maxPassengersCountตัวแปรสแตติกเป็นแบบสาธารณะ หากยังคงเป็นส่วนตัว คลาสสาธารณะทั่วไปจะไม่สามารถเข้าถึงได้ เราพบคลาสคงที่แล้ว! :) ตอนนี้เรามาเรียนชั้นในกันเถอะ มี 3 ประเภท: คลาสภายในแบบธรรมดา คลาสแบบโลคอล และคลาสแบบนิรนาม ตัวอย่างการสืบทอดคลาสซ้อน - 3เรามาเปลี่ยนจากง่ายไปซับซ้อนกันอีกครั้ง :)

คลาสภายในที่ไม่ระบุชื่อ

คลาสภายในที่ไม่ระบุชื่อไม่สามารถสืบทอดคลาสอื่นได้ ไม่มีคลาสอื่นใดที่สามารถสืบทอดคลาสที่ไม่ระบุชื่อได้ มันไม่ง่ายไปกว่านี้แล้ว! :)

ชั้นเรียนในท้องถิ่น

คลาสท้องถิ่น (ในกรณีที่คุณลืม) จะถูกประกาศภายในบล็อครหัสของคลาสอื่น บ่อยครั้งสิ่งนี้เกิดขึ้นภายในบางวิธีของคลาสภายนอก ตามเหตุผลแล้ว เฉพาะคลาสโลคัลอื่นๆ ภายในเมธอดเดียวกัน (หรือโค้ดบล็อก) เท่านั้นที่สามารถสืบทอดคลาสโลคัลได้ นี่คือตัวอย่าง:
public class PhoneNumberValidator {

   public void validatePhoneNumber(final String number) {

       class PhoneNumber {

           private String phoneNumber;

           public PhoneNumber() {
               this.phoneNumber = number;
           }

           public String getPhoneNumber() {
               return phoneNumber;
           }

           public void setPhoneNumber(String phoneNumber) {
               this.phoneNumber = phoneNumber;
           }
       }

       class CellPhoneNumber extends PhoneNumber {

       }

       class LandlinePhoneNumber extends PhoneNumber {


       }

       // ...number validation code
   }
}
นี่คือรหัสจากบทเรียนของเราในชั้นเรียนในท้องถิ่น คลาสตัวตรวจสอบหมายเลขของเรามีPhoneNumberคลาสท้องถิ่น หากเราต้องการให้เป็นตัวแทนสองรายการที่แตกต่างกัน เช่น หมายเลขโทรศัพท์มือถือและหมายเลขโทรศัพท์พื้นฐาน เราสามารถทำได้ภายในวิธีการเดียวกันเท่านั้น เหตุผลนั้นง่ายมาก: ขอบเขตของคลาสโลคัลถูกจำกัดไว้ที่เมธอด (บล็อกโค้ด) ที่มันถูกประกาศ ด้วยเหตุนี้ เราจึงไม่สามารถใช้ภายนอกได้ (รวมถึงการสืบทอดคลาส) อย่างไรก็ตาม ความเป็นไปได้สำหรับการสืบทอดภายในคลาสท้องถิ่นนั้นกว้างกว่ามาก! คลาสโลคัลสามารถสืบทอด:
  1. ชั้นเรียนธรรมดา
  2. ชั้นในที่จัดอยู่ในชั้นเดียวกับชั้นในท้องที่หรือในบรรพบุรุษคนใดคนหนึ่ง
  3. คลาสโลคัลอื่นที่ประกาศในเมธอดเดียวกัน (โค้ดบล็อก)
จุดที่หนึ่งและสามดูชัดเจน แต่จุดที่สองค่อนข้างสับสน :/ ลองดูสองตัวอย่าง ตัวอย่างที่ 1 — "การทำให้คลาสโลคัลสืบทอดคลาสภายในที่ประกาศในคลาสเดียวกับคลาสโลคัล":
public class PhoneNumberValidator {

   class PhoneNumber {

       private String phoneNumber;

       public PhoneNumber(String phoneNumber) {
           this.phoneNumber = phoneNumber;
       }

       public String getPhoneNumber() {
           return phoneNumber;
       }

       public void setPhoneNumber(String phoneNumber) {
           this.phoneNumber = phoneNumber;
       }
   }

   public void validatePhoneNumber(final String number) {

       class CellPhoneNumber extends PhoneNumber {

           public CellPhoneNumber(String phoneNumber) {
               super(number);
           }
       }

       class LandlinePhoneNumber extends PhoneNumber {

           public LandlinePhoneNumber(String phoneNumber) {
               super(number);
           }
       }

       // ...number validation code
   }
}
ที่นี่เราลบPhoneNumberคลาสออกจากvalidatePhoneNumber()เมธอดและทำให้เป็นคลาสภายในแทนที่จะเป็นคลาสโลคอล สิ่งนี้ไม่ได้หยุดเราจากการทำให้คลาสโลคัล 2 คลาสของเราสืบทอดมา ตัวอย่างที่ 2 — "... หรือในบรรพบุรุษของคลาสนี้" ตอนนี้มันน่าสนใจมากขึ้นแล้ว เราสามารถก้าวให้PhoneNumberสูงขึ้นไปอีกในสายการสืบทอด เรามาประกาศAbstractPhoneNumberValidatorคลาสนามธรรมซึ่งจะกลายเป็นบรรพบุรุษของPhoneNumberValidatorคลาสของเรา:
public abstract class AbstractPhoneNumberValidator {

   class PhoneNumber {

       private String phoneNumber;

       public PhoneNumber(String phoneNumber) {
           this.phoneNumber = phoneNumber;
       }

       public String getPhoneNumber() {
           return phoneNumber;
       }

       public void setPhoneNumber(String phoneNumber) {
           this.phoneNumber = phoneNumber;
       }
   }

}
อย่างที่คุณเห็น เราไม่ได้แค่ประกาศเท่านั้น แต่เรายังย้ายคนPhoneNumberชั้นในเข้าไป ด้วย อย่างไรก็ตาม ในคลาสที่สืบทอดมาPhoneNumberValidatorคลาสโลคัลที่ประกาศในเมธอดสามารถสืบทอดได้PhoneNumberโดยไม่มีปัญหา!
public class PhoneNumberValidator extends AbstractPhoneNumberValidator {

   public void validatePhoneNumber(final String number) {

       class CellPhoneNumber extends PhoneNumber {

           public CellPhoneNumber(String phoneNumber) {
               super(number);
           }
       }

       class LandlinePhoneNumber extends PhoneNumber {

           public LandlinePhoneNumber(String phoneNumber) {
               super(number);
           }
       }

       // ...number validation code
   }
}
เนื่องจากความสัมพันธ์ในการสืบทอด คลาสท้องถิ่นภายในคลาสลูกหลาน "เห็น" คลาสภายในภายในบรรพบุรุษ และสุดท้าย เรามาต่อที่กลุ่มสุดท้ายกันเถอะ :)

ชั้นเรียนภายใน

คลาสภายในที่ประกาศในคลาสภายนอกเดียวกัน (หรือในลูกหลานของมัน) สามารถสืบทอดคลาสภายในอื่นได้ ลองสำรวจสิ่งนี้โดยใช้ตัวอย่างของเรากับจักรยานจากบทเรียนในชั้นเรียนภายใน
public class Bicycle {

   private String model;
   private int maxWeight;

   public Bicycle(String model, int maxWeight) {
       this.model = model;
       this.maxWeight = maxWeight;
   }

   public void start() {
       System.out.println("Let's go!");
   }

   class Seat {

       public void up() {

           System.out.println("Seat up!");
       }

       public void down() {

           System.out.println("Seat down!");
       }
   }

   class SportSeat extends Seat {

       // ...methods
   }
}
ที่นี่เราประกาศSeatชั้นในภายในBicycleชั้นเรียน เบาะนั่งแบบพิเศษSportSeatที่สืบทอดมา แต่เราสามารถสร้างประเภท "จักรยานแข่ง" แยกต่างหากและแยกประเภทได้:
public class SportBicycle extends Bicycle {

   public SportBicycle(String model, int maxWeight) {
       super(model, maxWeight);
   }


   class SportSeat extends Seat {

       public void up() {

           System.out.println("Seat up!");
       }

       public void down() {

           System.out.println("Seat down!");
       }
   }
}
นี่เป็นตัวเลือก ชนชั้นในของลูกหลาน ( SportBicycle.SportSeat) "เห็น" ชนชั้นในของบรรพบุรุษและสามารถสืบทอดได้ การสืบทอดคลาสภายในมีคุณสมบัติที่สำคัญอย่างหนึ่ง! ในสองตัวอย่างก่อนหน้านี้SportSeatชั้นเรียนของเราเป็นชั้นเรียนภายใน แต่ถ้าเราตัดสินใจที่จะสร้างSportSeatคลาสสาธารณะธรรมดาที่สืบทอดSeatคลาสภายในพร้อมกันล่ะ
// Error! No enclosing instance of type 'Bicycle' is in scope
class SportSeat extends Bicycle.Seat {

   public SportSeat() {

   }

   public void up() {

       System.out.println("Seat up!");
   }

   public void down() {

       System.out.println("Seat down!");
   }
}
เรามีข้อผิดพลาด! คุณเดาได้ไหมว่าทำไม :) มันตรงไปตรงมาทั้งหมด เมื่อเราพูดถึงBicycle.Seatคลาสภายใน เราได้กล่าวถึงการอ้างอิงถึงอินสแตนซ์ของคลาสภายนอกจะถูกส่งผ่านไปยังตัวสร้างของคลาสภายในโดยปริยาย ซึ่งหมายความว่าคุณไม่สามารถสร้างSeatวัตถุโดยไม่สร้างBicycleวัตถุ แต่สิ่งที่เกี่ยวกับการสร้างSportSeat? ซึ่งแตกต่างจากSeatมันไม่มีกลไกในตัวสำหรับการส่งตัวสร้างการอ้างอิงไปยังอินสแตนซ์ของคลาสภายนอกโดยปริยาย จนกระทั่ง หากไม่มีBicycleวัตถุ เราไม่สามารถสร้างSportSeatวัตถุได้ เช่นเดียวกับในกรณีSeatของ ดังนั้น มีเพียงสิ่งเดียวเท่านั้นที่เราต้องทำ — ส่งSportSeatการอ้างอิง ถึง Bicycleวัตถุ ให้กับตัวสร้างอย่างชัดแจ้ง นี่คือวิธีการ:
class SportSeat extends Bicycle.Seat {

   public SportSeat(Bicycle bicycle) {

       bicycle.super();
   }

   public void up() {

       System.out.println("Seat up!");
   }

   public void down() {

       System.out.println("Seat down!");
   }
}
เราเรียกตัวสร้าง superclass โดยใช้super(); Now หากเราต้องการสร้างSportSeatวัตถุ ไม่มีอะไรจะหยุดเราไม่ให้ทำสิ่งนี้:
public class Main {

   public static void main(String[] args) {

       Bicycle bicycle = new Bicycle("Peugeot", 120);
       SportSeat peugeotSportSeat = new SportSeat(bicycle);

   }
}
วุ้ย บทเรียนนี้ค่อนข้างยาว :) แต่คุณได้เรียนรู้มากมาย! ตอนนี้ได้เวลาแก้ไขงานบางอย่างแล้ว! :)
ความคิดเห็น
  • เป็นที่นิยม
  • ใหม่
  • เก่า
คุณต้องลงชื่อเข้าใช้เพื่อแสดงความคิดเห็น
หน้านี้ยังไม่มีความคิดเห็นใด ๆ