ทำไมคลาส Java ภายนอกสามารถเข้าถึงสมาชิกส่วนตัวของคลาสภายในได้


177

ฉันสังเกตว่าคลาสภายนอกสามารถเข้าถึงตัวแปรอินสแตนซ์ส่วนตัวของคลาสภายในได้ เป็นไปได้อย่างไร? นี่คือตัวอย่างรหัสที่แสดงให้เห็นถึงสิ่งเดียวกัน:

class ABC{
    class XYZ{
        private int x=10;
    }

    public static void main(String... args){
        ABC.XYZ xx = new ABC().new XYZ();
        System.out.println("Hello :: "+xx.x); ///Why is this allowed??
    }
}

เหตุใดพฤติกรรมนี้จึงอนุญาต


คำถามนี้ทำให้ฉันสับสนอยู่พักหนึ่งจนกระทั่งฉันเห็นความคิดเห็น ... ซึ่งอธิบายว่าทำไมฉันไม่สามารถเข้าถึง xx.x บนเครื่องของฉัน ..
Wang Sheng

4
ความคิดเห็นสับสนฉันฉันเรียกใช้รหัสข้างต้นใน Java 8 มันจะรวบรวมและเรียกใช้ xx.x สามารถเข้าถึงได้
ลีออน

Harish คุณช่วยกรุณายกเลิกการยอมรับคำตอบที่ยอมรับได้ (ซึ่งไม่ตอบคำถามที่คุณถาม) และยอมรับคำตอบของ Martin Andersson ลงด้านล่างซึ่งคำตอบนี้จะตอบโดยละเอียดหรือไม่?
temporary_user_name

FWIW ไวยากรณ์นี้น่ากลัวอย่างแน่นอน:new ABC().new XYZ()
Josh M.

คำตอบ:


80

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

  1. ฟังก์ชันการทำงานบางอย่างในคลาสภายนอกของคุณจะชัดเจนที่สุดหากถูกนำไปใช้ในคลาสที่แยกต่างหาก
  2. แม้ว่ามันจะอยู่ในคลาสที่แยกจากกัน แต่ฟังก์ชั่นนั้นเชื่อมโยงอย่างใกล้ชิดกับวิธีการทำงานของคลาสภายนอก

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


217
คำตอบนี้อธิบายว่าทำไมคลาสที่ซ้อนกันสามารถเข้าถึงสมาชิกส่วนตัวของคลาสภายนอกได้ แต่คำถามคือทำไมคลาสภายนอกมีสิทธิ์เข้าถึงสมาชิกส่วนตัวของคลาสที่ซ้อนกัน
แอนดรู

13
เพียงเพิ่ม "และในทางกลับกัน" เพื่อกำหนดข้อกำหนดเหล่านี้คลาสภายในสามารถเข้าถึงคลาสภายนอกได้อย่างเต็มที่และตอนนี้ก็ตอบคำถาม
anthropomo

13
อันนี้ไม่ใช่คำตอบที่ถูกต้องสำหรับปัญหานี้ได้ที่นี่: stackoverflow.com/questions/19747812/…
Colin Su

4
@anthropomo: ไม่มันไม่ได้ ข้อกำหนดทั้งสองเป็นไปได้อย่างสมบูรณ์แบบโดยไม่ต้องเรียนนอกที่มีการเข้าถึงสมาชิกส่วนตัวของชั้นใน
หรือ Mapper

หนึ่งตัวอย่างที่ดีที่นี้จะเป็นประโยชน์โดยเฉพาะอย่างยิ่งเป็นแบบสร้างstackoverflow.com/a/1953567/1262000 ระดับผู้ปกครองเพียงต้องการคอนสตรัคเตอร์ที่ใช้ตัวสร้างและเข้าถึงตัวแปรสมาชิกทั้งหมด มิฉะนั้นคุณจะต้องมีคอนสตรัคเตอร์ส่วนตัวในคลาสพาเรนต์พร้อมตัวแปรสมาชิกส่วนตัวทั้งหมด
vikky.rk

62

หากคุณต้องการซ่อนสมาชิกส่วนตัวของชั้นในของคุณคุณอาจกำหนดส่วนต่อประสานกับสมาชิกสาธารณะและสร้างชั้นในแบบไม่ระบุชื่อที่ใช้ส่วนต่อประสานนี้ ตัวอย่างร้อง:

class ABC{
    private interface MyInterface{
         void printInt();
    }

    private static MyInterface mMember = new MyInterface(){
        private int x=10;

        public void printInt(){
            System.out.println(String.valueOf(x));
        }
    };

    public static void main(String... args){
        System.out.println("Hello :: "+mMember.x); ///not allowed
        mMember.printInt(); // allowed
    }
}

นี่เป็นตัวอย่างรหัสที่ยอดเยี่ยม สิ่งที่ฉันต้องการ ขอบคุณ!
kevinarpe

โปรดระบุรหัสที่สามารถเรียกใช้ได้ มีอะไรเพิ่มเติมโปรดเหตุผลที่ตัวแปรส่วนตัวไม่สามารถเข้าถึงได้
androidyue

7
แต่แล้ว ... ชั้นในนั้นไม่ระบุชื่อ คุณไม่สามารถสร้างอินสแตนซ์ของคลาสภายในนั้นได้หลายอินสแตนซ์หรือใช้คลาสภายในนั้นสำหรับการประกาศตัวแปร ฯลฯ
หรือ Mapper

@OR Mapper ที่ว่าทำไมถึงแม้ว่าจะเป็นแบบสาธารณะที่นี่ก็ไม่ได้รับอนุญาตเช่นx mMember.x
MAC

54

คลาสภายในคือ (เพื่อจุดประสงค์ในการควบคุมการเข้าถึง) ซึ่งถือว่าเป็นส่วนหนึ่งของคลาสที่มีอยู่ นี่หมายถึงการเข้าถึงแบบเต็มรูปแบบสำหรับทุกคน

วิธีนี้จะถูกนำมาใช้คือการใช้วิธีการป้องกันแพคเกจสังเคราะห์: ชั้นในจะถูกรวบรวมไปยังชั้นที่แยกต่างหากในแพคเกจเดียวกัน (ABC $ XYZ) JVM ไม่สนับสนุนการแยกระดับนี้โดยตรงดังนั้นในระดับ bytecode ระดับ ABC $ XYZ จะมีวิธีการป้องกันแพ็กเกจที่คลาสภายนอกใช้เพื่อเข้าถึงเมธอด / ฟิลด์ส่วนตัว


17

มีคำตอบที่ถูกต้องปรากฏในคำถามอื่นที่คล้ายกับนี้: ทำไมสมาชิกเอกชนของคลาสที่ซ้อนกันสามารถเข้าถึงได้โดยวิธีการของคลาสที่ปิดล้อม

มันบอกว่ามีคำจำกัดความของการกำหนดขอบเขตส่วนตัวบนJLS - การพิจารณาการเข้าถึง :

มิฉะนั้นหากสมาชิกหรือตัวสร้างถูกประกาศเป็นส่วนตัวการเข้าถึงจะทำได้ก็ต่อเมื่อมันเกิดขึ้นภายในเนื้อความของคลาสระดับบนสุด (§7.6) ที่ล้อมรอบการประกาศของสมาชิกหรือคอนสตรัคเตอร์


ฉันคิดว่าตัวอย่างนี้สามารถตอบคำถามของฉันได้อย่างดี
เชน

5

กรณีการใช้งานที่สำคัญของ IMHO สำหรับชั้นในคือลวดลายโรงงาน คลาสที่ปิดล้อมอาจเตรียมอินสแตนซ์ของข้อ จำกัด การเข้าถึงภายในคลาสที่ไม่มีข้อ จำกัด และส่งอินสแตนซ์ไปยังโลกภายนอกซึ่งการเข้าถึงแบบส่วนตัวจะได้รับการเคารพ

ในทางตรงข้ามกับabyx การประกาศคลาสคงไม่เปลี่ยนข้อ จำกัด การเข้าถึงคลาสที่ล้อมรอบดังแสดงด้านล่าง ข้อ จำกัด การเข้าถึงระหว่างคลาสสแตติกในคลาสที่ปิดล้อมเดียวกันนั้นกำลังทำงานอยู่ ฉันรู้สึกประหลาดใจ ...

class MyPrivates {
    static class Inner1 { private int test1 = 2; }
    static class Inner2 { private int test2 = new Inner1().test1; }

    public static void main(String[] args) {
        System.out.println("Inner : "+new Inner2().test2);
    }
}

1
ความคิดเห็นที่ดี แต่ไม่มีคำตอบ
ceving

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

3

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

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


3

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

ถ้าในกรณีของคุณมันทำให้รู้สึกไม่สำหรับการเรียนที่จะสามารถที่จะเห็นผลงานภายในของแต่ละคน - static class XYZซึ่งโดยทั่วไปหมายถึงว่าชั้นด้านในก็สามารถได้รับการทำชั้นปกติคุณสามารถประกาศระดับชั้นเป็น การใช้staticจะหมายความว่าพวกเขาจะไม่ได้ของรัฐหุ้น (และตัวอย่างเช่นnew ABC().new XYZ()จะไม่ทำงานและคุณจะต้องใช้new ABC.XYZ().
แต่ถ้าเป็นกรณีที่คุณควรคิดเกี่ยวกับว่าXYZมันควรจะเป็นระดับชั้นและบางทีมันอาจจะสมควรได้รับของตัวเอง ไฟล์บางครั้งการสร้างคลาสภายในแบบสแตติก (ตัวอย่างเช่นถ้าคุณต้องการคลาสขนาดเล็กที่ใช้อินเทอร์เฟซที่คลาสภายนอกของคุณใช้อยู่และจะไม่มีประโยชน์ในที่อื่น) แต่ประมาณครึ่งหนึ่งของเวลา มันควรจะได้รับการทำชั้นนอก


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

3

Thilo เพิ่มคำตอบที่ดีสำหรับคำถามแรกของคุณ "เป็นไปได้อย่างไร" ฉันต้องการที่จะอธิบายรายละเอียดเล็กน้อยในคำถามที่ถามที่สอง: เหตุใดจึงมีพฤติกรรมนี้อนุญาต

สำหรับผู้เริ่มต้นเราจะต้องชัดเจนอย่างสมบูรณ์ว่าพฤติกรรมนี้ไม่ จำกัด เฉพาะคลาสภายในซึ่งโดยนิยามเป็นประเภทซ้อนกันไม่คงที่ ลักษณะการทำงานนี้ได้รับอนุญาตสำหรับประเภทที่ซ้อนกันทั้งหมดรวมถึง enums ซ้อนกันและอินเทอร์เฟซที่ต้องเป็นแบบคงที่และไม่สามารถมีอินสแตนซ์ที่ล้อมรอบ โดยพื้นฐานแล้วตัวแบบคือการทำให้เข้าใจง่ายลงไปยังข้อความสั่งต่อไปนี้: รหัสที่ซ้อนกันมีการเข้าถึงแบบเต็มรูปแบบในการใส่รหัส - และในทางกลับกัน

ถ้าอย่างนั้นทำไม ฉันคิดว่าตัวอย่างแสดงให้เห็นถึงจุดที่ดีขึ้น

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

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

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


1
นี่ควรเป็นคำตอบที่ยอมรับ ชัดเจนและทั่วถึง คำตอบที่ยอมรับไม่ได้ตอบคำถาม
temporary_user_name

IMO ยังคงไม่ตอบคำถามว่าทำไมเราไม่สามารถเพิ่มเขตข้อมูลส่วนตัวลงในคลาสภายในซึ่งชั้นนอกไม่สามารถเข้าถึงได้โดยตรง นอกจากว่าฉันผิดผิดซากปรักหักพังนี้เป็นหนึ่งในกรณีหลักสำหรับชั้นใน - การสร้างประเภทที่ไม่เปลี่ยนรูปแบบซึ่งมีอายุสั้น FWIW C # สนับสนุนสิ่งนี้อย่างมีความสุข: repl.it/repls/VengefulCheeryInverse
Josh M.

-1

คลาสภายในถือเป็นคุณลักษณะของคลาสภายนอก ดังนั้นไม่ว่าตัวแปรอินสแตนซ์ของคลาสภายในจะเป็นส่วนตัวหรือไม่คลาสภายนอกสามารถเข้าถึงได้โดยไม่มีปัญหาใด ๆ เช่นเดียวกับการเข้าถึงแอตทริบิวต์ส่วนตัวอื่น ๆ (ตัวแปร)

class Outer{

private int a;

class Inner{
private int b=0;
}

void outMethod(){
a = new Inner().b;
}
}

-2

เพราะmain()วิธีการของคุณอยู่ในABCชั้นเรียนซึ่งสามารถเข้าถึงชั้นในของตัวเอง


2
คำถามไม่ใช่ว่าสมาชิกของABCคลาสสามารถเข้าถึงคลาสที่ซ้อนอยู่ในABCคลาสได้หรือไม่ แต่ทำไมพวกเขาสามารถเข้าถึงสมาชิกส่วนตัวของคลาสที่ซ้อนอยู่ในABCคลาสใน Java ได้
หรือผู้ทำแผนที่

ฉันตอบคำถามในวันเดียวกันกับที่ถาม ใครบางคนแก้ไขคำถาม 2 ปีต่อมาและ downvote มา 3 ปีต่อมา ฉันค่อนข้างมั่นใจว่าใครก็ตามที่แก้ไขคำถามจะเปลี่ยนถ้อยคำของคำถามมากเกินไป
aberrant80
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.