В Java, почему защищенные члены стали доступными для классов одного и того же пакета?

14

Из официальной документации ...

Модификатор класса пакета подкласс мира 
общедоступный ГГГГ 
защищенный YYYN 
без модификатора 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();
 }
}
jramoyo
источник

Ответы:

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. Доступ по умолчанию «package» полезен в широком диапазоне случаев, потому что классы не всегда являются хорошей единицей инкапсуляции. Различные составные объекты, в которых некоторые объекты действуют как совокупность других объектов, но эти элементы не должны быть публично модифицируемыми, поскольку во всей коллекции есть несколько инвариантов, поэтому коллекция должна иметь повышенный доступ к элементам. У C ++ есть друзья, у Java есть доступ к пакетам.
  2. Теперь область доступа «пакет» в основном не зависит от «защищенной» области. Поэтому вам понадобятся дополнительные спецификаторы доступа только для пакета, только для подклассов, а также для пакета и подклассов. Область действия «пакета» более ограничена, так как набор классов в пакете обычно определен, в то время как подкласс может появляться где угодно. Таким образом, для простоты Java просто включает доступ к пакету в защищенный доступ и не имеет дополнительного спецификатора для подклассов, но не для пакетов. Хотя вы почти всегда должны думать об protectedэтом.
Ян Худек
источник
Не было бы проще, если бы protectedтолько подкласс? Честно говоря, долгое время у меня было впечатление, что это поведение
jramoyo
@jramoyo: Нет, потому что вам все равно нужно как-то сделать комбинированное поведение доступным, что означало бы другой спецификатор.
Ян Худек
6
@jramoyo - в C # protected- только класс и подкласс, а также internalбиблиотека / пакет. Он также имеет protected internalчто эквивалентно Java protected.
Бобсон
@Bobson - спасибо, реализация C # кажется лучшим выбором
jramoyo
5

ИМХО, это было плохое дизайнерское решение в Java.

Просто размышления, но я думаю, что они хотели, чтобы уровни доступа были в строгой прогрессии: private - «package» - protected - public. Они не хотели иметь иерархию, где некоторые поля были бы доступны для пакета, но не для подклассов, некоторые доступны для подклассов, но не для пакета, а некоторые - оба.

Тем не менее, по моему скромному мнению, они должны были пойти другим путем: сказали, что защищенный виден только классу и подклассам, а этот пакет виден классу, подклассам и пакету.

У меня часто есть данные в классе, которые должны быть доступны для подклассов, но не для остальной части пакета. Мне трудно придумать случай, когда обратное было правдой. У меня было несколько редких случаев, когда у меня был набор взаимосвязанных классов, которые должны обмениваться данными, но которые должны обеспечивать безопасность этих данных извне. Хорошо, поместите их в пакет и т. Д. Но у меня никогда не было случая, когда я хочу, чтобы пакет делился данными, но я хочу сохранить его от подклассов. Хорошо, я могу представить себе ситуацию. Я полагаю, если этот пакет является частью библиотеки, которая может быть расширена классами, о которых я ничего не знаю, по тем же причинам, по которым я бы сделал данные в классе частными. Но более распространенным является желание сделать данные доступными только для класса и его детей.

Есть много вещей, которые я храню в тайне между мной и моими детьми, и мы не делимся ими с соседями. Очень мало, что я держу в секрете между мной и соседями и не делюсь с моими детьми. :-)

сойка
источник
0

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

Casey
источник
1
Служебные классы являются скорее примером классов, которые являются внутренними в целом, а не их членами.
Ян Худек
0

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

Один из вариантов использования - при реализации шаблона Memento .

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

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

Тулаинс Кордова
источник
1
Нет, объект памятки не нуждается и не должен иметь никакого доступа к объекту. Это объект, который сериализуется в память и снова десериализуется. А сам сувенир - просто тупая сумка для вещей. Ни один из них не должен иметь более общего доступа к другому.
Ян Худек
@JanHudec Я буквально заявил «это -> один <- из возможных способов достижения паттерна Memento» .
Тулаинс Кордова
И еще один возможный способ достижения модели Memento - сделать все публичным. То есть я не вижу твоей точки зрения.
Томас Эдинг
-1

симметрия?

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

jwenting
источник
1
спасибо, у вас есть пример? Похоже, что это может быть достигнуто с помощью средства доступа «по умолчанию», а не «защищенного»
jramoyo
-1

Иерархия инкапсуляции Java хорошо определена:

Класс -> Пакет -> Наследование

«Защищенный» является более слабой формой конфиденциальности, чем пакет по умолчанию, как решают разработчики Java. Доступ к элементам пакета по умолчанию ограничен подмножеством объектов, которым разрешен доступ к защищенным элементам.

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

С концептуальной точки зрения имеет смысл иметь что-то в java.util, чтобы быть «более дружелюбным» с другим классом в пакете, чем что-то в com.example.foo.bar, который подклассирует это. В первом случае классы могут быть написаны одним и тем же автором или, по крайней мере, программистами из одной организации.

Рич Смит
источник
1
что значит "просто более слабая форма ..." ?
комнат