В ООП центральную роль играют классы. Классы состоят из членов, членами классов могут быть переменные, функции, конструкторы, блоки инициализации, свойства, вложенные классы, декларации объектов. В ООП класс является также типом данных.
Для использования класса необходимо сначала объявить класс и затем создать его экземпляр. Каждый экземпляр класса хранится в отдельной области оперативной памяти независимо друг от друга, так как класс определяет только структуру, а фактические значение должны быть присвоены или рассчитаны для каждого экземпляра отдельно. Необходимые значения для членов класса могут быть переданы посредством конструктора класса или присвоены членам класса путём получения доступа к его членам и присвоения новых значений.
Классы объявляются посредством ключевого слова class:
class TestClass {
// Тело класса
}
Классы могут иметь первостепенный конструктор, второстепенный конструктор, блоки инициализации и свойства. Первостепенный конструктор пишется сразу после названия класса в виде скобок функции с передаваемыми параметрами точно так же, как и в функциях. Второстепенный конструктор пишется внутри тела класса:
class TestClass(param : String) {
// Переменные-члены класса
var stringParam : String
var intParam : Int = 0
// Блок инициализации
init {
stringParam = param
}
// Второстепенный конструктор должен вызывать первостепенный конструктор. В данном случае первостепенный конструктор получает один параметр, а второстепенный два параметра и для правильно работы кода параметр типа String необходимо передать первостепенному конструктору вызвав его.
constructor(param: Int, sParam : String) : this(sParam) {
// Инициализация переменной класса под названием intParam значением аргумента param конструктора
intParam = param
}
}
В примере выше первостепенный конструктор принимает только один параметр типа String. В классе объявлено два переменных-члена: один с типом String, другой Int. Если переменная не инициализируется в блоке init ей необходимо присвоить значение во время объявления, как и сделано для переменной intParam. Блок init служит для сохранения полученных через конструктор значений в локальных переменных класса так как аргументы конструктора доступны для использования только в блоках инициализации и мы присвоили значение полученного аргумента param члену класса stringParam. Мы не можем присвоить его переменной intParam, так как типы должны совпадать. Блоков инициализации может быть несколько и они выполняются по порядку написания в коде. Порядок определяется порядком строк кода. Чем выше написан init блок тем раньше он выполняется.
Во второстепенном конструкторе мы объявляем, что мы можем принять два параметра: один с типом Int, другой с типом String. Из вторичных конструкторов необходимо вызывать первичный конструктор и поэтому во вторичных конструкторах также есть необходимость принимать те параметры, которые необходимы в первостепенном конструкторе или если типы получаемых данных имеют большую разницу, первостепенный конструктор необходимо объявлять пустым, а все необходимые данные получать через вторичных конструкторов. Вызов первостепенного конструктора из второстепенного осуществляется посредством написания ключевого слова this и вызова его в качестве функции и передачи необходимого параметра, полученного через второстепенный конструктор, что является вызовом первостепенного конструктора.
Ключевое слово this внутри классах ссылается к данному экземпляру класса и если есть необходимость сослаться на члены класса это можно сделать либо написав название переменной или функции, либо через ключевое слово this, после чего идёт точка и название необходимого члена. Для удобства аргументы, принимаемые посредством конструкторов, можно назвать также, как и называются члены класса, но в данном случае возникает неоднозначность и для её устранения необходимо использовать ключевое слово this перед названием членов класса.
class TestClass(stringParam : String) {
// Переменные-члены класса
var stringParam : String
var intParam : Int = 0
// Блок инициализации
init {
// Неоднозначность исчезает, когда мы явно указываем, что переменная this.stringParam это член нашего класса, а stringParam это аргумент, полученный в конструкторе
this.stringParam = stringParam
}
// Второстепенный конструктор должен вызывать первостепенный конструктор. В данном случае первостепенный конструктор получает один параметр, а второстепенный два параметра и для правильно работы кода параметр типа String необходимо передать первостепенному конструктору вызвав его.
constructor(intParam : Int, stringParam: String ) : this(stringParam) {
// Инициализация переменной класса под названием intParam значением аргумента param конструктора
this.intParam = intParam
}
}
Можно облегчить нашу работу и не писать код по инициализации, написав в первостепенном конструкторе перед названиями аргументов val или var и в таком случае компилятор получает аргументы и создаёт за нас также члены класса с типом и названием этих аргументов. Так можно делать только в первостепенном конструкторе, во второстепенных такой возможности нет:
class TestClass(var stringParam : String) {
fun printSomething() {
// За нас автоматически создаётся также переменная с аналогичным типом и названием как в конструкторе под названием stringParam и с типом String
println(stringParam)
}
}
Использование классов осуществляется посредством объявления их экземпляров и в большинстве случаев присвоение экземпляра переменной для дальнейшего взаимодействия именно с этим экземпляром. Создание экземпляра класса выглядит как вызов функции и в качестве названия функции используется название класса. Каждый экземпляр класса и является объектом в ООП.
// Создаём и инициализируем экземпляр класса TestClass и присваиваем переменной testCls
val testCls = TestClass("This is value of stringParam class member")
// Выводим значение переменной stringParam экземпляра класса TestClass, который был создан и сохранён в переменной testCls
println(testCls.stringParam)
Реклама