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

Ответы:


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++] любого другого класса, все автоматически доступно.


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

0

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

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

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

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


1
Нет, объект памятки не нуждается и не должен иметь никакого доступа к объекту. Это объект, который сериализуется в память и снова десериализуется. А сам сувенир - просто тупая сумка для вещей. Ни один из них не должен иметь более общего доступа к другому.
Ян Худек

@JanHudec Я буквально заявил «это -> один <- из возможных способов достижения паттерна Memento» .
Тулаинс Кордова

И еще один возможный способ достижения модели 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.