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