Mengapa kelas luar Jawa dapat mengakses anggota pribadi kelas dalam?

177

Saya mengamati bahwa kelas-kelas luar dapat mengakses variabel-variabel instance kelas privat. Bagaimana ini mungkin? Berikut adalah contoh kode yang menunjukkan hal yang sama:

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??
    }
}

Mengapa perilaku ini diizinkan?

Harish
sumber
Pertanyaan ini membingungkan saya cukup lama sampai saya melihat komentar ... yang menjelaskan mengapa saya tidak dapat mengakses xx.x di mesin saya ..
Wang Sheng
4
Komentar membuat saya bingung, saya menjalankan kode di atas di Java 8, itu mengkompilasi dan menjalankan. xx.x dapat diakses.
leon
Harish, bisakah Anda tidak menerima jawaban yang diterima (yang tidak menjawab pertanyaan yang Anda ajukan) dan sebaliknya menerima jawaban Martin Andersson di bawah, yang menjawab dengan sangat teliti?
temporary_user_name
FWIW sintaks ini benar-benar menghebohkan:new ABC().new XYZ()
Josh M.

Jawaban:

80

Kelas dalam hanyalah cara untuk memisahkan beberapa fungsi yang benar-benar milik kelas luar asli. Mereka dimaksudkan untuk digunakan ketika Anda memiliki 2 persyaratan:

  1. Beberapa fungsionalitas di kelas luar Anda akan paling jelas jika diterapkan di kelas yang terpisah.
  2. Meskipun berada di kelas yang terpisah, fungsionalitasnya sangat terkait erat dengan cara kerja kelas luar.

Dengan persyaratan ini, kelas dalam memiliki akses penuh ke kelas luarnya. Karena mereka pada dasarnya adalah anggota dari kelas luar, masuk akal bahwa mereka memiliki akses ke metode dan atribut dari kelas luar - termasuk privat.

Kaleb Brasee
sumber
217
Jawaban ini menjelaskan mengapa kelas bersarang memiliki akses ke anggota pribadi dari kelas luar mereka. Tetapi pertanyaannya adalah mengapa kelas luar memiliki akses ke anggota pribadi dari kelas bersarang.
Andrew
13
Cukup tambahkan "dan sebaliknya" ke Mengingat persyaratan ini, kelas dalam memiliki akses penuh ke kelas luar mereka dan sekarang menjawab pertanyaan.
antropomo
13
Yang ini bukan jawaban yang benar untuk masalah ini, di sini tidak: stackoverflow.com/questions/19747812/…
Colin Su
4
@anthropomo: Tidak, tidak. Kedua persyaratan tersebut sangat layak tanpa kelas luar yang memiliki akses ke anggota pribadi kelas dalam.
ATAU Mapper
Salah satu contoh yang baik di mana ini sangat berguna adalah Pola Builder, stackoverflow.com/a/1953567/1262000 . Kelas induk hanya membutuhkan konstruktor yang mengambil Builder dan mengakses semua variabel anggotanya. Kalau tidak, Anda harus memiliki konstruktor pribadi di kelas induk dengan semua variabel anggota pribadi.
vikky.rk
62

Jika Anda ingin menyembunyikan anggota pribadi dari kelas batin Anda, Anda dapat mendefinisikan Antarmuka dengan anggota publik dan membuat kelas dalam anonim yang mengimplementasikan antarmuka ini. Contoh di bawah:

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
    }
}
Ich
sumber
Ini adalah potongan kode yang brilian. Apa yang saya butuhkan. Terima kasih!
kevinarpe
Harap berikan kode yang dapat dijalankan. Terlebih lagi tolong alasan mengapa variabel pribadi tidak dapat diizinkan untuk mengakses.
androidyue
7
Tapi kemudian ... kelas batinnya anonim. Anda tidak dapat membuat beberapa instance kelas dalam itu, atau menggunakan kelas batin itu untuk deklarasi variabel apa pun, dll.
ATAU Mapper
@ ATAU Mapper itu sebabnya meskipun xpublik di sini, itu tidak diizinkan mMember.x.
MAC
54

Kelas dalam (untuk tujuan kontrol akses) dianggap sebagai bagian dari kelas yang mengandung. Ini berarti akses penuh ke semua privat.

Cara ini diimplementasikan menggunakan metode yang dilindungi paket sintetik: Kelas dalam akan dikompilasi ke kelas yang terpisah dalam paket yang sama (ABC $ XYZ). JVM tidak mendukung level isolasi ini secara langsung, sehingga pada level bytecode ABC $ XYZ akan memiliki metode yang dilindungi paket yang digunakan kelas luar untuk sampai ke metode / bidang pribadi.

Thilo
sumber
17

Ada jawaban yang benar muncul pada pertanyaan lain yang mirip dengan ini: Mengapa anggota pribadi dari kelas bersarang dapat diakses dengan metode kelas melampirkan?

Dikatakan ada definisi pelingkupan pribadi pada JLS - Menentukan Aksesibilitas :

Kalau tidak, jika anggota atau konstruktor dinyatakan pribadi, maka akses diizinkan jika dan hanya jika itu terjadi di dalam tubuh kelas tingkat atas (§7.6) yang melampirkan pernyataan anggota atau konstruktor.

Colin Su
sumber
Saya pikir cuplikan ini dapat menjawab pertanyaan saya dengan baik.
shen
5

Kasus penggunaan penting IMHO untuk kelas dalam adalah pola pabrik. Kelas terlampir dapat menyiapkan turunan dari kelas dalam tanpa batasan akses dan meneruskan turunan ke dunia luar, di mana akses pribadi akan dihormati.

Bertentangan dengan abyx yang mendeklarasikan class static tidak mengubah batasan akses ke kelas yang menyertakan, seperti yang ditunjukkan di bawah ini. Juga pembatasan akses antara kelas statis di kelas tertutup yang sama berfungsi. Saya terkejut ...

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);
    }
}
thomasfr
sumber
1
Komentar yang bagus tetapi tidak ada jawaban.
ceving
@cerving Ini sebenarnya satu-satunya jawaban yang memungkinkan saya untuk melihat penggunaan nyata praktis dari keputusan desain yang aneh ini. Pertanyaannya adalah mengapa hal itu diputuskan dengan cara ini, dan ini adalah alasan yang bagus - sebuah demonstrasi perbedaan antara apa yang Anda ingin akses kelas luar di kelas dalam dan apa yang Anda inginkan untuk diakses oleh kelas yang tidak terkait lainnya.
et_l
3

Pembatasan akses dilakukan berdasarkan per kelas. Tidak ada cara untuk metode yang dinyatakan dalam kelas untuk tidak dapat mengakses semua anggota instance / kelas. Ini masuk akal bahwa kelas-kelas dalam juga memiliki akses tanpa batas ke anggota kelas luar, dan kelas luar memiliki akses tanpa batas ke anggota kelas dalam.

Dengan menempatkan kelas di dalam kelas lain, Anda membuatnya terikat erat dengan implementasi, dan apa pun yang merupakan bagian dari implementasi harus memiliki akses ke bagian lain.

TofuBeer
sumber
3

Logika di balik kelas batin adalah bahwa jika Anda membuat kelas dalam di kelas luar, itu karena mereka perlu berbagi beberapa hal, dan dengan demikian masuk akal bagi mereka untuk dapat memiliki lebih banyak fleksibilitas daripada kelas "biasa".

Jika, dalam kasus Anda, tidak masuk akal bagi kelas untuk dapat melihat cara kerja masing-masing - yang pada dasarnya berarti bahwa kelas batin hanya dapat dibuat sebagai kelas reguler, Anda dapat mendeklarasikan kelas dalam sebagai static class XYZ. Menggunakan staticakan berarti mereka tidak akan berbagi status (dan, misalnya new ABC().new XYZ()tidak akan berfungsi, dan Anda harus menggunakannya new ABC.XYZ().
Tetapi, jika itu masalahnya, Anda harus memikirkan apakah XYZbenar-benar harus menjadi kelas dalam dan bahwa mungkin itu pantas sendiri) Terkadang masuk akal untuk membuat kelas dalam statis (misalnya, jika Anda membutuhkan kelas kecil yang mengimplementasikan antarmuka yang digunakan oleh kelas luar Anda, dan itu tidak akan membantu di tempat lain). Tetapi sekitar setengah dari waktu seharusnya dibuat kelas luar.

abyx
sumber
2
Kelas luar juga dapat mengakses anggota privat dari kelas dalam statis , jadi ini tidak ada hubungannya dengan statis . Anda mengatakan "tidak masuk akal bagi kelas untuk dapat melihat kerja batin satu sama lain", tetapi itu tidak selalu terjadi - bagaimana jika masuk akal hanya bagi kelas dalam untuk melihat pekerjaan dalam kelas luar, tetapi tidak sebaliknya. sebaliknya?
ATAU Mapper
3

Thilo menambahkan jawaban yang bagus untuk pertanyaan pertama Anda, "Bagaimana ini mungkin?" Saya ingin menguraikan sedikit pada pertanyaan kedua: Mengapa perilaku ini diizinkan?

Sebagai permulaan, mari kita menjadi sangat jelas bahwa perilaku ini tidak terbatas pada kelas dalam, yang menurut definisi adalah tipe bersarang non-statis. Perilaku ini diperbolehkan untuk semua tipe bersarang, termasuk enum bersarang dan antarmuka yang harus statis dan tidak dapat memiliki instance terlampir. Pada dasarnya, model ini adalah penyederhanaan ke pernyataan berikut: Kode bersarang memiliki akses penuh ke kode terlampir - dan sebaliknya.

Jadi mengapa? Saya pikir contoh menggambarkan hal itu dengan lebih baik.

Pikirkan tubuh dan otak Anda. Jika Anda menyuntikkan heroin ke lengan Anda, otak Anda menjadi tinggi. Jika wilayah amygdala otak Anda melihat apa yang dia yakini sebagai ancaman terhadap keselamatan pribadi Anda, katakanlah tawon misalnya, ia akan membuat tubuh Anda berbalik dan berlari ke bukit tanpa Anda "berpikir" dua kali tentang hal itu.

Jadi, otak adalah bagian intrinsik dari tubuh - dan anehnya, sebaliknya. Menggunakan kontrol akses antara entitas yang terkait erat seperti kehilangan klaim hubungan mereka. Jika Anda benar-benar membutuhkan kontrol akses, maka Anda perlu memisahkan kelas lebih banyak menjadi unit yang benar-benar berbeda. Sampai saat itu, mereka adalah unit yang sama. Contoh pendorong untuk studi lebih lanjut adalah dengan melihat bagaimana Java Iteratorbiasanya diimplementasikan.

Akses tanpa batas dari kode penutup ke kode bersarang membuatnya, sebagian besar, agak tidak berguna untuk menambahkan pengubah akses ke bidang dan metode dari tipe bersarang. Melakukan hal itu menambah kekacauan dan mungkin memberikan rasa aman yang salah untuk pendatang baru bahasa pemrograman Java.

Martin Andersson
sumber
1
Ini seharusnya menjadi jawaban yang diterima. Sangat jelas dan teliti. Sebaliknya jawaban yang diterima bahkan tidak menjawab pertanyaan.
temporary_user_name
IMO ini masih tidak menjawab pertanyaan mengapa kita tidak dapat dengan mudah menambahkan bidang pribadi ke kelas dalam, yang tidak dapat diakses oleh kelas luar secara langsung. Kecuali saya salah, ini menghancurkan salah satu kasus utama untuk kelas batin - menciptakan "struct-like" berumur pendek, tipe yang tidak berubah. FWIW C # dengan senang hati mendukung ini: repl.it/repls/VengefulCheeryInverse
Josh M.
-1

Kelas dalam dianggap sebagai atribut dari kelas luar. Oleh karena itu, tidak peduli variabel instance kelas dalam adalah pribadi atau tidak, kelas luar dapat mengakses tanpa masalah sama seperti mengakses atribut pribadi lainnya (variabel).

class Outer{

private int a;

class Inner{
private int b=0;
}

void outMethod(){
a = new Inner().b;
}
}
MonMoonkey
sumber
-2

Karena main()metode Anda ada di ABCkelas, yang dapat mengakses kelas batinnya sendiri.

aberrant80
sumber
2
Pertanyaannya bukanlah apakah anggota ABCkelas dapat mengakses kelas yang bersarang di ABCkelas, tetapi mengapa mereka dapat mengakses anggota kelas pribadi yang bersarang di ABCkelas di Jawa.
ATAU Mapper
Saya menjawab pertanyaan pada hari yang sama ketika ditanya. Seseorang mengedit pertanyaan 2 tahun kemudian, dan downvote datang 3 tahun kemudian. Saya cukup yakin siapa pun yang mengedit pertanyaan mengubah terlalu banyak kata dari pertanyaan itu.
aberrant80