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

- ชั้นเรียนธรรมดา
- คลาสซ้อนแบบสแตติกที่ประกาศในคลาสภายนอกหรือบรรพบุรุษของมัน
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 ประเภท: คลาสภายในแบบธรรมดา คลาสแบบโลคอล และคลาสแบบนิรนาม 
คลาสภายในที่ไม่ระบุชื่อ
คลาสภายในที่ไม่ระบุชื่อไม่สามารถสืบทอดคลาสอื่นได้ ไม่มีคลาสอื่นใดที่สามารถสืบทอดคลาสที่ไม่ระบุชื่อได้ มันไม่ง่ายไปกว่านี้แล้ว! :)ชั้นเรียนในท้องถิ่น
คลาสท้องถิ่น (ในกรณีที่คุณลืม) จะถูกประกาศภายในบล็อครหัสของคลาสอื่น บ่อยครั้งสิ่งนี้เกิดขึ้นภายในบางวิธีของคลาสภายนอก ตามเหตุผลแล้ว เฉพาะคลาสโลคัลอื่นๆ ภายในเมธอดเดียวกัน (หรือโค้ดบล็อก) เท่านั้นที่สามารถสืบทอดคลาสโลคัลได้ นี่คือตัวอย่าง:
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
คลาสท้องถิ่น หากเราต้องการให้เป็นตัวแทนสองรายการที่แตกต่างกัน เช่น หมายเลขโทรศัพท์มือถือและหมายเลขโทรศัพท์พื้นฐาน เราสามารถทำได้ภายในวิธีการเดียวกันเท่านั้น เหตุผลนั้นง่ายมาก: ขอบเขตของคลาสโลคัลถูกจำกัดไว้ที่เมธอด (บล็อกโค้ด) ที่มันถูกประกาศ ด้วยเหตุนี้ เราจึงไม่สามารถใช้ภายนอกได้ (รวมถึงการสืบทอดคลาส) อย่างไรก็ตาม ความเป็นไปได้สำหรับการสืบทอดภายในคลาสท้องถิ่นนั้นกว้างกว่ามาก! คลาสโลคัลสามารถสืบทอด:
- ชั้นเรียนธรรมดา
- ชั้นในที่จัดอยู่ในชั้นเดียวกับชั้นในท้องที่หรือในบรรพบุรุษคนใดคนหนึ่ง
- คลาสโลคัลอื่นที่ประกาศในเมธอดเดียวกัน (โค้ดบล็อก)
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);
}
}
วุ้ย บทเรียนนี้ค่อนข้างยาว :) แต่คุณได้เรียนรู้มากมาย! ตอนนี้ได้เวลาแก้ไขงานบางอย่างแล้ว! :)
GO TO FULL VERSION