JavaRush /จาวาบล็อก /Random-TH /การสืบทอดคลาสที่ซ้อนกัน

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

เผยแพร่ในกลุ่ม
สวัสดี! วันนี้เราจะมาดูการทำงานของกลไกสำคัญ - การสืบทอดในคลาสที่ซ้อนกัน ฉันไม่รู้ว่าคุณเคยคิดบ้างไหมว่าคุณจะทำอะไรเมื่อคุณต้องการสืบทอดคลาสที่ซ้อนกันจากคลาสอื่น ถ้าไม่เชื่อฉัน: สถานการณ์นี้อาจสร้างความสับสนได้เนื่องจากมีความแตกต่างมากมายที่นี่:
  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 {


       }

       //...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);
           }
       }

       //...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);
           }
       }

       //...code валидации номера
   }
}
ด้วยการเชื่อมต่อผ่านการสืบทอด คลาสท้องถิ่นภายในคลาสลูกหลาน "ดู" คลาสภายในภายในบรรพบุรุษ และสุดท้ายก็มาต่อที่กลุ่มสุดท้ายกันเลย :)

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

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

   private String model;
   private int mawWeight;

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

   public void start() {
       System.out.println("Go!");
   }

   class Seat {

       public void up() {

           System.out.println("Сидение поднято выше!");
       }

       public void down() {

           System.out.println("Сидение опущено ниже!");
       }
   }

   class SportSeat extends Seat {

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

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


   class SportSeat extends Seat {

       public void up() {

           System.out.println("Сидение поднято выше!");
       }

       public void down() {

           System.out.println("Сидение опущено ниже!");
       }
   }
}
สิ่งนี้ก็เป็นไปได้เช่นกัน คลาสภายในของเด็ก ( SportBicycle.SportSeat) “มองเห็น” คลาสภายในของบรรพบุรุษและสามารถสืบทอดจากคลาสเหล่านั้นได้ การสืบทอดจากคลาสภายในมีคุณลักษณะที่สำคัญอย่างหนึ่ง! ในสองตัวอย่างก่อนหน้านี้เราSportSeatมีข้อมูลภายใน แต่จะเกิดอะไรขึ้นถ้าเราตัดสินใจที่จะทำให้SportSeatเป็นคลาสสาธารณะปกติ ซึ่งสืบทอดมาจากคลาสภายในSeatด้วย
//ошибка! No inclosing instance of  type 'Bicycle' is in scope
class SportSeat extends Bicycle.Seat {

   public SportSeat() {

   }

   public void up() {

       System.out.println("Сидение поднято выше!");
   }

   public void down() {

       System.out.println("Сидение опущено ниже!");
   }
}
เราได้รับข้อผิดพลาด! คุณเดาได้ไหมว่ามันเกี่ยวข้องกับอะไร? :) มันง่ายมาก เมื่อเราพูดถึงคลาสภายในBicycle.Seatเราได้กล่าวถึงว่า Constructor ของคลาสภายในส่งผ่านการอ้างอิงไปยังอ็อบเจ็กต์ของคลาสภายนอกโดยปริยาย ดังนั้น หากไม่มีการสร้างวัตถุBicycleคุณจะไม่สามารถสร้างวัตถุSeatได้ แล้วการสร้างสรรค์ล่ะSportSeat? ไม่มีกลไกในตัวที่เหมือนกันสำหรับการส่งผ่านการอ้างอิงไปยังอ็อบเจ็กต์คลาสภายนอกในตัวสร้างเช่นเดียวกับSeatใน อย่างไรก็ตาม หากไม่มี object เราก็ ไม่สามารถสร้าง object ได้Bicycleเช่นเดียวกับในกรณีที่มี ดังนั้นเราจึงเหลือเพียงสิ่งเดียวที่ต้องทำ - ส่ง การอ้างอิงถึงวัตถุ ไปยังตัวสร้าง อย่างชัดเจน ต่อไปนี้เป็นวิธีดำเนินการ: SeatSportSeatSportSeatBicycle
class SportSeat extends Bicycle.Seat {

   public SportSeat(Bicycle bicycle) {

       bicycle.super();
   }

   public void up() {

       System.out.println("Сидение поднято выше!");
   }

   public void down() {

       System.out.println("Сидение опущено ниже!");
   }
}
สำหรับสิ่งนี้ เราใช้คำพิเศษsuper(); ตอนนี้ หากเราต้องการสร้างวัตถุSportSeatไม่มีอะไรจะหยุดเราไม่ให้ทำสิ่งนี้:
public class Main {

   public static void main(String[] args) {

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

   }
}
วุ้ย การบรรยายกลายเป็นเรื่องใหญ่มาก :) แต่คุณได้เรียนรู้สิ่งใหม่ ๆ มากมาย! ตอนนี้เป็นเวลาที่จะแก้ไขปัญหาบางอย่างแล้ว! :)
ความคิดเห็น
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION