Урок 16: Модификаторы доступа (видимости) в Kotlin. public, private, protected, internal

. Инкапсуляция. Модификаторы доступа 1 - Android [Kotlin] для начинающих

Основные модификаторы доступа

Модификаторы доступа это определенные ключевые слова в языке, с помощью которых можно устанавливать уровень видимости для класса, переменной или метода. Под уровнем видимости я имею в виду буквально будет ли видна переменная или функция, из другого класса или пакета. Расширение или ограничение видимости данных нужно для правильного проектирования архитектуры программы, соблюдая основные принципы объектно-ориентированного программирования. В том числе инкапсуляцию. О об этом принципе ООП будет чуть ниже.

Сейчас пройдемся по основным модификаторам доступа:

  • public
  • private
  • protected
  • internal

public

В Kotlin этот модификатор проставляется по умолчанию для всех классов, методов или переменных. Как написано в неофициальной русскоязычной документации по языку: “Код данного объявления будет виден из космоса”. То есть все данные будут публичными, если не указывать никакой модификатор. И доступ к ним возможен из любого места проекта. Другими словами обратиться к этим данным можно из любого класса или пакета, импортировав их в требуемый файл.

private

Объявленные данные с этим модификатором доступны только в рамках файла. Либо в рамках класса, если данные помечены как приватные внутри него.

protected

Данные с этим модификатором видны в классе, в котором они определены и в классах-наследниках.

internal

Данные с этим модификатором доступны в любой части модуля, в котором они определены. С модулями будем разбираться в рамках серии уроков по Android, поэтому пока по нему примеров не будет.

Применение

Переходим к практике. Описывать будем сущность путеводителя для путешествующих автостопом по Галактике.

Создаем класс HitchhikersGuide.

class HitchhikersGuide()

Итак, в Kotlin, не указывая модификатор, элемент по умолчанию становится публичным. Если навести курсор на название класса, увидим его полное объявление с модификатором public. То же самое увидим, если объявим поле и функцию в теле класса. Переменная title и метод chooseArticle().

class HitchhikersGuide() {

   val title = ""

   fun chooseArticle() {
	   println("Open catalog")
   }

}

Причем можно принудительно добавить ключевое слово public перед объявлением и ничего принципиально не изменится. Только то, что студия разработки подсветит серым и предложит удалить его как повторяющийся модификатор. Воспользуемся этим предложением.

fun main() {

   val guide = HitchhikersGuide()
   guide.title = "Don't panic"
   guide.chooseArticle()

}

Мы имеем доступ ко всем публичным данным класса, так и задумано. С помощью модификатора private можно ограничивать этот доступ до уровня файла или класса. Например, сделаем приватным поле title. Теперь это поле доступно только в рамках класса HitchhikersGuide. Соответственно, результат будет такой же, если сделать приватным и метод.

При наведении на ошибку в рабочем файле нам говорят, что нет доступа к приватной переменной или методу. Если открыть контекстное меню со всеми предлагаемыми вариантами исправления, увидим две подсказки. Можно сделать данные публичными (просто удалив ключевое слово private) или пометить их как internal. У нас сейчас нет разделения на модули и все происходящее считаем единым целым модулем main. Поэтому эти данные будут видны везде. Модуль вы можете увидеть слева в иерархии проекта, он по умолчанию называется main и подсвечивается квадратом на папке.

Также приватным можно объявить и весь класс. Сделаем это и уже будет невозможным создание объекта. А также автоматически закрывается доступ ко всей внутрянке класса.

Область видимости

Закомментируем пока этот код, оставим публичным класс и переменную title. Переместимся в файл HitchhikersGuide. Для демонстрации создадим здесь другой вспомогательный класс Support. Внутри сделаем публичную функцию в которой будем получать и распечатывать данные класса HitchhikersGuide. Выведем в консоль название класса и значение переменной title. Как видите, метод chooseArticle() недоступен – его область видимости ограничивается классом.

class Support {

   fun printInfo() {
	   println(HitchhikersGuide::class.simpleName)
	   println(HitchhikersGuide().title)
	   println(supportInfo)
   }
}

Для примера объявим еще одну переменную, но вне классов. С рандомным названием и содержанием типа supportInfo, и распечатаем ее. Обратите внимание, что это также приватная переменная, но ее область видимости распространяется на весь текущий файл. В рабочем файле она также будет уже не видна.

Инкапсуляция

Такое ограничение видимости есть инкапсуляция. Определение инкапсуляции можно дать такое – это упаковка данных и функций, для работы с ними, внутри общего компонента. Это механизм для пресечения прямого доступа извне к определенным атрибутам компонента.

Без инкапсуляции можно иметь прямой доступ к переменной класса, например, из другого класса. Таким образом можно случайно нарушить логику работы отдельного компонента программы.

Чтобы обеспечить непрямой доступ к данным другого класса, можно использовать публичные методы. Они будут обращаться к внутренним приватным переменным и отдавать значения. Например, добавим в класс приватную переменную, которая будет по умолчанию хранить количество разделов гайда numberOfPages. Тут же добавим публичный метод getNumberOfPages(), который будет возвращать данные объявленной ранее переменной. Если говорить точнее, будет отдавать значение переменной numberOfPages у созданного ранее экземпляра.

class HitchhikersGuide() {

   var title = "Don't panic"
   private val numberOfPages = 9999

   private fun chooseArticle() {
		println("Open catalog")
   }

   fun getNumberOfPages() = numberOfPages

}

Теперь в рабочем файле можем получить это значение, вызвав getNumberOfPages() через экземпляр класса, в котором он находится.

val guide = HitchhikersGuide()
println(guide.getNumberOfPages())

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

class HitchhikersGuide() {

   var title = "Don't panic"
   private var numberOfPages = 9999

   private fun chooseArticle() {
		println("Open catalog")
   }

   fun getNumberOfPages() = numberOfPages
   fun setNumberOfPages(number: Int) {
        numberOfPages = number
   }

}

Вызовем этот метод в рабочем файле, в параметры положим новое целочисленное значение. Распечатаем его заново. Значение количества страниц для этого объекта изменено.

val guide = HitchhikersGuide()

println(guide.getNumberOfPages())
guide.getNumberOfPages()
guide.setNumberOfPages(4200)
println(guide.getNumberOfPages())

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

И так тихо сапой мы подобрались к теме сеттеров и геттеров. Об этом в следующем уроке. А проверить себя в теме модификаторов можно с помощью тестов в боте под видео.

Для тех, кто собрался стать Android-разработчиком

Пошаговаясхема
Пошаговая
схема

Описание процесса обучения от основ Kotlin до Android-разработчика

Бесплатныеуроки
Бесплатные
уроки

Авторский бесплатный курс по основам языка программирования Kotlin

Обучающийбот
Обучающий
бот

Тренажер и самоучитель по Котлин – бесплатные тесты и практика

Поделиться уроком

Ответить

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