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

คลาสภายในที่ซ้อนกันหรือคลาสภายในใน Java

เผยแพร่ในกลุ่ม
สวัสดี! วันนี้เราจะเริ่มดูหัวข้อสำคัญ - วิธีการทำงานของคลาสที่ซ้อนกันใน Java ในภาษาอังกฤษเรียกว่า Nested Class Java อนุญาตให้คุณสร้างคลาสบางคลาสภายในคลาสอื่น:
class OuterClass {
    ...
    static class StaticNestedClass {
        ...
    }
    class InnerClass {
        ...
    }
}
มันคือคลาสเหล่านี้ที่เรียกว่าซ้อนกัน แบ่งออกเป็น 2 ประเภท:
  1. คลาสที่ซ้อนกันแบบไม่คงที่ - คลาสที่ซ้อนกันแบบไม่คงที่ เรียกอีกอย่างว่าคลาสภายในในอีกทางหนึ่ง
  2. คลาสที่ซ้อนกันแบบคงที่ - คลาสที่ซ้อนกันแบบคงที่
ในทางกลับกัน คลาสภายในจะมีประเภทย่อยพิเศษสองประเภท นอกจากความจริงที่ว่าคลาสภายในสามารถเป็นเพียงคลาสภายในได้ ยังสามารถเป็น:
  • ชั้นเรียนท้องถิ่น
  • ชั้นเรียนที่ไม่ระบุชื่อ
ยากนิดหน่อย? :) ไม่เป็นไร นี่คือแผนภาพเพื่อความชัดเจน กลับมาใหม่ในระหว่างการบรรยายหากคุณรู้สึกสับสนกะทันหัน! คลาสภายในที่ซ้อนกัน - 2ในการบรรยายวันนี้ เราจะพูดถึงคลาสภายใน - คลาสภายใน (คลาสเหล่านี้ไม่ใช่คลาสที่ซ้อนกันแบบคงที่ และคลาสที่ซ้อนกันแบบไม่คงที่) มีการเน้นเป็นพิเศษในแผนภาพทั่วไปเพื่อที่คุณจะได้ไม่หลงทาง :) เริ่มจากคำถามที่ชัดเจน: เหตุใดคลาสเหล่านี้จึงเรียกว่า "ภายใน" คำตอบนั้นค่อนข้างง่าย: เพราะพวกมันถูกสร้างขึ้นในคลาสอื่น นี่คือตัวอย่าง:
public class Bicycle {

   private String model;
   private int weight;

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

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

   public class HandleBar {

       public void right() {
           System.out.println("Steering wheel to the right!");
       }

       public void left() {

           System.out.println("Steering wheel to the left!");
       }
   }

   public class Seat {

       public void up() {

           System.out.println("The seat is up!");
       }

       public void down() {

           System.out.println("The seat is down!");
       }
   }
}
ที่นี่เรามีคลาสBicycle-จักรยาน มี 2 ​​ฟิลด์และ 1 วิธี - start(). คลาสภายในที่ซ้อนกัน - 3ความแตกต่างจากคลาสปกติคือมีสองคลาสซึ่งมีรหัสเขียนอยู่ข้างในBicycleได้แก่ คลาสHandleBar(พวงมาลัย) และSeat(ที่นั่ง) เหล่านี้เป็นคลาสที่ครบถ้วน: อย่างที่คุณเห็นแต่ละคลาสมีวิธีการของตัวเอง ณ จุดนี้ คุณอาจมีคำถาม: ทำไมเราถึงจัดคลาสหนึ่งไว้ข้างในอีกคลาสหนึ่ง? ทำไมต้องทำให้พวกเขาเป็นภายใน? โอเค สมมติว่าเราต้องการคลาสแยกสำหรับพวงมาลัยและที่นั่งในโปรแกรม แต่คุณไม่จำเป็นต้องทำรัง! คุณสามารถเรียนภาคปกติได้ ตัวอย่างเช่นเช่นนี้:
public class HandleBar {
   public void right() {
       System.out.println("Steering wheel to the right!");
   }

   public void left() {

       System.out.println("Steering wheel left");
   }
}

public class Seat {

   public void up() {

       System.out.println("The seat is up!");
   }

   public void down() {

       System.out.println("The seat is down!");
   }
}
คำถามที่ดีมาก! แน่นอนว่าเราไม่มีข้อจำกัดทางเทคนิค - เราสามารถทำได้ด้วยวิธีนี้ เป็นเรื่องเกี่ยวกับการออกแบบคลาสอย่างถูกต้องจากมุมมองของโปรแกรมเฉพาะและในความหมายของโปรแกรมนั้น คลาสภายในเป็นคลาสสำหรับการเน้นเอนทิตีบางอย่างในโปรแกรมที่เชื่อมโยงกับเอนทิตีอื่นอย่างแยกไม่ออก พวงมาลัย ที่นั่ง แป้นเหยียบเป็นส่วนประกอบของจักรยาน แยกจากจักรยานก็ไม่สมเหตุสมผล ถ้าเราแยกคลาสเหล่านี้ออกจากคลาสสาธารณะ โปรแกรมของเราอาจมีโค้ดต่อไปนี้:
public class Main {

   public static void main(String[] args) {
       HandleBar handleBar = new HandleBar();
       handleBar.right();
   }
}
อืม... ความหมายของโค้ดนี้ยากที่จะอธิบายด้วยซ้ำ เรามีแฮนด์จักรยานแปลกๆ อยู่ (ทำไมต้องใช้ด้วย พูดตามตรงก็ไม่รู้) แล้วพวงมาลัยนี้ก็หมุนไปทางขวา... ด้วยตัวเอง โดยไม่มีจักรยาน... ด้วยเหตุผลบางอย่าง การแยกสาระสำคัญของพวงมาลัยออกจากแก่นแท้ของจักรยานทำให้เราสูญเสียตรรกะของโปรแกรมของเราไป เมื่อใช้คลาสภายใน โค้ดจะดูแตกต่างไปจากเดิมอย่างสิ้นเชิง:
public class Main {

   public static void main(String[] args) {

       Bicycle peugeot = new Bicycle("Peugeot", 120);
       Bicycle.HandleBar handleBar = peugeot.new HandleBar();
       Bicycle.Seat seat = peugeot.new Seat();

       seat.up();
       peugeot.start();
       handleBar.left();
       handleBar.right();
   }
}
เอาต์พุตคอนโซล:

Сиденье поднято выше!
Поехали!
Руль влево!
Руль вправо!
สิ่งที่เกิดขึ้นอย่างกะทันหันก็สมเหตุสมผล! :) เราได้สร้างวัตถุจักรยาน เราสร้าง "วัตถุย่อย" สองอย่างขึ้นมา - พวงมาลัยและเบาะนั่ง เรายกเบาะให้สูงขึ้นเพื่อความสะดวก - แล้วเราก็ไป: เรากลิ้งและบังคับทิศทางในที่ที่เราต้องไป! :) วิธีการที่เราต้องการถูกเรียกบนวัตถุที่จำเป็น ทุกอย่างเรียบง่ายและสะดวกสบาย ในตัวอย่างนี้ การเน้นแฮนด์และเบาะนั่งจะช่วยเพิ่มการห่อหุ้ม (เรากำลังซ่อนข้อมูลเกี่ยวกับชิ้นส่วนของจักรยานภายในคลาสที่เกี่ยวข้อง) และช่วยให้เราสร้างนามธรรมที่มีรายละเอียดมากขึ้น ตอนนี้เรามาดูสถานการณ์อื่นกัน สมมติว่าเราต้องการสร้างโปรแกรมที่สร้างโมเดลร้านขายจักรยานและอะไหล่ คลาสภายในที่ซ้อนกัน - 4ในสถานการณ์นี้ วิธีแก้ปัญหาก่อนหน้าของเราจะล้มเหลว ภายในขอบเขตของร้านอะไหล่ แต่ละส่วนของจักรยานยนต์มีความหมายที่นอกเหนือไปจากแก่นแท้ของจักรยานยนต์ด้วยซ้ำ ตัวอย่างเช่น เราจะต้องมีวิธีการเช่น "ขายคันเหยียบให้ผู้ซื้อ" "ซื้อเบาะใหม่" เป็นต้น อาจเป็นความผิดพลาดหากใช้คลาสภายในที่นี่ - แต่ละส่วนของจักรยานภายในโปรแกรมใหม่ของเรามีความหมายในตัวเอง: มันแยกจากแก่นแท้ของจักรยาน และไม่มีการเชื่อมโยงกับมันในทางใดทางหนึ่ง นี่คือสิ่งที่คุณควรใส่ใจหากคุณสงสัยว่าคุณจำเป็นต้องใช้คลาสภายในหรือแยกเอนทิตีทั้งหมดออกเป็นคลาสแยกกัน การเขียนโปรแกรมเชิงวัตถุนั้นยอดเยี่ยมเพราะทำให้ง่ายต่อการสร้างแบบจำลองเอนทิตีในโลกแห่งความเป็นจริง นี่คือสิ่งที่คุณสามารถใช้เป็นแนวทางในการตัดสินใจว่าจะใช้คลาสภายในหรือไม่ ในร้านค้าจริง อะไหล่จะแยกจากจักรยานยนต์ ซึ่งถือเป็นเรื่องปกติ ซึ่งหมายความว่าสิ่งนี้จะถูกต้องเมื่อออกแบบโปรแกรม เอาล่ะ เราได้แยก "ปรัชญา" ออกไปแล้ว :) ตอนนี้เรามาทำความคุ้นเคยกับคุณสมบัติ "ทางเทคนิค" ที่สำคัญของคลาสภายในกันดีกว่า นี่คือสิ่งที่คุณต้องจำและทำความเข้าใจอย่างแน่นอน:
  1. วัตถุของคลาสภายในไม่สามารถดำรงอยู่ได้หากไม่มีวัตถุของคลาส "ภายนอก"

    นี่เป็นเหตุผล: นั่นคือเหตุผลที่เราสร้าง คลาส Seatภายในขึ้นมาHandleBarเพื่อไม่ให้พวงมาลัยและที่นั่งแบบไม่มีเจ้าของปรากฏที่นี่และที่นั่นในโปรแกรมของเรา

    รหัสนี้จะไม่รวบรวม:

    public static void main(String[] args) {
    
       HandleBar handleBar = new HandleBar();
    }

    คุณสมบัติที่สำคัญดังต่อไปนี้ต่อจากนี้:

  2. วัตถุของคลาสภายในสามารถเข้าถึงตัวแปรของคลาส "ภายนอก"

    ตัวอย่างเช่น เรามาเพิ่มBicycleตัวแปร ให้กับคลาสของเรา int seatPostDiameter- เส้นผ่านศูนย์กลางของหลักอาน

    จากนั้นในคลาสภายในSeatเราสามารถสร้างวิธีการgetSeatParam()ที่จะบอกพารามิเตอร์ที่นั่งให้เราทราบ:

    public class Bicycle {
    
       private String model;
       private int weight;
    
       private int seatPostDiameter;
    
       public Bicycle(String model, int weight, int seatPostDiameter) {
           this.model = model;
           this.weight = weight;
           this.seatPostDiameter = seatPostDiameter;
    
       }
    
       public void start() {
           System.out.println("Go!");
       }
    
       public class Seat {
    
           public void up() {
    
               System.out.println("The seat is up!");
           }
    
           public void down() {
    
               System.out.println("The seat is down!");
           }
    
           public void getSeatParam() {
    
               System.out.println("Seat parameter: seatpost diameter = " + Bicycle.this.seatPostDiameter);
           }
       }
    }

    และตอนนี้เราสามารถรับข้อมูลนี้ในโปรแกรมของเรา:

    public class Main {
    
       public static void main(String[] args) {
    
           Bicycle bicycle = new Bicycle("Peugeot", 120, 40);
           Bicycle.Seat seat = bicycle.new Seat();
    
           seat.getSeatParam();
       }
    }

    เอาต์พุตคอนโซล:

    
    Параметр сиденья: диаметр подседельного штыря = 40

    ใส่ใจ:ตัวแปรใหม่ถูกประกาศด้วยตัวแก้ไขที่เข้มงวดที่สุดprivate- แล้วคนชั้นในยังเข้าได้!

  3. ไม่สามารถสร้างวัตถุคลาสภายในในวิธีการคงที่ของคลาส "ภายนอก"

    สิ่งนี้อธิบายได้จากคุณสมบัติการออกแบบของคลาสภายใน คลาสภายในสามารถมีคอนสตรัคเตอร์พร้อมพารามิเตอร์หรือเพียงคอนสตรัคเตอร์เริ่มต้น แต่โดยไม่คำนึงถึงสิ่งนี้ เมื่อเราสร้างอ็อบเจ็กต์ของคลาสภายใน การอ้างอิงถึงอ็อบเจ็กต์ของคลาส "ภายนอก" จะถูกส่งผ่านไปอย่างเงียบ ๆ ท้ายที่สุดแล้ว การมีอยู่ของวัตถุดังกล่าวถือเป็นข้อกำหนดเบื้องต้น มิฉะนั้นเราจะไม่สามารถสร้างวัตถุของชนชั้นภายในได้

    แต่ถ้าเมธอดคลาสภายนอกเป็นแบบสแตติก แสดงว่าออบเจ็กต์คลาสภายนอกอาจไม่มีอยู่เลย! ซึ่งหมายความว่าตรรกะของชนชั้นภายในจะถูกทำลาย ในสถานการณ์เช่นนี้ คอมไพลเลอร์จะเกิดข้อผิดพลาด:

    public static Seat createSeat() {
    
       //Bicycle.this cannot be referenced from a static context
       return new Seat();
    }
  4. คลาสภายในไม่สามารถมีตัวแปรและวิธีการคงที่

    ตรรกะที่นี่เหมือนกัน: วิธีการและตัวแปรแบบคงที่สามารถมีอยู่และถูกเรียกได้แม้ว่าจะไม่มีวัตถุก็ตาม

    แต่หากไม่มีวัตถุของคลาส "ภายนอก" เราก็จะไม่สามารถเข้าถึงคลาสภายในได้

    ขัดแย้งกันชัดๆ! ดังนั้นจึงห้ามไม่ให้มีตัวแปรคงที่และวิธีการในคลาสภายใน

    คอมไพเลอร์จะแสดงข้อผิดพลาดเมื่อพยายามสร้าง:

    public class Bicycle {
    
       private int weight;
    
    
       public class Seat {
    
           //inner class cannot have static declarations
           public static void getSeatParam() {
    
               System.out.println("Seat parameter: seatpost diameter = " + Bicycle.this.seatPostDiameter);
           }
       }
    }
  5. เมื่อสร้างอ็อบเจ็กต์ของคลาสภายใน ตัวแก้ไขการเข้าถึงจะมีบทบาทสำคัญ

    คลาส ภายในสามารถแสดงโดยตัวดัดแปลงการเข้าถึงมาตรฐาน- public, privateและprotectedpackage private

    ทำไมมันถึงสำคัญ?

    สิ่งนี้ส่งผลต่อตำแหน่งในโปรแกรมของเราที่เราสามารถยกตัวอย่างคลาสภายในได้

    หากคลาสของเราSeatถูกประกาศเป็นpublicเราสามารถสร้างอ็อบเจ็กต์ในคลาสอื่นได้ ข้อกำหนดเพียงอย่างเดียวคือต้องมีอ็อบเจ็กต์ของคลาส "ภายนอก" ด้วย

    อย่างไรก็ตาม เราได้ทำสิ่งนี้ไปแล้วที่นี่:

    public class Main {
    
       public static void main(String[] args) {
    
           Bicycle peugeot = new Bicycle("Peugeot", 120);
           Bicycle.HandleBar handleBar = peugeot.new HandleBar();
           Bicycle.Seat seat = peugeot.new Seat();
    
           seat.up();
           peugeot.start();
           handleBar.left();
           handleBar.right();
       }
    }

    เราเข้าถึงคลาสภายในได้อย่างง่ายดายHandleBarจากไฟล์Main.

    หากเราประกาศคลาสภายในเป็นprivateเราจะมีสิทธิ์เข้าถึงเฉพาะการสร้างวัตถุภายในคลาส "ภายนอก" เท่านั้น

    Seatเราจะไม่สามารถสร้างวัตถุ จากภายนอกได้อีกต่อไป:

    private class Seat {
    
       //methods
    }
    
    public class Main {
    
       public static void main(String[] args) {
    
           Bicycle bicycle = new Bicycle("Peugeot", 120, 40);
    
           //Bicycle.Seat has a private access in 'Bicycle'
           Bicycle.Seat seat = bicycle.new Seat();
       }
    }

    คุณคงเข้าใจตรรกะอยู่แล้ว :)

  6. ตัวดัดแปลงการเข้าถึงสำหรับคลาสภายในทำงานเหมือนกับตัวแปรทั่วไป

    ตัวแก้ไขprotectedให้การเข้าถึงตัวแปรคลาสในคลาสที่สืบทอดและในคลาสที่อยู่ในแพ็คเกจเดียวกัน

    งาน เดียวกันprotectedสำหรับคลาสภายใน สามารถสร้าง ออบเจ็กต์protectedคลาสภายในได้ :

    • ภายในคลาส "ภายนอก"
    • ในชั้นเรียนที่สืบทอด;
    • ในคลาสที่อยู่ในแพ็คเกจเดียวกัน

    หากคลาสภายในไม่มีตัวแก้ไขการเข้าถึง ( package private) ก็สามารถสร้างอ็อบเจ็กต์ของคลาสภายในได้

    • ภายในคลาส "ภายนอก"
    • ในคลาสที่อยู่ในแพ็คเกจเดียวกัน

    คุณคุ้นเคยกับตัวปรับแต่งมาเป็นเวลานาน ดังนั้นจึงไม่มีปัญหาใดๆ ที่นี่

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