Зачем нужен protected
Перейти к содержимому

Зачем нужен protected

  • автор:

Методы доступа. Наиболее популярные ситуации

Статья в первую очередь расчитана на начинающих разработчиков, либо для тех, кто только начинает переходить от процедурного стиля программирования к ООП, посему матерых гуру просьба не вгонять в минуса 🙂

Права доступа к свойствам и методам — это на первый взгляд всего лишь три слова: private, protected и public. Но что скрывается за ними? Какие преимущества это дает в разработке? И как их правильно использовать? Здесь, как и во всех других аспектах программирования, без практики не разобраться…

Одна из трех основных концепций ООП — наследование (другие две: инкапсуляция и полиморфизм). Вобщем-то именно для нее и были реализованы права доступов. Основанная идея наследования: Дочерний объект, при наследовании (extend) родителя перенимает себе все родительские методы и свойства, а так же может обзавестись своими собственными. Понимая эту базу, можно перейти в всему что находится ниже…

Private — объявляет метод или свойство доступным только в том классе в котором он присутствует. Тоесть к private методам и свойствам мы не можем обращаться ни из объектов, ни из дочерних классов.

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

Public — публичный. Классы и методы, объявленные public, могут быть доступны как внутри самого класса, так и в дочерних классах и в объектах, реализовавших класс.

Сразу хочу заметить, что при наследовании, методы доступа изменяться могут только к более лояльным. тоесть в следующей последовательности, но не обратно: private → protected → public

Так же методы могут быть final тоесть такими, которые невозможно переопределить в классах потомках.

Вобщем-то все методы доступа используются исключительно для самодокументации кода и не несут никакой логической составляющей, так что и без них жизнь только тяжела, но не невозможна, что доказывает РНР4, в котором все методы и свойства были публичными…

Практика

Иногда случаются ситуации, когда этих методов доступа недостаточно. Тоесть, например, мы можем хотеть иметь доступ из объекта на чтение какого-то свойства, но при этом не иметь возможности в него писать. Самое простое решение: объявить свойство public и добавить комментарий /* только для чтения */, но про комментарий можно ненароком забыть и испортить логику поведения программы, вклинившись с нестандартным значением посреди выполнения. тогда приходит время использовать геттеры (getter\’s). Геттер — не что иное, как метод класса, реализующий исключительную возможность читать не публичные свойства из объекта. Вот пример:

class A private $a = 7;//мы не можем читать и писать в это свойство из объекта, реализующего этот класс

public function getA() < //публичный метод будет доступен объекту для обращения
return $this->a; //внутри класса мы можем получать доступ к приватным свойствам
>
>

$obj = new A();
echo $obj->getA();//мы получили значение приватной переменной $a

Похожим способом ведут себя и сеттеры (setter\’s), когда нам необходимо иметь возможность установить значение переменной, но не читать ее напрямую, так как она, к примеру, должна быть преобразована прежде чем быть использованной. Пример метода сеттера:

//.
public funtion setA($value) < //метод будет доступен для объекта
$this->a = $value; //приватное свойство $a может быть установленное внутри класса, но не доступно для прямого влияния из объекта
>
//.

Еще одним вариантом реализации доступа к методам, когда метод должен быть отовсюду доступен только для чтения, является введение \«псевдо-свойства\»:

class A public function getValue() static $value;

if (empty($value)) $value = //. тут значение создается по каким-то известным параметрам и повлиять извне на него мы никак не сможем
>

в примере выше, класс А будет обладать псевдо-свойством $value. Псевдо — потому что оно реализуется исключительно через метод, а доступ к нему возможен только на чтение. Еще можете заметить что я использовал паттерн \«ленивой инициализации\», что бы отложить создание свойства до последнего момента и заодно как бы \«закешировать\» его. Где это можно применить, хорошо проиллюстрировано в соседнем топике об ООП в РНР.

Хорошей практикой является сокрытие всех свойств методом private и, в зависимости от нужд, создавать для них сеттеры или геттеры, но нужно быть внимательным, что если для свойства существует и сеттер и геттер, а дополнительной логики обработки данных нет, то не проще ли их убрать, а свойство сделать публичным? 🙂

Когда использовать protected, private, public и static методы?

Я джуниор программист, осваиваю потихоньку ООП. Я понимаю что означают protected, private, public и static методы, но я не могу понять того, зачем и в каких случаях они используются. Хотелось бы получить исчерпывающий ответ по этому вопросу.

  • Вопрос задан более трёх лет назад
  • 15996 просмотров

Комментировать
Решения вопроса 1
Full-stack developer (Symfony, Angular)

Есть такая штука в ООП как инкапсуляция. С этого стоит начать и в принципе можно было бы закончить, если бы не static и не protected.

public
Публичные методы класса. Логично да? Какие методы у нас должны быть публичными — те которые составляют интерфейс класса. То есть для, например, класса кеширования у нас должен быть такой интерфейс:

interface Cache

Давайте введем понятие «клиентский код». Это тот код который будет использовать ваш код, ваши объекты. Что он должен знать о реализации ваших классов? Да ничего он знать не должен. Он должен знать только как им пользоваться. Это как раз таки интерфейс и регламентирует. Мол «этот объект позволяет вам брать/сохранять/удалять данные в/из кэша. Клиентский код не знает где кэш находится, есть ли он вообще. да и ему и не нужно.

private
Внутренние методы классов. Те которые не входят в интерфейс но нужны, скажем, для уменьшения дублирования реализации.

class FileCache implements Cache< public function has($name) < return is_file($this->getCacheFilePath($name)); > public function get($name) < if (!$this->has($name)) < return null; >return unserialize(file_get_contents($this->getCacheFilePath($name))); > private function getCacheFilePath($name) < return $this->cacheDir . '/' . $name; > >

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

static
Статические методы, это всяческие методы хелперы. Например в языках типа Java есть такое понятие как именованные конструкторы. Мол когда для конструирования объектов нужно много данных переделать или что-то еще, для удобства заводят статический метод какой а в нем уже реализуют какую-то логику. Желательно вообще что бы статический метод не содержал много логики, но. всякое бывает. Вообще тут стоит еще поподробнее почитать о том как себя ведут статические методы и свойства, в чем их особенность и т.д. Попробуйте тот же сингелтон сделать к примеру.

class Template($content) < public static fromFile($path) < return new static(file_get_contents($path)); >>

Вот. Возможно человеки вроде FanatPHP разгромят мой ответ, или дополнят его.

Так же если вам хочется научиться правильно составлять интерфейсы классов, следует почитать про low coupling и high cohesion. Эти два принципа должны заставить вас соблюдать баланс и держать в интерфейсе класса только то, что там должно быть. Так же к вопросу о наследовании можно подключить принцип подстановки Барбары Лисков. Даже на той же википедии почитайте. А там если станет интересно в принципе можно поискать интересные материалы по GRASP и SOLID.

Зачем в java нужен protected конструктор

protected конструктор позволяет запретить создание экземпляра базового класса. В производных классах этот конструктор будет доступен, а стало быть, можно будет создавать потомков.

Данный подход применим во многих ООП языках, не только в java.

Отслеживать
ответ дан 1 июл 2016 в 13:20
28.5k 12 12 золотых знаков 58 58 серебряных знаков 118 118 бронзовых знаков

насколько я понимаю, не совсем позволяет запретить создание экземпляра базового класса, а создавать экземпляр базового класса напрямую именно этим конструктором. Класс вполне может иметь статическую функцию внутри, которой бы вызывался этот protected конструктор, либо вызывать этот конструктор из другого, публичного конструктора

1 июл 2016 в 13:23
А чем этот подход отличается от объявления класса абстракным?
1 июл 2016 в 13:24
@MaxArt, тем, что экземпляр класса все еще можно создать, экземпляр абстрактного класса — нельзя
1 июл 2016 в 13:24
Было бы круто увидеть в ответе пару примеров того как можно это применить)
1 июл 2016 в 13:27

А экземпляр класса с protected конструктором получается все таки можно создавать в классах того же пакета?

1 июл 2016 в 13:35

protected конструктор нужен для вызова его из наследников, но сокрытия от доступа извне.
Отличный пример — protected конструктор класса Throwable.

Throwable(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace) 

Последний параметр указывает, нужно ли писать стектрейс (что является очень затратной операцией). Можно наследоваться от Throwable и, вызывая этот конструктор родителя, получать очень легковесные «сигнальные» исключения.

Модификаторы private, protected, public в Java

Модификаторы доступа private, protected, public ставятся перед именем класса, метода или поля и ограничивают доступ к нему. К локальным переменным модификаторы доступа не применимы.
Помимо этих трех явных модификаторов, есть еще так называемый default-модификатор, или модификатор по умолчанию, иначе говоря — это отсутствие всякого модификатора. Но это отсутствие тоже подразумевает свои правила доступа (видимость только внутри пакета).

Зачем нужны модификаторы доступа

Модификаторы доступа существуют для того, чтобы сделать код надежнее и защищеннее. Нужно максимально ограничивать видимость своих классов, методов и полей, и открывать их только там, где это действительно необходимо. Если вы откроете что-то лишнее, то другой разработчик (или даже вы сами) может по ошибке воспользоваться открытым классом/методом. Чем это чревато? А тем, что если в дальнейшем вы исправите свой код (отвечающий за внутреннюю реализацию, но открытый для пользования извне), то код другого программиста перестанет работать, так как опирается на ваш код. Открывать нужно только то, что вы планируете поддерживать и что будет стабильно работать (без изменения контракта) во всех последующих версиях. Все остальное — внутренняя реализация, которая касается только вас и может меняться, ее никто не должен использовать.

Нормально сделать видимым, например, один класс вашего пакета и только методы, предназначенные для внешнего использования (методы API). Все остальное скрыть. Это называется инкапсуляцией (скрытием реализации).

Правила доступа

На картинке показаны правила доступа к полю или методу с конкретным модификатором (последний столбец — про модули, они появились в Java 9):

Модификаторы доступа в Java

Модификатор private

Это самый ограничивающий модификатор. К полям и методам, помеченным ключевым словом private, можно обратиться только из того же класса, где они находятся.

Допустим у нас есть класс A с private полем privateVar и с private методом privateMethod(). Из класса A мы можем обращаться к полю, см. обращение this.privateVar:

package accessmodifiers.priv; public class A < private int privateVar = 1; private void privateMethod() < System.out.println("A private method is printing " + this.privateVar); >public static void main(String[] args) < A a = new A(); a.privateVar = 2; a.privateMethod(); >>

А теперь попробуем обратиться к этому полю и методу из класса B, код не скомпилируется:

package accessmodifiers.priv; public class B < void testAccess() < A a = new A(); a.privateVar = 10; // illegal a.privateMethod(); // illegal >>

Вышеприведенный код выдает ошибки компиляции:

The field A.privateVar is not visible The method privateMethod() from the type A is not visible

Иногда возникает вопрос

Можно ли в классе A получить доступ к private методам и полям другого объекта A (не текущего this)?

Да, можно. Обратите внимание на функцию main() из класса A, в которой создается новый объект A и идет обращение к его методам и полям (не через this):

package accessmodifiers.priv; public class A < private int privateVar = 1; private void privateMethod() < System.out.println("A private method is printing " + this.privateVar); >public static void main(String[] args) < A a = new A(); a.privateVar = 2; a.privateMethod(); >>

Как показано выше, мы обращаемся в методе main() к private полю privateVar другого объекта A, и это законно.

В Java ограничения доступа применимы на уровне класса, а не на уровне объекта (не обязательно, чтоб обращение шло к тому же экземпляру, главное, что он в том же классе). В Scala, например, существуют модификаторы доступа на уровне объекта

Можно ли переопределить private метод?

Нельзя, метод в подклассе не будет иметь никакого отношения к методу в суперклассе, так как private метод нигде не виден. Давайте попытаемся унаследоваться от класса A и «переопределить» private метод privateMethod():

public class SubA extends A < private void privateMethod() < System.out.println("B private method is printing "); >>

Попробуем создать объект SubA и вызвать privateMethod() на A:

A a=new SubA(); a.privateMethod();

Как видите, срабатывает метод privateMethod() класса A, то есть переопределения не происходит:

A private method is printing 2

Это происходит потому, что метод privateMethod() класса SubA не переопределяет метод privateMethod() класса A, а является независимым методом.

Модификатор default

Если мы не ставим никакого модификатора доступа перед методом, полем или классом, то этот метод/поле/класс видимы из кода только внутри пакета, в котором они находятся.

Давайте продемонстрируем это. Создадим снова класс A в пакете .def:

package ru.sysout.accessmodifiers.def; public class A < int defaultVar = 1; void defaultMethod() < System.out.println("A default method is printing " + this.defaultVar ); >>

И создадим в этом же пакете класс B, из которого будем пытаться получить доступ к полям и методам A, как и раньше:

package ru.sysout.accessmodifiers.def; public class B < void testAccess() < A a = new A(); a.defaultVar = 10; // legal a.defaultMethod(); // legal >>

В этот раз код компилируется, все в порядке — доступ есть.

Если бы класс B находится в другом пакете (отличном от ru.sysout.accessmodifiers.def, в том числе в подпакете), то доступа бы не было.

Модификатор protected

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

Снова создадим класс A с protected полем и методом:

package ru.sysout.accessmodifiers.prot; public class A < protected int protectedVar = 1; protected void protectedMethod() < System.out.println("A protected method is printing " + this.protectedVar); >>

Создадим в другом пакете класс C — наследника класса A и попытаемся получить доступ к полям методам класса A из класса C:

package ru.sysout.accessmodifiers.prot.sub; import ru.sysout.accessmodifiers.prot.A; public class C extends A < void testAccess(A a, C c) < this.protectedVar = 1;// legal this.protectedMethod();// legal // a.protectedVar = 10; // illegal c.protectedVar = 10; // legal // a.protectedMethod(); // illegal c.protectedMethod(); // legal >>

Как показано выше, обращение к полю и методу через this работает из другого пакета.

Также работает обращение ко всем другим экземплярам типа C, но к другим экземплярам типа A обращение не работает.

Модификатор public

Тут все просто — к полю и методу с модификатором public имеет доступ любой код. Давайте еще раз перепишем класс A:

package ru.sysout.accessmodifiers.pub; public class A < public int publicVar = 1; public void publicMethod() < System.out.println("A public method is printing " + this.publicVar); >>

И обратимся к его полю и методу из класса B, который находится в другом пакете и никакого отношения к классу A не имеет:

package ru.sysout.accessmodifiers.pub.sub; import ru.sysout.accessmodifiers.pub.A; public class B < void testAccess() < A a = new A(); a.publicVar = 10; // legal a.publicMethod(); // legal >>

Все получилось, обращение работает.

Какой модификатор выбрать?

Правило выбора модификатора такое — надо по возможности выбирать:

То есть надо максимально ограничивать видимость члена класса. Сначала надо попробовать сделать все private, и при необходимости открывать видимость.

Итог

Мы рассмотрели тонкости использования модификаторов доступа. Код примеров можно посмотреть на GitHub.

Автор sysout Опубликовано 26.09.2018 16.11.2022 Рубрики Core Java

Модификаторы private, protected, public в Java: 2 комментария

В разделе «Может ли объект A получить доступ к private методам и полям другого объекта A?» код не соответствует смыслу написанного — пожалуйста исправьте его

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *