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