Как создать экземпляр класса kotlin
На этом шаге мы приведем общие сведения об инициализации .
В предыдущих шагах мы узнали, как объявлять классы для представления объектов реального мира. В NyetHack игрок определяется свойствами и поведением. Несмотря на всю сложность, которую можно передать через свойства и функции классов, вы пока что видели лишь малую часть способов создания экземпляров класса. Вспомним, как в прошлых шагах был объявлен Player .
class Player
Заголовок класса Player довольно прост, поэтому создать экземпляр Player тоже было просто.
fun main() < val player = Player() . . . . >
То есть вызов конструктора класса создает экземпляр класса — этот процесс еще известен как инстанцирование . В дальнейших шагах рассказывается о способах инициализации классов и их свойств. Инициализируя переменную, свойство или экземпляр класса, вы присваиваете начальное значение, пригодное к использованию. Далее мы познакомимся с разными видами конструкторов, приемами инициализации свойств и даже научимся менять правила с помощью поздней и отложенной инициализации.
Примечание по терминологии: с технической точки зрения экземпляр класса создается, когда для него выделяется память, а инициализируется — когда ему присваивается значение. Но на практике эти термины обычно употребляют иначе. Часто под инициализацией подразумевается «все необходимое, чтобы подготовить переменную, свойство или экземпляр класса к использованию», тогда как инстанцирование ограничивается «созданием экземпляра класса». Здесь мы придерживаемся более распространенного значения.
На следующем шаге мы рассмотрим конструкторы .
Как создать экземпляр класса kotlin
На этом шаге мы рассмотрим способ создания экземпляра класса .
Объявленный класс похож на чертеж. Чертеж содержит информацию о том, как построить здание, но сам он не здание. Объявление класса Player работает похожим образом: до настоящего момента игрок не был создан — вы создали только его проект.
Когда вы начинаете новую игру в NyetHack , вызываются функция main() и один из первых объектов, которые нужно создать. В данном случае это игровой персонаж. Чтобы сконструировать героя, которого можно использовать в NyetHack , сначала нужно создать его экземпляр , вызвав конструктор . В Game.kt , где переменные объявлены в функции main() , создайте экземпляр Player , как показано ниже.
fun main() < val name = "Madrigal" var healthPoints = 89 val isBlessed = true val isImmortal = false val player = Player() // Аура val auraColor = auraColor(isBlessed, healthPoints, isImmortal) . . . . >
Рис.1. Создание экземпляра Player (Game.kt)
Вы вызвали главный конструктор Player , добавив скобки после имени класса Player . Это действие создает экземпляр класса Player . Переменная player теперь » содержит экземпляр класса Player» .
Имя «конструктор» говорит само за себя: он создает. Если конкретнее, он создает экземпляр и готовит его к использованию. Синтаксис вызова конструктора похож на вызов функции: он использует скобки для передачи аргументов его параметров. Другие способы создания экземпляра класса мы рассмотрим позже.
Посмотрим, что можно сделать с созданным экземпляром класса.
На следующем шаге мы рассмотрим функции класса .
Классы
Классы в Kotlin объявляются с помощью использования ключевого слова class .
class Person < /*. */ >
Объявление класса состоит из имени класса, заголовка (указания типов его параметров, основного конструктора и т.п) и тела класса, заключённого в фигурные скобки. И заголовок, и тело класса являются необязательными составляющими. Если у класса нет тела, фигурные скобки могут быть опущены.
class Empty
Конструкторы
Класс в Kotlin может иметь основной конструктор (primary constructor) и один или более дополнительных конструкторов (secondary constructors). Основной конструктор является частью заголовка класса, его объявление идёт сразу после имени класса (и необязательных параметров).
class Person constructor(firstName: String) < /*. */ >
Если у основного конструктора нет аннотаций и модификаторов видимости, ключевое слово constructor может быть опущено.
class Person(firstName: String) < /*. */ >
Основной конструктор не может содержать в себе исполняемого кода. Инициализирующий код может быть помещён в соответствующие блоки (initializers blocks), которые помечаются словом init .
При создании экземпляра класса блоки инициализации выполняются в том порядке, в котором они идут в теле класса, чередуясь с инициализацией свойств.
class InitOrderDemo(name: String) < val firstProperty = "Первое свойство: $name".also(::println) init < println("Первый блок инициализации: $") > val secondProperty = "Второе свойство: $".also(::println) init < println("Второй блок инициализации: $") > >
Обратите внимание, что параметры основного конструктора могут быть использованы в инициализирующем блоке. Они также могут быть использованы при инициализации свойств в теле класса.
class Customer(name: String)
Для объявления и инициализации свойств основного конструктора в Kotlin есть лаконичное синтаксическое решение:
class Person(val firstName: String, val lastName: String, var age: Int)
Такие объявления также могут включать в себя значения свойств класса по умолчанию.
class Person(val firstName: String, val lastName: String, var isEmployed: Boolean = true)
Вы можете использовать завершающую запятую при объявлении свойств класса.
class Person( val firstName: String, val lastName: String, var age: Int, // завершающая запятая ) < /*. */ >
Свойства, объявленные в основном конструкторе, могут быть изменяемые ( var ) и неизменяемые ( val ).
Если у конструктора есть аннотации или модификаторы видимости, ключевое слово constructor обязательно, и модификаторы используются перед ним.
class Customer public @Inject constructor(name: String) < /*. */ >
Для более подробной информации см. «Модификаторы доступа».
Дополнительные конструкторы
В классах также могут быть объявлены дополнительные конструкторы (secondary constructors), перед которыми используется ключевое слово constructor .
class Person(val pets: MutableList = mutableListOf()) class Pet < constructor(owner: Person) < owner.pets.add(this) // добавляет этого питомца в список домашних животных своего владельца >>
Если у класса есть основной конструктор, каждый дополнительный конструктор должен прямо или косвенно ссылаться (через другой(ие) конструктор(ы)) на основной. Осуществляется это при помощи ключевого слова this .
class Person(val name: String) < val children: MutableList= mutableListOf() constructor(name: String, parent: Person) : this(name) < parent.children.add(this) >>
Обратите внимание, что код в блоках инициализации фактически становится частью основного конструктора. Дополнительный конструктор ссылается на основной при помощи своего первого оператора, поэтому код во всех блоках инициализации, а также инициализация свойств выполняется перед выполнением кода в теле дополнительного конструктора.
Даже если у класса нет основного конструктора на него все равно происходит неявная ссылка и блоки инициализации выполняются также.
class Constructors < init < println("Блок инициализации") >constructor(i: Int) < println("Constructor $i") >>
Если в абстрактном классе не объявлено никаких конструкторов (основного или дополнительных), у этого класса автоматически сгенерируется пустой конструктор без параметров. Видимость этого конструктора будет public.
Если вы не желаете иметь класс с открытым public конструктором, вам необходимо объявить пустой конструктор с соответствующим модификатором видимости.
class DontCreateMe private constructor () < /*. */ >
On the JVM, if all of the primary constructor parameters have default values, the compiler will generate an additional parameterless constructor which will use the default values. This makes it easier to use Kotlin with libraries such as Jackson or JPA that create class instances through parameterless constructors. > > «`kotlin > class Customer(val customerName: String = «») > «` —>
В JVM компилятор генерирует дополнительный конструктор без параметров в случае, если все параметры основного конструктора имеют значения по умолчанию. Это делает использование таких библиотек, как Jackson и JPA, более простым с Kotlin, так как они используют пустые конструкторы при создании экземпляров классов.
class Customer(val customerName: String = "")
Создание экземпляров классов
Для создания экземпляра класса конструктор вызывается так, как если бы он был обычной функцией.
val invoice = Invoice() val customer = Customer("Joe Smith")
Kotlin does not have a `new` keyword. —>
В Kotlin нет ключевого слова new .
Создание экземпляров вложенных, внутренних и анонимных внутренних классов описано в разделе Вложенные классы.
Члены класса
Классы могут содержать в себе:
- Конструкторы и инициализирующие блоки
- Функции
- Свойства
- Вложенные классы
- Объявления объектов
Наследование
Классы могут быть производными друг от друга и формировать иерархии наследования. Узнайте больше о наследовании в Котлине.
Абстрактные классы
Класс может быть объявлен как abstract со всеми или некоторыми его членами. Абстрактный член не имеет реализации в своём классе. Обратите внимание, что нам не надо аннотировать абстрактный класс или функцию словом open — это и так подразумевается.
abstract class Polygon < abstract fun draw() >class Rectangle : Polygon() < override fun draw() < // рисование прямоугольника >>
Можно переопределить неабстрактный open член абстрактным.
open class Polygon < open fun draw() < // некоторый метод рисования полигонов по умолчанию >> abstract class WildShape : Polygon() < // Классы, которые наследуют WildShape, должны предоставлять свой собственный // метод рисования вместо использования по умолчанию для полигона abstract override fun draw() >
Вспомогательные объекты
Если вам нужно написать функцию, которая может быть использована без создания экземпляра класса, имеющую доступ к данным внутри этого класса (к примеру, фабричный метод), вы можете написать её как член объявления объекта внутри этого класса.
В частности, если вы объявляете вспомогательный объект в своём классе, у вас появляется возможность обращаться к членам класса, используя только название класса в качестве классификатора.
© 2015—2023 Open Source Community
Урок 11: ООП. Классы и объекты в Kotlin. Сущности и объекты
Начинаем разговор про классы и объекты в языке программирования Kotlin. Давайте попробую объяснить сначала с точки зрения прикладного использования. Начнем с понятия классы.
На протяжении всего обучения до этого момента мы оперировали переменными разных типов. Эти типы были как для базовых переменных Int, Boolean и т.д. Так и более сложные типы для массивов или диапазонов, например Array , LongRange , IntProgression и т.д.
Все эти типы представляют собой классы с разнообразным содержанием: в основном это функции и свойства. Это то, к чему мы обращаемся, чтобы использовать их заданные языком возможности.
На всякий случай вспомним такой пример из 8 урока. Когда мы выводили пронумерованный список ингредиентов, использовалась функция * indexOf*() , применимая к массиву ингредиентов.
arrayOfIngredients.indexOf(i)
Так вот, массив arrayOfIngredients был создан с типом Array (он же класс Array). Внутри него содержатся различные функции по взаимодействию конкретно с массивами, одна из которых * indexOf*() . Ее мы и вызвали, чтобы узнать индекс какого-то конкретного элемента. Что я хочу сказать – классы являются шаблонами, в которых определяются свойства и функции, предназначенные для определенных сценариев программы.
Теория ООП (объектно-ориентированный стиль)
Из этого следует, что мы можем создавать собственные классы с любой угодной нам логикой. Зачем? В современном мире принято использовать так называемый объектно ориентированный стиль программирования. Когда человеку проще мыслить объектами, поэтому прижился такой способ программирования. Это существенно облегчает разработку, поддержку и масштабирование программ. Как натренировать этот подход? Имея определенную бизнес задачу, необходимо будет сначала мысленно выделить сущности, которые будут использоваться. Затем определить их свойства. И, наконец, наделить сущность необходимыми действиями – функциями.
С практикой этот навык будет доведен до автоматизма, а пока перейдем к примерам. Представим, что мы проектируем гипотетическое приложение с рецептами. Там будет список блюд, разбитых по категориям. Для рецепта будет инструкция по приготовлению, его можно будет добавить в избранное или отправить кому-нибудь из контактов. Сейчас, конечно, не будем писать это приложение, такое подробное описание нужно для демонстрации рассуждения о выделении его сущностей.
Пример сущности в приложении с рецептами
Пойдем по порядку. Итак, из нашего минималистичного технического задания можно выделить минимум одну основную сущность – блюдо. Значит пора создать этот класс в среде разработки.
Создание классов в Kotlin
Для начала немного изменим иерархию файлов. Для уроков 1-10 создадим отдельный пакет. Для текущего урока также создадим пакет lesson_11. При создании класса файл правильней называть таким же именем (хоть и в Котлине нет жесткой привязки названия класса к названию файла). Кроме того, классы всегда пишутся с заглавной буквы. Для создания нового класса необходимо нажать ПКМ на пакет, выбрать New и выбрать Kotlin Class/File или File. Если выбрать пункт Class, то ключевые слова шаблона класса пропишутся автоматически, давайте выберем просто File, чтобы создался пустой документ. Итак, в названии файла напишем Dish – блюдо.
Вверху документа появился текст package lesson_11 , что указывает компилятору на принадлежность этого файла к созданному ранее пакету. Чтобы в файле объявить класс, необходимо написать ключевое слово class, после которого пишется название с заглавной буквы. Если в названии нужно прописать несколько слов – каждое новое слово тоже с заглавной буквы (без пробелов). Далее открываем фигурные скобки – это будет тело класса. Вообще и без фигурных скобок можно считать класс созданным, однако, без свойств и логики он не будет иметь никакого смысла.
class Dish
Хотя синтаксически все написано верно. Продолжим наполнять смыслом созданный класс.
Свойства (характеристики) сущности
Исходя из представленного выше гипотетического ТЗ, мы определили основную сущность в приложении – блюдо. Теперь подумаем о свойствах или характеристиках этой сущности. Объекты в реальном мире, как правило, отличаются какими-то свойствами. В случае блюд это могут быть уникальные названия, ингредиенты и так далее. Поэтому сейчас мы должны определить свойства, которые могут быть разными для каждого объекта этого класса в будущем. Свойства можно перечислить в круглых скобках, открытых после названия класса, разделяя запятыми. Объявляются они как переменные без инициализации – то есть тип указывается обязательно.
Итак, у блюда могут быть такие поля:
- id – предположим, что наши блюда хранятся в базе, чтобы отличать их друг от друга введем id — идентификатор. Он будет иметь числовое значение, что гарантированно предотвратит вероятность повторений в базе.
class Dish( val id: Int, )
- name – строка с названием блюда
- category — строка с категорией блюда
- ingredients – строковый список с ингредиентами
- inFavorites – булеан значение, флаг означает добавлено ли какое то блюдо в избранное
Есть два момента.
Trailing comma – висящая запятая
Первый. Свойства разделяются запятыми. А еще хорошей практикой является последний элемент также оставлять с запятой в конце. Это называется trailing comma. По русски это висящая (или последняя) запятая. На случай, если вам необходимо будет добавить новое свойство, вы просто впишете его в новую строку, не заботясь о расстановке запятых выше.
Инициализация свойства значением по умолчанию
Второй. При создании объекта (или экземпляра класса) нам необходимо будет указывать все перечисленные поля. Однако, есть случаи, когда нужно сразу проставить значение по умолчанию. В данном случае все будущие объекты при создании не будут находиться в избранном, поэтому поле inFavorites проинициализируем значением false.
class Dish( val id: Int, val name: String, val category: String, val ingredients: List, val inFavorites: Boolean = false, )
Создание экземпляра класса (объекта)
Окей. Мы создали класс и наделили его свойствами. Это по прежнему не конкретный объект. Часто для понимания классов, представляют аналогию с чертежом. То есть это шаблон, на основании которого будут создаваться уникальные объекты (или экземпляры класса). А так как в продуктовых программах разнообразных объектов может быть огромное количество, очень удобно их создавать по определенным правилам, с определенным предназначением.
Класс Dish мы пока трогать не будем, а продолжим работу в другом месте, как это часто бывает в практике. Для этого в пакете текущего урока создадим новый файл с произвольным названием, например, это будет Main. В нем создадим функцию main(), что будет точкой входа в приложение в рамках 11 урока. Вообщем тут должно быть все знакомо.
fun main()
Теперь будем создавать объекты (или экземпляры класса), то есть конкретные блюда с уникальными характеристиками.
Создаем переменную dish1 и через равно пишем название созданного ранее класса Dish. Открываем круглые скобки, в которых и будем прописывать свойства конкретного блюда, среда разработки уже подсвечивает их точно также, как мы заполняли параметрами функцию. Для красоты кода будем их записывать в столбик, явно обозначив названия свойств. Для поля ингредиенты создаем список и тут же заполняем его. Поле isFavorites здесь заполнять уже не обязательно, для него мы указали значение по умолчанию false, и теперь все объекты будут хранить в себе это значение сразу. И обратите внимание, здесь я тоже оставляю trailing comma после крайнего свойства.
Тут же создадим второй объект – другое блюдо.
Отлично, мы создали класс и на его основе два готовых объекта. При создании класса вызывается конструктор класса, но об этом немного попозже. А еще лучше подробнее ознакомиться к конструкторами дополнительно, когда немного освоитесь – ссылку оставлю в описании к видео ( https://kotlinlang.org/docs/classes.html#constructors). Далее явно проставим типы у переменных для наглядности. Типом переменной и является созданный класс Dish.
fun main()
Обращение к свойствам объекта
Теперь с этими объектами можно немного повзаимодействовать. Самое простое это, например, можно читать свойства. Обращаться к свойствам нужно через точку. Распечатаем какую-нибудь информацию.
println(dish1.name) println(dish1.ingredients) println(dish1.inFavorites) println() println(dish2.name) println(dish2.ingredients) println(dish2.inFavorites)
Хорошо, как видите свойство inFavorites также распечатывает заданное значение по умолчанию. Свойства можно изменять, присваивая новые значения. При этом переменная должна стать изменяемой (среда разработки предложит изменить val на var). У первого блюда изменим категорию, а второе пусть будет иметь статус “в избранном”.
dish1.category = "Блюда из яиц" dish2.inFavorites = true println(dish1.category) println(dish2.inFavorites)
Создание функций внутри класса
Окей. Вспоминаем, что у класса могут быть еще и действия. Это могут быть методы с обезличенными параметрами, которые могут присутствовать у каждого объекта этого класса.
Вновь обратимся к ТЗ и подумаем, какой функционал должен быть у сущности “Блюдо”. Например, у нас есть поле inFavorites, что подразумевает добавление блюда в избранное. Также пользователь может совершить обратное действие – удаление из избранного.
Далее функционал приложения может подразумевать действие “Начать готовку”, которое перенесет пользователя на экран с процессом приготовления, в котором будут красиво расписаны шаги и приложены фото (гипотетически). Это значит, что для этого действия также нужен отдельный метод. И, наконец, добавим такое действие, как “Скачать список покупок” – в нем будет возвращаться список ингредиентов. Пока хватит.
Такой подход служит для оптимизации кода. Вместо того, чтобы клепать копии этих методов для каждого объекта в рабочем классе или в функции main(), как в нашем случае, можно будет переиспользовать методы класса сколько угодно раз с разными параметрами.
Давайте перечислим эти функции в классе Dish(). Наполнять методы будем консольными сообщениями для имитации запуска требуемого функционала. Для добавления/удаления из избранного будем распечатывать сообщение включающее название блюда и менять флаг inFavorites на true и наоборот. В строке пишем переменную name. В нее подставится конкретное значение в зависимости от объекта, для которого будет вызываться это действие. Метод startCooking() также будет сигнализировать текстовым сообщением. Метод downloadIngredients() пусть будет возвращать список ингредиентов для выбранного блюда для имитации его скачивания.
fun addToFavorites() < println("Блюдо $name добавлено в избранное") inFavorites = true >fun removeFromFavorites() < println("Блюдо $name удалено из избранного") inFavorites = false >fun startCooking() < println("Пользователь перешел на экран начала приготовления блюда $name") >fun downloadIngredients() : List
Обращение к методам класса
Наконец, перейдем в основной метод для демонстрации обращения к методам класса. Воспроизведем пару пользовательских сценариев.
Для блюда 1 они будут такие:
- объявили заголовок действий, чтоб не путаться
- добавили блюдо в избранное – для проверки изменения поля распечатаем его актуальное значение
- далее перешли на экран приготовления выбранного блюда
Затем отбиваем пустую строку, написали внутренний заголовок и для блюда 2 действия будут такими:
- пользователь добавил блюдо в избранное – распечатываем это поле
- пользователь решил скачать список с ингредиентами. Для этого сохраним вернувшееся из функции значение в отдельную переменную и распечатаем его
- пользователь удалил блюдо из избранного – вновь распечатываем это поле для просмотра изменений
println("Действия для блюда Яичница") dish1.addToFavorites() println(dish1.inFavorites) dish1.startCooking() println() println("Действия для блюда Куриный суп") dish2.addToFavorites() println(dish2.inFavorites) val ingredientsForSoup = dish2.downloadIngredients() println(ingredientsForSoup) dish2.removeFromFavorites() println(dish2.inFavorites)
После запуска в консоли можно отследить работу всех методов. Программа отработала корректно. Целью примера было продемонстрировать логику работы функций в классе, к которым мы обращаемся через экземпляры класса. Единожды объявленные методы могут переиспользоваться для любого количества объектов с разнообразными параметрами.
Для тех, кто собрался стать Android-разработчиком
Пошаговая
схема
Описание процесса обучения от основ Kotlin до Android-разработчика
Бесплатные
уроки
Авторский бесплатный курс по основам языка программирования Kotlin
Обучающий
бот
Тренажер и самоучитель по Котлин – бесплатные тесты и практика