Di Jawa, mengapa anggota yang dilindungi dapat diakses oleh kelas dari paket yang sama?

14

Dari dokumentasi resmi ...

Paket Kelas Pengubah Dunia Subkelas 
YYYY publik 
melindungi YYYN 
tidak ada pengubah YYNN 
YNNN pribadi 

Masalahnya, saya tidak ingat memiliki use case di mana saya perlu mengakses anggota yang dilindungi dari kelas dalam paket yang sama.

Apa alasan di balik implementasi ini?

Sunting: Untuk memperjelas, saya sedang mencari kasus penggunaan khusus di mana baik subclass dan kelas dalam paket yang sama perlu mengakses bidang atau metode yang dilindungi.

package some.package;
public class A {
 protected void protectedMethod(){
  // do something
 }
}

package another.package;
public class B extends A{
 public void someMethod(){
  // accessible because B is a subclass of A
  protectedMethod();
 }
} 

package some.package;
public class C {
 public void anotherMethod(){
  // accessible because C is in the same package as A
  protectedMehtod();
 }
}
jramoyo
sumber

Jawaban:

4

mencari kasus penggunaan tertentu di mana subclass dan kelas dalam paket yang sama perlu mengakses bidang atau metode yang dilindungi ...

Bagi saya, use case semacam itu agak umum daripada spesifik, dan itu berasal dari preferensi saya untuk:

  1. Mulailah dengan pengubah akses seketat mungkin, menggunakan yang lebih lemah hanya nanti jika dianggap perlu.
  2. Minta unit test berada dalam paket yang sama dengan kode yang diuji.

Dari atas, saya dapat mulai mendesain objek saya dengan pengubah akses default (saya akan mulai dengan privatetetapi itu akan mempersulit pengujian unit):

public class Example {
    public static void main(String [] args) {
        new UnitTest().testDoSomething(new Unit1(), new Unit2());
    }

    static class Unit1 {
        void doSomething() {} // default access
    }
    static class Unit2 {
        void doSomething() {} // default access
    }

    static class UnitTest {
        void testDoSomething(Unit1 unit1, Unit2 unit2) {
            unit1.doSomething();
            unit2.doSomething();
        }
    }
}

Catatan di potongan di atas, Unit1, Unit2dan UnitTestyang bersarang dalam Exampleuntuk kesederhanaan presentasi, tetapi dalam proyek nyata, saya kemungkinan akan memiliki kelas-kelas dalam file terpisah (dan UnitTestbahkan dalam direktori terpisah ).

Kemudian, ketika suatu kebutuhan muncul, saya akan melemahkan kontrol akses dari standar menjadi protected:

public class ExampleEvolved {
    public static void main(String [] args) {
        new UnitTest().testDoSomething(new Unit1(), new Unit2());
    }

    static class Unit1 {
        protected void doSomething() {} // made protected
    }
    static class Unit2 {
        protected void doSomething() {} // made protected
    }

    static class UnitTest {
        // ---> no changes needed although UnitTest doesn't subclass
        // ...and, hey, if I'd have to subclass... which one of Unit1, Unit2?
        void testDoSomething(Unit1 unit1, Unit2 unit2) {
            unit1.doSomething();
            unit2.doSomething();
        }
    }
}

Anda lihat, saya dapat menjaga kode tes unit ExampleEvolvedtidak berubah karena metode yang dilindungi dapat diakses dari paket yang sama, meskipun mengakses objek bukan sub-kelas .

Lebih sedikit perubahan yang diperlukan => modifikasi yang lebih aman; setelah semua saya mengubah hanya pengubah akses dan saya tidak memodifikasi metode Unit1.doSomething()dan Unit2.doSomething()melakukan apa, jadi itu wajar untuk mengharapkan kode tes unit untuk terus berjalan tanpa modifikasi.

agas
sumber
5

Saya akan mengatakan ini memiliki dua bagian:

  1. Akses standar, "paket", berguna dalam berbagai kasus, karena kelas tidak selalu merupakan unit enkapsulasi yang baik. Berbagai objek komposit di mana beberapa objek bertindak sebagai koleksi objek lain, tetapi item tidak boleh dimodifikasi untuk umum, karena ada beberapa invarian di seluruh koleksi, sehingga koleksi harus memiliki akses tinggi ke item. C ++ punya teman, Java punya akses paket.
  2. Sekarang lingkup akses "paket" pada dasarnya tidak tergantung pada lingkup "subclass" (dilindungi). Jadi Anda akan membutuhkan penentu akses tambahan untuk paket saja, hanya subkelas dan paket dan subkelas. Ruang lingkup "paket" lebih terbatas karena set kelas dalam suatu paket biasanya pasti sementara subkelas dapat muncul di mana saja. Jadi untuk menjaga hal-hal sederhana, Java cukup menyertakan akses paket dalam akses yang dilindungi dan tidak memiliki specifier tambahan untuk subclass-tetapi-bukan-paket. Meskipun Anda hampir selalu harus berpikir protectedseperti itu.
Jan Hudec
sumber
Bukankah lebih sederhana jika protectedhanya subkelas? Jujur, untuk waktu yang lama, saya mendapat kesan bahwa itulah perilaku
jramoyo
@jramoyo: Tidak, karena Anda masih harus membuat perilaku gabungan tetap tersedia, yang berarti specifier lain.
Jan Hudec
6
@jramoyo - Dalam C #, protectedhanya kelas dan subkelas, dan internalperpustakaan / paket-lebar. Ini juga memiliki protected internalyang setara dengan Java protected.
Bobson
@ Bobson - terima kasih, implementasi C # sepertinya pilihan yang lebih baik
jramoyo
5

IMHO, ini adalah keputusan desain yang buruk di Jawa.

Hanya berspekulasi, tapi saya pikir mereka ingin tingkat akses berada dalam perkembangan yang ketat: privat - "paket" - dilindungi - publik. Mereka tidak ingin memiliki hierarki, di mana beberapa bidang akan tersedia untuk paket tetapi tidak subclass, beberapa tersedia untuk subclass tetapi tidak paket, dan beberapa keduanya.

Namun, menurut pendapat saya yang sederhana mereka seharusnya pergi ke arah lain: Mengatakan bahwa dilindungi hanya dapat dilihat oleh kelas dan subkelas, dan paket tersebut dapat dilihat oleh kelas, subkelas, dan paket.

Saya sering memiliki data di kelas yang harus dapat diakses oleh subclass, tetapi tidak untuk sisa paket. Saya sulit sekali memikirkan kasus di mana kebalikannya benar. Saya memiliki beberapa kesempatan langka di mana saya memiliki bundel kelas yang saling terkait yang perlu berbagi data tetapi yang harus menjaga data tersebut aman dari luar bundel. Jadi oke, letakkan di paket, dll. Tapi saya tidak pernah punya kasus di mana saya ingin paket untuk berbagi data, tapi saya ingin menyimpannya dari sub-kelas. Oke, saya bisa membayangkan situasi akan datang. Saya kira jika paket ini adalah bagian dari perpustakaan yang mungkin diperluas oleh kelas yang saya tidak tahu, karena banyak alasan yang sama saya akan membuat data di kelas pribadi. Tetapi jauh lebih umum jika ingin menyediakan data hanya untuk kelas dan anak-anaknya.

Ada banyak hal yang saya rahasiakan antara saya dan anak-anak saya dan kami tidak berbagi dengan tetangga. Sangat sedikit yang saya rahasiakan antara saya dan tetangga dan tidak berbagi dengan anak-anak saya. :-)

Jay
sumber
0

Contoh yang baik yang langsung terlintas dalam pikiran adalah kelas utilitas yang banyak digunakan dalam paket tetapi Anda tidak ingin akses publik (di belakang layar-gambar-memuat-dari-disk, kelas menangani-penciptaan / penghancuran, dll .) bukannya membuat segalanya [Jawa setara friend access modifier or idiomdi C++] setiap kelas yang lain semuanya tersedia secara otomatis.

Casey
sumber
1
Kelas utilitas lebih merupakan contoh kelas yang bersifat internal secara keseluruhan daripada anggota mereka.
Jan Hudec
0

Kasing penggunaan untuk pengubah akses paket / dilindungi mirip dengan yang untuk pengubah akses teman di C ++.

Satu kasus penggunaan adalah ketika menerapkan pola Memento .

Objek kenang-kenangan perlu mengakses keadaan internal suatu objek, untuk menahannya, untuk berfungsi sebagai pos pemeriksaan untuk membatalkan operasi.

Mendeklarasikan kelas dalam paket yang sama adalah salah satu cara yang mungkin untuk mencapai pola Memento, karena Java tidak memiliki pengubah akses "teman" .

Tulains Córdova
sumber
1
Tidak, objek memento tidak perlu dan tidak boleh memiliki akses ke objek. Ini adalah objek yang membuat cerita bersambung ke dalam kenang-kenangan dan deserializes lagi. Dan kenang-kenangan itu sendiri hanyalah tas properti bodoh. Keduanya tidak harus memiliki lebih dari akses publik ke yang lain.
Jan Hudec
@JanHudec Saya menyatakan secara harfiah "adalah -> satu <- dari cara yang mungkin untuk mencapai pola Memento" .
Tulains Córdova
Dan cara lain yang mungkin untuk mencapai pola Memento adalah membuat semuanya publik. Yaitu, saya gagal memahami maksud Anda.
Thomas Eding
-1

simetri?

Jarang membutuhkan akses seperti itu, itulah sebabnya akses standar jarang digunakan. Tetapi kadang-kadang kerangka kerja menginginkannya untuk kode yang dihasilkan, di mana kelas pembungkus ditempatkan dalam paket yang sama yang berinteraksi dengan kelas Anda secara langsung, pergi ke anggota, bukan pengakses untuk alasan kinerja .. Pikirkan GWT.

jwenting
sumber
1
terima kasih, apakah Anda punya contoh? Kedengarannya seperti itu dapat dicapai oleh accessor "default" daripada "dilindungi"
jramoyo
-1

Hirarki enkapsulasi Java didefinisikan dengan baik:

Kelas -> Paket -> Warisan

"Dilindungi" hanyalah bentuk privasi yang lebih lemah daripada paket-default seperti yang ditentukan oleh desainer Java. Akses ke item bawaan-paket terbatas pada subset entitas yang diizinkan untuk mengakses item yang dilindungi.

Masuk akal dari sudut pandang matematika dan implementasi untuk memiliki set entitas Anda yang diizinkan mengakses hal-hal yang akan disarangkan. (Anda tidak dapat menyarangkan set akses-paket Anda di dalam set akses-terlindungi Anda karena kelas diizinkan mewarisi dari paket lain).

Masuk akal dari sudut pandang konseptual untuk memiliki sesuatu di java.util untuk menjadi "lebih ramah" dengan kelas lain dalam paket daripada sesuatu di com.example.foo.bar yang mensubklasifikasikan. Dalam kasus sebelumnya, kelas-kelas cenderung ditulis oleh penulis yang sama, atau setidaknya coders dari organisasi yang sama.

Rich Smith
sumber
1
apa artinya "hanya bentuk yang lebih lemah ..." ?
nyamuk