Pertanyaan Di Jawa, perbedaan antara paket pribadi, publik, terlindungi, dan pribadi


Di Java, apakah ada aturan yang jelas tentang kapan harus menggunakan setiap pengubah akses, yaitu default (paket pribadi), public, protected dan private, saat membuat class dan interface dan berurusan dengan warisan?


2490
2017-10-18 19:53


asal


Jawaban:


Tutorial resmi mungkin berguna bagi Anda.

            │ Kelas │ Paket │ Subclass │ Subclass │ Dunia
            │ │ │ (pkg yang sama) │ (diff pkg) │
────────────┼───────┼─────────┼──────────┼──────── ──┼────────
publik │ + │ + │ + │ + │ +
────────────┼───────┼─────────┼──────────┼──────── ──┼────────
dilindungi │ + │ + │ + │ + │
────────────┼───────┼─────────┼──────────┼──────── ──┼────────
tidak ada pengubah │ + │ + │ + │ │
────────────┼───────┼─────────┼──────────┼──────── ──┼────────
pribadi │ + │ │ │ │

+: dapat diakses
kosong: tidak dapat diakses

4645
2017-10-18 19:57



(Caveat: Saya bukan programmer Java, saya seorang programmer Perl. Perl tidak memiliki perlindungan formal yang mungkin mengapa saya memahami masalah dengan sangat baik :))

Pribadi

Seperti yang Anda pikirkan, hanya itu kelas di mana dinyatakan dapat melihatnya.

Paket Pribadi

Hanya dapat dilihat dan digunakan oleh paket di mana itu diumumkan. Ini adalah default di Java (yang oleh sebagian orang dianggap sebagai kesalahan).

Terlindung

Paket Private + dapat dilihat oleh subclass atau anggota paket.

Publik

Semua orang bisa melihatnya.

Diterbitkan

Terlihat di luar kode yang saya kontrol. (Meskipun tidak sintaks Java, ini penting untuk diskusi ini).

C ++ mendefinisikan tingkat tambahan yang disebut "teman" dan semakin sedikit yang Anda ketahui tentang itu, semakin baik.

Kapan sebaiknya Anda menggunakan apa? Seluruh ide adalah enkapsulasi untuk menyembunyikan informasi. Sebisa mungkin Anda ingin menyembunyikan detail tentang bagaimana sesuatu dilakukan dari pengguna Anda. Mengapa? Karena dengan begitu Anda dapat mengubahnya nanti dan tidak merusak kode siapa pun. Ini memungkinkan Anda mengoptimalkan, memperbaiki, mendesain ulang, dan memperbaiki bug tanpa khawatir seseorang menggunakan kode yang baru saja Anda periksa.

Jadi, aturan praktis adalah membuat hal-hal hanya terlihat seperti yang seharusnya. Mulailah dengan pribadi dan hanya tambahkan lebih banyak visibilitas sesuai kebutuhan. Hanya membuat publik yang benar-benar perlu bagi pengguna untuk mengetahui, setiap detail yang Anda buat kram publik kemampuan Anda untuk mendesain ulang sistem.

Jika Anda ingin pengguna dapat menyesuaikan perilaku, daripada membuat internal publik agar mereka dapat mengesampingkan perilaku, sering kali lebih baik untuk mendorong nyali tersebut menjadi objek dan menjadikan antarmuka itu publik. Dengan cara itu mereka dapat dengan mudah memasukkan objek baru. Misalnya, jika Anda sedang menulis CD player dan ingin "go find info about CD" ini sedikit dapat disesuaikan, daripada membuat metode tersebut publik, Anda akan menempatkan semua fungsi itu ke dalam objeknya sendiri dan menjadikannya hanya objek Anda sebagai pengambil / penyetel publik. . Dengan cara ini, pelit mengungkap keberanian Anda mendorong komposisi dan pemisahan kekhawatiran yang baik

Secara pribadi, saya tetap hanya "pribadi" dan "publik". Banyak bahasa OO yang memilikinya. "Dilindungi" dapat berguna, tetapi itu benar-benar sebuah cheat. Setelah antarmuka lebih dari privat, itu di luar kendali Anda dan Anda harus mencari kode orang lain untuk menemukan kegunaan.

Di sinilah ide "diterbitkan" masuk. Mengubah antarmuka (refactoring itu) mengharuskan Anda menemukan semua kode yang menggunakannya dan mengubahnya juga. Jika antarmuka bersifat pribadi, tidak masalah. Jika dilindungi Anda harus pergi mencari semua subclass Anda. Jika itu publik Anda harus pergi mencari semua kode yang menggunakan kode Anda. Kadang-kadang hal ini mungkin, misalnya jika Anda bekerja pada kode perusahaan yang hanya untuk penggunaan internal, tidak masalah jika antarmuka bersifat publik. Anda dapat mengambil semua kode dari repositori perusahaan. Tetapi jika sebuah antarmuka "diterbitkan", jika ada kode yang menggunakannya di luar kendali Anda, maka Anda akan disemprot. Anda harus mendukung antarmuka atau kode pemecahan risiko. Bahkan antarmuka yang dilindungi dapat dianggap diterbitkan (itulah mengapa saya tidak peduli dengan dilindungi).

Banyak bahasa menemukan sifat hierarkis publik / terlindungi / pribadi terlalu membatasi dan tidak sejalan dengan kenyataan. Untuk itu ada konsep a kelas sifat, tapi itu pertunjukan lain.


357
2017-10-18 21:17



Ini adalah versi tabel yang lebih baik. (Bukti masa depan dengan kolom untuk modul.)

Java Access Modifiers

Penjelasan

  • SEBUAH pribadi anggotanya adalah hanya dapat diakses dalam kelas yang sama seperti yang dideklarasikan.

  • Seorang anggota dengan tidak ada pengubah akses hanya dapat diakses dalam kelas dalam paket yang sama.

  • SEBUAH terlindung anggota dapat diakses di semua kelas dalam paket yang sama dan dalam subclass dalam paket lain.

  • SEBUAH publik anggota dapat diakses untuk semua kelas (kecuali jika berada di a modul yang tidak mengekspor paket yang dideklarasikan di).


Pengubah mana yang dipilih?

Pengubah akses adalah alat untuk membantu Anda mencegah penghancuran enkapsulasi secara tidak sengaja(*). Tanyakan pada diri Anda apakah Anda ingin anggota menjadi sesuatu yang bersifat internal untuk kelas, paket, hirarki kelas atau tidak internal sama sekali, dan pilih tingkat akses yang sesuai.

Contoh:

  • Lapangan long internalCounter mungkin harus pribadi karena bisa berubah dan detail implementasi.
  • Kelas yang seharusnya hanya dipakai dalam kelas pabrik (dalam paket yang sama) harus memiliki konstruktor terbatas paket, karena tidak mungkin untuk memanggilnya langsung dari luar paket.
  • Sebuah internal void beforeRender() metode yang disebut tepat sebelum rendering dan digunakan sebagai pengait di subclass harus dilindungi.
  • SEBUAH void saveGame(File dst) metode yang dipanggil dari kode GUI harus bersifat publik.

(*) Apa sebenarnya enkapsulasi?


243
2017-11-10 10:27



                | highest precedence <---------> lowest precedence
*———————————————+———————————————+———————————+———————————————+———————
 \ xCanBeSeenBy | this          | any class | this subclass | any
  \__________   | class         | in same   | in another    | class
             \  | nonsubbed     | package   | package       |    
Modifier of x \ |               |           |               |       
————————————————*———————————————+———————————+———————————————+———————
public          |              |          |              |      
————————————————+———————————————+———————————+———————————————+———————
protected       |              |          |              |   ✘   
————————————————+———————————————+———————————+———————————————+———————
package-private |               |           |               |
(no modifier)   |              |          |       ✘       |   ✘   
————————————————+———————————————+———————————+———————————————+———————
private         |              |     ✘     |       ✘       |   ✘    

165
2018-01-09 21:42



Aturan mudah. Mulailah dengan menyatakan semuanya pribadi. Dan kemudian maju ke arah publik ketika kebutuhan muncul dan desain menjaminnya.

Ketika memaparkan anggota, tanyakan kepada diri sendiri apakah Anda mengekspos pilihan representasi atau pilihan abstraksi. Yang pertama adalah sesuatu yang ingin Anda hindari karena akan memperkenalkan terlalu banyak ketergantungan pada representasi aktual daripada pada perilaku yang dapat diamati.

Sebagai aturan umum saya mencoba untuk menghindari penerapan metode yang berlebihan oleh subclassing; terlalu mudah mengacaukan logika. Deklarasikan metode perlindungan abstrak jika Anda berniat untuk menimpanya.

Juga, gunakan anotasi @Override saat menimpa untuk mencegah kerusakan ketika Anda melakukan refactor.


130
2017-10-18 20:00



Ini sebenarnya sedikit lebih rumit daripada tampilan grid sederhana. Grid memberi tahu Anda apakah akses diizinkan, tetapi apakah sebenarnya akses itu? Juga, tingkat akses berinteraksi dengan kelas dan pewarisan bersarang dengan cara yang rumit.

Akses "default" (ditentukan oleh tidak adanya kata kunci) juga disebut paket-pribadi. Pengecualian: dalam antarmuka, tidak ada pengubah berarti akses publik; pengubah selain dari publik dilarang. Konstanta Enum selalu bersifat publik.

Ringkasan

Apakah akses ke anggota dengan specifier akses ini diperbolehkan?

  • Anggota adalah private: Hanya jika anggota didefinisikan dalam kelas yang sama dengan kode panggilan.
  • Anggota adalah paket pribadi: Hanya jika kode panggilan ada dalam paket yang segera dilampirkan anggota.
  • Anggota adalah protected: Paket yang sama, atau jika anggota ditentukan dalam superclass kelas yang berisi kode panggilan.
  • Anggota adalah public: Iya nih.

Apa yang berlaku bagi penspesifikasi akses

Variabel lokal dan parameter formal tidak dapat mengambil penentu akses. Karena mereka pada dasarnya tidak dapat diakses ke luar sesuai dengan aturan pelingkupan, mereka secara efektif bersifat pribadi.

Untuk kelas hanya di lingkup atas public dan paket-pribadi diizinkan. Pilihan desain ini mungkin karena protected dan private akan menjadi mubazir pada tingkat paket (tidak ada warisan paket).

Semua penentu akses dimungkinkan pada anggota kelas (konstruktor, metode dan fungsi anggota statis, kelas bertingkat).

Terkait: Aksesibilitas Kelas Jawa

Memesan

Para penspesifikasi akses dapat dipesan secara ketat

publik> dilindungi> paket-pribadi> pribadi

yang berarti bahwa public menyediakan sebagian besar akses, private sangat sedikit. Referensi apa pun yang mungkin pada anggota pribadi juga berlaku untuk anggota paket-pribadi; referensi apa pun ke anggota paket-pribadi berlaku pada anggota yang dilindungi, dan seterusnya. (Memberikan akses kepada anggota yang dilindungi ke kelas lain dalam paket yang sama dianggap sebagai kesalahan.)

Catatan

  • Metode kelas adalah diizinkan untuk mengakses anggota pribadi dari objek lain dari kelas yang sama. Lebih tepatnya, metode kelas C dapat mengakses anggota C pribadi pada objek-objek subclass manapun dari C. Java tidak mendukung pembatasan akses oleh instance, hanya berdasarkan kelas. (Bandingkan dengan Scala, yang mendukungnya menggunakan private[this].)
  • Anda memerlukan akses ke konstruktor untuk membuat objek. Jadi jika semua konstruktor bersifat pribadi, kelas hanya dapat dibangun oleh kode yang tinggal di dalam kelas (biasanya metode pabrik statis atau penginisialisasi variabel statis). Demikian pula untuk konstruktor paket-pribadi atau yang dilindungi.
    • Hanya memiliki konstruktor pribadi juga berarti bahwa kelas tidak dapat disublisensikan secara eksternal, karena Java memerlukan konstruktor subkelas secara implisit atau eksplisit memanggil konstruktor superclass. (Ini bisa, bagaimanapun, mengandung kelas bersarang yang meng-subclassenya.)

Kelas batin

Anda juga harus mempertimbangkan bersarang cakupan, seperti kelas batin. Contoh kerumitannya adalah bahwa kelas batin memiliki anggota, yang dapat mengambil pengubah akses. Jadi Anda dapat memiliki kelas batin pribadi dengan anggota publik; Apakah anggota dapat diakses? (Lihat di bawah.) Aturan umumnya adalah melihat ruang lingkup dan berpikir secara rekursif untuk melihat apakah Anda dapat mengakses setiap level.

Namun, ini cukup rumit, dan untuk detail lengkap, berkonsultasi dengan Spesifikasi Bahasa Jawa. (Ya, sudah ada bug kompiler di masa lalu.)

Untuk merasakan bagaimana interaksi ini, pertimbangkan contoh ini. Adalah mungkin untuk "membocorkan" kelas batin pribadi; ini biasanya peringatan:

class Test {
    public static void main(final String ... args) {
        System.out.println(Example.leakPrivateClass()); // OK
        Example.leakPrivateClass().secretMethod(); // error
    }
}

class Example {
    private static class NestedClass {
        public void secretMethod() {
            System.out.println("Hello");
        }
    }
    public static NestedClass leakPrivateClass() {
        return new NestedClass();
    }
}

Output kompiler:

Test.java:4: secretMethod() in Example.NestedClass is defined in an inaccessible class or interface
        Example.leakPrivateClass().secretMethod(); // error
                                  ^
1 error

Beberapa pertanyaan terkait:


94
2017-09-13 07:38



Sebagai aturan praktis:

  • pribadi: ruang lingkup kelas.
  • default (atau paket-pribadi): cakupan paket.
  • terlindung: lingkup paket + anak (seperti paket, tetapi kita dapat meng-subclass-nya dari paket yang berbeda). Pemodifikasi yang dilindungi selalu menjaga hubungan "orangtua-anak".
  • publik: dimana mana.

Akibatnya, jika kita membagi akses menjadi tiga hak:

  • (Langsung (aktifkan dari metode di dalam kelas yang sama).
  • (Referensi (gunakan metode menggunakan referensi ke kelas, atau melalui sintaks "titik").
  • (Warisan (melalui subclassing).

maka kita memiliki tabel sederhana ini:

+—-———————————————+————————————+———————————+
|                 |    Same    | Different |
|                 |   Package  | Packages  |
+—————————————————+————————————+———————————+
| private         |   D        |           |
+—————————————————+————————————+———————————+
| package-private |            |           |
| (no modifier)   |   D R I    |           |
+—————————————————+————————————+———————————+
| protected       |   D R I    |       I   |
+—————————————————+————————————+———————————+
| public          |   D R I    |    R  I   |
+—————————————————+————————————+———————————+

63
2017-12-18 18:01



Pendek sekali

  • public: dapat diakses dari mana saja.
  • protected: dapat diakses oleh kelas paket yang sama dan subclass yang berada dalam paket apa pun.
  • default (tidak ada pengubah yang ditentukan): dapat diakses oleh kelas dari paket yang sama.
  • private: dapat diakses hanya dalam kelas yang sama.

43
2017-10-27 17:49



Pengubah akses yang paling disalahpahami di Jawa adalah protected. Kita tahu bahwa itu mirip dengan pengubah default dengan satu pengecualian di mana subclass dapat melihatnya. Tapi bagaimana caranya? Berikut ini contoh yang dapat menjelaskan kebingungan:

  • Asumsikan bahwa kita memiliki 2 kelas; Father dan Son, masing-masing dalam paketnya sendiri:

    package fatherpackage;
    
    public class Father
    {
    
    }
    
    -------------------------------------------
    
    package sonpackage;
    
    public class Son extends Father
    {
    
    }
    
  • Mari tambahkan metode terlindungi foo() untuk Father.

    package fatherpackage;
    
    public class Father
    {
        protected void foo(){}
    }
    
  • Metode foo() dapat disebut dalam 4 konteks:

    1. Di dalam kelas yang terletak di paket yang sama di mana foo() didefinisikan (fatherpackage):

      package fatherpackage;
      
      public class SomeClass
      {
          public void someMethod(Father f, Son s)
          {
              f.foo();
              s.foo();
          }
      }
      
    2. Di dalam subclass, pada contoh saat ini melalui this atau super:

      package sonpackage;
      
      public class Son extends Father
      {
          public void sonMethod()
          {
              this.foo();
              super.foo();
          }
      }
      
    3. Pada referensi yang bertipe adalah kelas yang sama:

      package fatherpackage;
      
      public class Father
      {
          public void fatherMethod(Father f)
          {
              f.foo(); // valid even if foo() is private
          }
      }
      
      -------------------------------------------
      
      package sonpackage;
      
      public class Son extends Father
      {
          public void sonMethod(Son s)
          {
              s.foo();
          }
      }
      
    4. Pada referensi yang tipe adalah kelas induk dan itu dalam paket di mana foo() didefinisikan (fatherpackage) [Ini dapat dimasukkan dalam konteks no. 1]:

      package fatherpackage;
      
      public class Son extends Father
      {
          public void sonMethod(Father f)
          {
              f.foo();
          }
      }
      
  • Situasi berikut ini tidak valid.

    1. Pada referensi yang tipe adalah kelas induk dan itu di luar paket di mana foo() didefinisikan (fatherpackage):

      package sonpackage;
      
      public class Son extends Father
      {
          public void sonMethod(Father f)
          {
              f.foo(); // compilation error
          }
      }
      
    2. Non-subkelas di dalam paket subkelas (Subkelas mewarisi anggota yang dilindungi dari induknya, dan menjadikannya pribadi untuk non-subclass):

      package sonpackage;
      
      public class SomeClass
      {
          public void someMethod(Son s) throws Exception
          {
              s.foo(); // compilation error
          }
      }
      

32
2017-11-15 20:06