Классы, объекты, свойства и функции-члены в Kotlin
Приступая к знакомству с объектно-ориентированным программированием, следует оговорить, что в Kotlin мы уже пользовались всеми его преимуществами и особенностями. Мы просто не создавали собственные классы, не определяли в них свойства и методы, которыми будут обладать объекты, созданные на базе этих классов.
Вместо этого мы использовали те классы, которые встроены в Kotlin, его стандартную библиотеку, в основном это были базовые классы. Например, класс целых чисел Int или класс массивов Array. И хотя мы называли их типами данных, в Kotlin это то же самое что класс, класс данных.
От классов создаются конкретные объекты – экземпляры классов. Так число 10 – это экземпляр класса Int. От одного и того же класса можно создать множество разных объектов. Однако у всех объектов, созданных от одного класса, будет одинаковый перечень свойств и действий, которые с ними можно совершать.
Так у всех экземпляров, созданных от класса Array, будет свойство длинны массива. Однако значение этого свойства у каждого будет свое. Все массивы будут поддерживать операцию извлечения элемента по индексу, но из-за того, что в одном массиве 5 элементов, а в другом 50, диапазон допустимых индексов будет разным.
Идея объектно-ориентированного программирования предполагает, что в программе есть сущности-объекты и порождаемые ими явления, подобно тому, как обстоит дело в реальном мире, и так, как мы привыкли воспринимать окружающее. А выполнение программой определенной задачи во многом сводится к изменению свойств объектов с помощью функций-методов, то есть действий, которые они и над ними можно совершать.
Класс – это ничто иное как обобщенное описание свойств и действий, которыми обладают объекты данного класса.
В Kotlin классы принято определять в отдельных файлах, хотя это не обязательно. Можно в том же, где находится функция main() – точка входа в программу; также в одном файле может содержаться несколько классов.
Создадим свой класс, похожий на класс целых чисел. Назовем его NumInc. Объекты этого класса будут обладать двумя свойствами – хранить в себе число и шаг, число может изменяться на шаг. Также в классе определим две функции – увеличивающую число на шаг и уменьшающую.
class NumInc { var number = 0 var step = 1 fun inc() { number += step } fun dec() { number -= step } }
fun main() { val a = NumInc() val b = NumInc() a.number = 10 a.step = 2 a.inc() b.dec() println("a: ${a.number}") println("b: ${b.number}") }
Результат выполнение программы:
a: 12 b: -1
Мы создали класс NumInc, объекты которого обладают свойствами number
и step
, начальные значения которых 0 и 1. Также у объектов этого класса есть функции-члены inc() и dec().
В других языках программирования функции-члены класса называют методами. В Kotlin этот термин не принят, хотя это сути дела не меняет. Видимо от него отказались, так как у объектов кроме функций-членов также могут быть функции-расширения (определяемые особым образом за пределами класса), которые также следует считать методами, в отличие от обычных функций.
В синтаксисе своего вызова функция-член отличается от обычной функции тем, что вызывается в нотации через точку: объект.функция()
. Например, мы можем вызвать функцию inc() только через объект a или b, то есть a.inc()
или b.inc()
.
Однако есть исключения. В Kotlin, как и в других объектно-ориентированных языках, многие операторы (сложение, взятие по индексу) и ключевые слова – это на самом деле функции-члены, которые вызываются с помощью таких специальных знаков и слов. Для каждой такой функции, определяемой в классе, есть свое особое имя, регламентируемое языком программирования. Определение подобной функции в классе называется переопределением оператора.
В main() мы создали два объекта от класса NumInc. Это подобно тому, как если бы создавались объекты от класса Array.
val c = Array(3, {0})
Если вы посмотрите тип переменных a
и b
(Ctrl + Shift + P), увидите, что их тип NumInc. В полном варианте определение переменной выглядело бы так:
val a: NumInc = NumInc()
При создании объектов после имени класса ставятся круглые скобки. Только в нашем случае в скобках мы ничего не передаем.
При вызове класса мы могли бы что-нибудь передать в скобках для инициации свойств объекта. Однако вместо этого жестко "вшили" эти значения в сам класс. Поэтому все объекты класса NumInc исходно обладают одинаковыми значениями свойств: number = 0
и step = 1
.
Несмотря на то, что и у объекта a
и у b
значения этих свойств в момент создания одинаковое, у каждого объекта свой number
и свой step
. Изменяя значение переменной a.number
, мы никаким образом не меняем значение b.number
.
Методы inc() и dec() у объектов одни и те же. С каждым объектом они делают то же самое, однако при работе используют свойства того объекта, на который они были вызваны (того, что стоит перед точкой). Если inc() вызывается на a
, то есть a.inc()
, то выражение тела этого метода будет невидимо для нас преобразовано из number += step
в a.number += a.step
.
Вопросы:
-
Почему в примере значение b.number оказалось равным -1?
-
Добавьте в класс еще одну функцию, которая увеличивает число не на шаг-свойство, а на число, которое передается в функцию в качестве аргумента.
PDF-версия курса с ответами к практическим работам
Приложение для Android "Kotlin. Курс"
Комментарии
Урок 1 kotlin ООП