โค้ดยิม/จาวาบล็อก/สุ่ม/คลาสภายในที่ซ้อนกัน
John Squirrels
ระดับ
San Francisco

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

เผยแพร่ในกลุ่ม
สวัสดี! วันนี้เราจะกล่าวถึงหัวข้อสำคัญ - คลาสที่ซ้อนกันทำงานอย่างไรใน Java 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("Let's go!");
   }

   public class Handlebar {

       public void right() {
           System.out.println("Steer right!");
       }

       public void left() {

           System.out.println("Steer left!");
       }
   }

   public class Seat {

       public void up() {

           System.out.println("Seat up!");
       }

       public void down() {

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

   public void left() {

       System.out.println("Steer left");
   }
}

public class Seat {

   public void up() {

       System.out.println("Seat up!");
   }

   public void down() {

       System.out.println("Seat 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();
   }
}
เอาต์พุตคอนโซล:
Seat up!
Let's go!
Steer left!
Steer right!
ตอนนี้สิ่งที่เราเห็นก็มีเหตุผล! :) เราสร้างวัตถุจักรยาน เราสร้าง "subobjects" ของจักรยานสองชิ้น — แฮนด์บาร์และเบาะนั่ง เรายกที่นั่งขึ้นเพื่อความสบายและออกไป: ถีบและบังคับเลี้ยวตามต้องการ! :) เมธอดที่เราต้องการนั้นถูกเรียกบนวัตถุที่เหมาะสม ทุกอย่างง่ายและสะดวก ในตัวอย่างนี้ การแยกแฮนด์บาร์และเบาะนั่งออกช่วยเพิ่มการห่อหุ้ม (เราซ่อนข้อมูลเกี่ยวกับชิ้นส่วนจักรยานไว้ในคลาสที่เกี่ยวข้อง) และช่วยให้เราสร้างนามธรรมที่มีรายละเอียดมากขึ้นได้ ทีนี้มาดูสถานการณ์อื่นกัน สมมติว่าเราต้องการสร้างโปรแกรมจำลองร้านจักรยานและอะไหล่จักรยาน คลาสภายในที่ซ้อนกัน - 4ในสถานการณ์นี้ วิธีแก้ไขปัญหาก่อนหน้านี้จะไม่ทำงาน ที่ร้านขายจักรยาน อะไหล่จักรยานแต่ละชิ้นมีความสมเหตุสมผลแม้ว่าจะแยกออกจากจักรยานก็ตาม ตัวอย่างเช่น เราต้องการวิธีการเช่น "ขายคันเหยียบให้กับลูกค้า" "ซื้อเบาะนั่งใหม่" เป็นต้น การใช้คลาสภายในที่นี่อาจไม่ถูกต้อง — ชิ้นส่วนจักรยานแต่ละชิ้นในโปรแกรมใหม่ของเรามีความหมายที่ยืนหยัด ของมันเอง: สามารถแยกออกจากแนวคิดของจักรยานได้ นี่คือสิ่งที่คุณต้องใส่ใจหากคุณสงสัยว่าควรใช้คลาสภายในหรือจัดระเบียบเอนทิตีทั้งหมดเป็นคลาสแยกต่างหาก การเขียนโปรแกรมเชิงวัตถุนั้นดีเพราะทำให้ง่ายต่อการจำลองเอนทิตีในโลกแห่งความเป็นจริง นี่อาจเป็นหลักการชี้นำของคุณในการตัดสินใจว่าจะใช้ชั้นเรียนภายในหรือไม่ ในร้านค้าจริง อะไหล่แยกจากจักรยาน — ไม่เป็นไร ซึ่งหมายความว่ามันก็โอเคเมื่อออกแบบโปรแกรม เอาล่ะ เราเข้าใจ "ปรัชญา" แล้ว :) ตอนนี้เรามาทำความรู้จักกับคุณสมบัติ "ทางเทคนิค" ที่สำคัญของชั้นเรียนภายในกัน นี่คือสิ่งที่คุณต้องจำและทำความเข้าใจอย่างแน่นอน:
  1. วัตถุของชั้นในไม่สามารถอยู่ได้โดยไม่มีวัตถุของชั้นนอก

    สิ่งนี้สมเหตุสมผล: นี่คือเหตุผลที่เราสร้างSeatและHandlebarคลาสภายในในโปรแกรมของเรา — เพื่อไม่ให้จบลงด้วยแฮนด์บาร์และเบาะนั่งที่รกร้าง

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

    public static void main(String[] args) {
    
       Handlebar handlebar = new Handlebar();
    }

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

  2. วัตถุของชั้นในสามารถเข้าถึงตัวแปรของชั้นนอก

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

    จากนั้นในSeatคลาสภายใน เราสามารถสร้างdisplaySeatProperties()เมธอดที่แสดงคุณสมบัติของที่นั่งได้:

    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("Let's go!");
       }
    
       public class Seat {
    
           public void up() {
    
               System.out.println("Seat up!");
           }
    
           public void down() {
    
               System.out.println("Seat down!");
           }
    
           public void displaySeatProperties() {
    
               System.out.println("Seat properties: 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.displaySeatProperties();
       }
    }

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

    Seat properties: seatpost diameter = 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 {
    
           // An inner class cannot have static declarations
           public static void displaySeatProperties() {
    
               System.out.println("Seat properties: seatpost diameter = " + Bicycle.this.seatPostDiameter);
           }
       }
    }
  5. เมื่อสร้างออบเจกต์ของคลาสภายใน ตัวแก้ไขการเข้าถึงนั้นมีความสำคัญ

    คลาสภายในสามารถทำเครื่องหมายด้วยตัวดัดแปลงการเข้าถึงมาตรฐาน: public, private, protected, package privateและ

    ทำไมเรื่องนี้?

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

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

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

    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 private access in Bicycle
           Bicycle.Seat seat = bicycle.new Seat();
       }
    }

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

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

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

    protectedยังใช้ได้กับชั้นเรียนภายใน เราสามารถสร้างprotectedวัตถุของชั้นใน:

    • ในชั้นนอก;
    • ในคลาสย่อยของมัน
    • ในคลาสที่อยู่ในแพ็คเกจเดียวกัน

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

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

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

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