Чому в Java захищені члени стали доступними для класів одного пакету?


14

З офіційної документації ...

Підклас пакунків класу модифікаторів 
громадський РРРР 
захищений РРРР 
немає модифікатора YYNN 
приватний YNNN 

Справа в тому, що я не можу пригадати, щоб я мав випадок використання, коли мені потрібно було отримати доступ до захищених членів із класу в межах одного пакету.

Якими були причини цього впровадження?

Редагувати: Для уточнення я шукаю конкретний випадок використання, коли і підклас, і клас в одному пакеті потребують доступу до захищеного поля чи методу.

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();
 }
}

Відповіді:


4

шукаючи конкретний випадок використання, коли і підклас, і клас в одному пакеті потребують доступу до захищеного поля чи методу ...

Ну для мене такий випадок використання є скоріше загальним, ніж конкретним, і випливає з моїх уподобань до:

  1. Почніть з максимально суворого модифікатора доступу, вдаючись до більш слабкого (і) лише пізніше, як вважаєте необхідним.
  2. Нехай одиничні тести проживають у тій же упаковці, що і тестований код.

Зверху я можу почати проектувати для своїх об'єктів модифікатори доступу за замовчуванням (я б почав з, privateале це ускладнить тестування одиниць):

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();
        }
    }
}

Примітка боку в наведеному фрагменті, Unit1, Unit2і UnitTestє вкладеними в Exampleдля простоти викладу, але в реальному проекті, я, швидше за все , ці класи в окремих файлах (і UnitTestнавіть в окремому каталозі ).

Тоді, коли виникає необхідність, я б послабив контроль доступу від замовчування до 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();
        }
    }
}

Розумієте, я можу зберігати код тесту одиниці в ExampleEvolvedнезмінному вигляді, оскільки захищені методи доступні з одного пакету, навіть якщо доступ до об'єкта не є підкласом .

Менше змін потрібно => більш безпечна модифікація; врешті-решт я змінив лише модифікатори доступу, і я не змінив, які методи Unit1.doSomething()і Unit2.doSomething()робити, тому цілком природно очікувати, що тестовий код модуля продовжить працювати без змін.


5

Я б сказав, що це має дві частини:

  1. Доступ за замовчуванням, "пакунок", корисний у широкому діапазоні випадків, оскільки класи не завжди є доброю одиницею інкапсуляції. Різні складові об’єкти, де один об’єкт виступає як колекція інших об'єктів, але елементи не повинні бути публічно модифікованими, оскільки в цілій колекції є деякі інваріанти, тому колекція повинна мати підвищений доступ до предметів. У C ++ є друзі, у Java є пакетний доступ.
  2. Тепер область доступу "пакету" в основному не залежить від області "підкласу" (захищеного). Тому вам знадобляться додаткові специфікатори доступу лише для пакунків, підкласів та пакунків та підкласів. Область "пакет" більш обмежена, оскільки набір класів у пакеті, як правило, визначений, тоді як підклас може з'являтися в будь-якому місці. Отже, щоб зробити це просто, Java просто включає доступ до пакету в захищений доступ і не має додаткових специфікаторів для підкласів, але не-пакет. Хоча майже завжди слід думати protectedяк саме про це.

Чи не було б простіше, якщо protectedтільки підклас? Чесно кажучи, довгий час у мене було враження, що така поведінка
jramoyo

@jramoyo: Ні, тому що вам все-таки потрібно зробити якось доступною комбіновану поведінку, що означатиме інший специфікатор.
Ян Худек

6
@jramoyo - У C # - protectedце лише клас та підклас і internalє бібліотекою / пакетом. Він також має protected internalеквівалент Java protected.
Бобсон

@Bobson - дякую, реалізація C # здається кращим вибором
jramoyo

5

IMHO, це було поганим дизайнерським рішенням на Java.

Просто спекулюю, але я думаю, що вони хотіли, щоб рівні доступу були суворо прогресували: приватний - "пакет" - захищений - публічний. Вони не хотіли мати ієрархію, де деякі поля були б доступні для пакета, але не підкласи, деякі доступні для підкласів, але не пакет, а деякі обидва.

Але, на мою скромну думку, вони повинні були піти іншим шляхом: Сказав, що захищена видима лише для класів і підкласів, і цей пакет видно для класу, підкласів і пакета.

У мене часто є дані в класі, які повинні бути доступними для підкласів, але не для решти пакету. Мені важко думати про випадок, коли було зворотним. У мене були кілька рідкісних випадків, коли у мене був пакет взаємопов'язаних класів, яким потрібно обмінюватися даними, але які повинні захищати ці дані поза межами пакету. Так добре, покладіть їх у пакет і т. Д. Але у мене ніколи не було випадку, коли я хотів би, щоб пакет обмінювався даними, але я хотів би зберегти його від підкласів. Гаразд, я можу уявити, що ситуація складається. Я припускаю, що якщо цей пакет є частиною бібліотеки, яку можна розширити класами, про які я нічого не знаю, з тих же причин я б зробив дані в класі приватними. Але набагато частіше хочеться зробити дані доступними лише для класу та його дітей.

Є багато речей, які я залишаю приватними між мною та дітьми, і ми не ділимось із сусідами. Дуже мало, що я залишаюсь приватним між мною та сусідами і не ділюсь зі своїми дітьми. :-)


0

Хороший приклад, який одразу приходить в голову - це утилітні класи, які багато використовуються в пакеті, але ви не бажаєте публічного доступу (поза кадром-завантаження зображень з диска, класів створення / руйнування тощо) .) замість того , щоб все [еквівалент в Java friend access modifier or idiomв C++] кожному іншому класі все автоматично доступно.


1
Корисні класи - це більше приклад класів, які є внутрішніми, а не їх членами.
Ян Худек

0

Випадки використання для модифікатора доступу до захищеного пакету / пакету аналогічні випадкам для модифікаторів доступу для друзів у C ++.

Один випадок використання - це при впровадженні схеми Memento .

Об'єкту memento потрібно отримати доступ до внутрішнього стану об'єкта, утримувати його, щоб він служив контрольною точкою для скасування операцій.

Оголошення класу в одному пакеті є одним із можливих способів досягнення шаблону Memento, оскільки у Java немає модифікатора доступу "друг" .


1
Ні, об'єкт memento не потребує і не повинен мати доступу до об'єкта. Саме об’єкт серіалізується в пам’яті і знову дезаріалізується. А сам спогад - це просто німий мішок власності. Ні один не повинен мати більше доступу громадськості до іншого.
Ян Худек

@JanHudec я заявив буквально "є -> один <- з можливих способів досягнення схеми" Мементо " .
Тулен Кордова

І ще один можливий спосіб досягти моделі Memento - оприлюднити все. Тобто я не бачу вашої суті.
Томас Едінг

-1

симетрія?

Такий доступ рідко потребує, через що так рідко використовується доступ за замовчуванням. Але іноді фреймворки хочуть його для згенерованого коду, коли класи обгортки розміщуються в одному пакеті, який безпосередньо взаємодіє з вашими класами, переходячи до членів замість аксесуарів з міркувань продуктивності. Подумайте, GWT.


1
дякую, у вас є приклад? звучить так, що це може бути досягнуто "за замовчуванням" аксесуаром, а не "захищеним"
jramoyo

-1

Ієрархія інкапсуляції Java чітко визначена:

Клас -> Пакет -> Спадщина

"Захищений" - це лише слабша форма конфіденційності, ніж пакети за замовчуванням, як вирішили дизайнери Java. Доступ до пакунків за замовчуванням обмежений лише підмножиною об'єктів, яким дозволено отримати доступ до захищених елементів.

З точки зору математики та реалізації має багато сенсу мати ваші набори сутностей, яким дозволено отримувати доступ до речей, які потрібно вкладати. (Ви не можете вкладати набір доступу до пакета всередині вашого набору захищеного доступу, оскільки класи дозволяється успадковувати інші пакети).

З концептуальної точки зору є сенс мати щось у java.util, щоб бути "дружнішим" з іншим класом у пакеті, ніж щось у com.example.foo.bar, що це підкласифікує. У першому випадку класи, ймовірно, будуть написані тим самим автором або, принаймні, кодерами з однієї організації.


1
що означає "просто слабша форма ..." ?
гнат
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.