Data class kotlin конструктор

Обновлено: 28.04.2024

Если у класса указать ключевое слово data, то автоматически будут созданы и переопределены методы toString(), equals(), hashCode(), copy(). Скорее всего вы будете использовать этот вариант для создания полноценного класса-модели с геттерами и сеттерами.

В конструкторе класса у параметров следует указывать val или var.

Подобные классы часто используются при работе с JSON.

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

toString()

Если нужно получить информацию о классе, то достаточно вызвать имя переменной класса. Вы получите строку со всеми значениями всех свойств на основе конструктора вместо непонятных символов @Cat5edea как в Java. Такой подход удобен при тестировании и отладке. Сразу понятно, о чём идёт речь.

Можно сразу определить значение по умолчанию у поля класса. При инициализации объекта можно не указывать поле, но оно будет доступно для вычислений.

equals()

При определении класса данных функция equals() (и оператор ==) по-прежнему возвращает true, если ссылки указывают на один объект. Но она также возвращает true, если объекты имеют одинаковые значения свойств, определённых в конструкторе:

Если вы переопределяете функцию equals(), также необходимо переопределить функцию hashCode().

Кстати, если вам нужно проверить, что две переменные ссылаются на один объект, то используйте оператор ===. В отличие от оператора ==, поведение оператора === не зависит от функции equals(), которое в разных классах может вести себя по разному. Оператор === всегда ведёт себя одинаково независимо от разновидности класса.

hashCode()

Если два объекта данных считаются равными (имеют одинаковые значения свойств), функция hashCode() возвращает для этих объектов одно и то же значение:

Если вам потребуется создать копию объекта данных, изменяя некоторые из его свойств, но оставить другие свойства в исходном состоянии, воспользуйтесь функцией copy(). Для этого функция вызывается для того объекта, который нужно скопировать, и ей передаются имена всех изменяемых свойств с новыми значениями.

Фактически мы создаём копию объекта, меняем значение нужного свойства и присваиваем новый объект переменной с новым именем. При этом исходный объект остаётся без изменений.

Деструктурирующее присваивание

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

Мы могли бы обратиться и привычным способом.

Деструктуризация позволяет разбить объект на несколько переменных.

Можно пропустить через цикл.

Можно пропустить какую-то переменную через символ подчёркивания.

Несколько конструкторов

Добавить второй конструктор к классу можно через ключевое слово constructor.

Нередко мы создаём классы, единственным назначением которых является хранение данных. Функционал и некоторые служебные функции таких классов зависят от самих данных, которые в них хранятся. В Kotlin они называются классами данных и помечены data .

Компилятор автоматически формирует следующие члены данного класса из свойств, объявленных в основном конструкторе:

  • пару функций equals() / hashCode() ,
  • функцию toString() в форме "User(name=John, age=42)" ,
  • компонентные функции componentN(), которые соответствуют свойствам, в соответствии с порядком их объявления,
  • функцию copy() (см. ниже).

Для обеспечения согласованности и осмысленного поведения сгенерированного кода классы данных должны удовлетворять следующим требованиям:

  • Основной конструктор должен иметь как минимум один параметр;
  • Все параметры основного конструктора должны быть отмечены, как val или var ;
  • Классы данных не могут быть абстрактными, open, sealed или inner;

Дополнительно, генерация членов классов данных при наследовании подчиняется следующим правилам:

Классы данных могут расширять другие классы (см. примеры в статье Изолированные классы).

Для того, чтобы у сгенерированного в JVM класса был конструктор без параметров, значения всех свойств должны быть заданы по умолчанию (см. Конструкторы).

Свойства, объявленные в теле класса

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

Только свойство name будет учитываться в реализациях функций toString() , equals() , hashCode() и copy() , и будет создана только одна компонентная функция component1() . Даже если два объекта класса Person будут иметь разные значения свойств age , они будут считаться равными.

Копирование

Используйте функцию copy() для копирования объекта, что позволит изменить только некоторые его свойств, оставив остальные неизменными. Для написанного выше класса User такая реализация будет выглядеть следующим образом:

Это позволяет вам писать:

Классы данных и мульти-декларации

Сгенерированные для классов данных компонентные функции позволяют использовать их в мульти-декларациях.

Стандартные классы данных

Стандартная библиотека предоставляет классы Pair и Triple . Однако, в большинстве случаев, проименованные классы данных являются лучшим решением, потому что делают код более читаемым, избегая малосодержательные имена для свойств.

Для всех классов в Kotlin родительским суперклассом является класс Any . Он также является родительским классом для любого класса, в котором не указан какой-либо другой родительский класс.

У Any есть три метода: equals() , hashCode() и toString() . Эти методы определены для всех классов в Kotlin.

По умолчанию все классы в Kotlin имеют статус final, который блокирует возможность наследования. Чтобы сделать класс наследуемым, его нужно пометить ключевым словом open .

Для явного объявления суперкласса мы помещаем его имя за знаком двоеточия в оглавлении класса:

Если у класса есть основной конструктор, базовый тип может (и должен) быть проинициализирован там же, с использованием параметров основного конструктора.

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

Переопределение методов класса

Kotlin требует явно указывать модификаторы и для членов, которые могут быть переопределены, и для самого переопределения.

Для Circle.draw() необходим модификатор override . В случае её отсутствия компилятор выдаст ошибку. Если у функции типа Shape.fill() нет модификатора open , объявление метода с такой же сигнатурой в производном классе невозможно, с override или без. Модификатор open не действует при добавлении к членам final класса (т.е. класса без модификатора open ).

Член класса, помеченный override , является сам по себе open, т.е. он может быть переопределён в производных классах. Если вы хотите запретить возможность переопределения такого члена, используйте final .

Переопределение свойств класса

Переопределение свойств работает также, как и переопределение методов; все свойства, унаследованные от суперкласса, должны быть помечены ключевым словом override , а также должны иметь совместимый тип. Каждое объявленное свойство может быть переопределено свойством с инициализацией или свойством с get -методом.

Вы также можете переопределить свойство val свойством var , но не наоборот. Это разрешено, поскольку свойство val объявляет get -метод, а при переопределении его как var дополнительно объявляется set -метод в производном классе.

Обратите внимание, что ключевое слово override может быть использовано в основном конструкторе класса как часть объявления свойства.

Порядок инициализации производного класса

При создании нового экземпляра класса в первую очередь выполняется инициализация базового класса (этому шагу предшествует только оценка аргументов, передаваемых в конструктор базового класса) и, таким образом, происходит до запуска логики инициализации производного класса.

Это означает, что свойства, объявленные или переопределенные в производном классе, не инициализированы к моменту вызова конструктора базового класса. Если какое-либо из этих свойств используется в логике инициализации базового класса (прямо или косвенно через другую переопределенную open реализацию члена класса), это может привести к некорректному поведению или сбою во время выполнения. Поэтому при разработке базового класса следует избегать использования членов с ключевым словом open в конструкторах, инициализации свойств и блоков инициализации ( init ).

Вызов функций и свойств суперкласса

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

Во внутреннем классе доступ к суперклассу внешнего класса осуществляется при помощи ключевого слова super , за которым следует имя внешнего класса: super@Outer .

Правила переопределения

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

Для того, чтобы отметить конкретный супертип (родительский класс), от которого мы наследуем данную реализацию, используйте ключевое слово super . Для задания имени родительского супертипа используются треугольные скобки, например super .

Это нормально, наследоваться одновременно от Rectangle и Polygon , но так как у каждого из них есть своя реализация функции draw() , мы должны переопределить draw() в Square и обеспечить нашу собственную реализацию этого метода для устранения получившейся неоднозначности.

Классы в Kotlin объявляются с помощью использования ключевого слова class .

Объявление класса состоит из имени класса, заголовка (указания типов его параметров, основного конструктора и т.п) и тела класса, заключённого в фигурные скобки. И заголовок, и тело класса являются необязательными составляющими. Если у класса нет тела, фигурные скобки могут быть опущены.

Конструкторы

Класс в Kotlin может иметь основной конструктор (primary constructor) и один или более дополнительных конструкторов (secondary constructors). Основной конструктор является частью заголовка класса, его объявление идёт сразу после имени класса (и необязательных параметров).

Если у основного конструктора нет аннотаций и модификаторов видимости, ключевое слово constructor может быть опущено.

Основной конструктор не может содержать в себе исполняемого кода. Инициализирующий код может быть помещён в соответствующие блоки (initializers blocks), которые помечаются словом init .

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

Обратите внимание, что параметры основного конструктора могут быть использованы в инициализирующем блоке. Они также могут быть использованы при инициализации свойств в теле класса.

Для объявления и инициализации свойств основного конструктора в Kotlin есть лаконичное синтаксическое решение:

Такие объявления также могут включать в себя значения свойств класса по умолчанию.

Вы можете использовать завершающую запятую при объявлении свойств класса.

Свойства, объявленные в основном конструкторе, могут быть изменяемые ( var ) и неизменяемые ( val ).

Если у конструктора есть аннотации или модификаторы видимости, ключевое слово constructor обязательно, и модификаторы используются перед ним.

Для более подробной информации см. "Модификаторы доступа".

Дополнительные конструкторы

В классах также могут быть объявлены дополнительные конструкторы (secondary constructors), перед которыми используется ключевое слово constructor .

Если у класса есть основной конструктор, каждый дополнительный конструктор должен прямо или косвенно ссылаться (через другой(ие) конструктор(ы)) на основной. Осуществляется это при помощи ключевого слова this .

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

Даже если у класса нет основного конструктора на него все равно происходит неявная ссылка и блоки инициализации выполняются также.

Если в абстрактном классе не объявлено никаких конструкторов (основного или дополнительных), у этого класса автоматически сгенерируется пустой конструктор без параметров. Видимость этого конструктора будет public.

Если вы не желаете иметь класс с открытым public конструктором, вам необходимо объявить пустой конструктор с соответствующим модификатором видимости.

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, так как они используют пустые конструкторы при создании экземпляров классов.

Создание экземпляров классов

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

Kotlin does not have a `new` keyword. -->

В Kotlin нет ключевого слова new .

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

Члены класса

Классы могут содержать в себе:

Наследование

Классы могут быть производными друг от друга и формировать иерархии наследования. Узнайте больше о наследовании в Котлине.

Абстрактные классы

Класс может быть объявлен как abstract со всеми или некоторыми его членами. Абстрактный член не имеет реализации в своём классе. Обратите внимание, что нам не надо аннотировать абстрактный класс или функцию словом open - это и так подразумевается.

Можно переопределить неабстрактный open член абстрактным.

Вспомогательные объекты

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

В частности, если вы объявляете вспомогательный объект в своём классе, у вас появляется возможность обращаться к членам класса, используя только название класса в качестве классификатора.

Classes in Kotlin are declared using the keyword class :

The class declaration consists of the class name, the class header (specifying its type parameters, the primary constructor, and some other things), and the class body surrounded by curly braces. Both the header and the body are optional; if the class has no body, the curly braces can be omitted.

Constructors

A class in Kotlin can have a primary constructor and one or more secondary constructors. The primary constructor is a part of the class header, and it goes after the class name and optional type parameters.

If the primary constructor does not have any annotations or visibility modifiers, the constructor keyword can be omitted:

The primary constructor cannot contain any code. Initialization code can be placed in initializer blocks prefixed with the init keyword.

During the initialization of an instance, the initializer blocks are executed in the same order as they appear in the class body, interleaved with the property initializers:

Primary constructor parameters can be used in the initializer blocks. They can also be used in property initializers declared in the class body:

Kotlin has a concise syntax for declaring properties and initializing them from the primary constructor:

Such declarations can also include default values of the class properties:

You can use a trailing comma when you declare class properties:

Much like regular properties, properties declared in the primary constructor can be mutable ( var ) or read-only ( val ).

If the constructor has annotations or visibility modifiers, the constructor keyword is required and the modifiers go before it:

Secondary constructors

A class can also declare secondary constructors, which are prefixed with constructor :

If the class has a primary constructor, each secondary constructor needs to delegate to the primary constructor, either directly or indirectly through another secondary constructor(s). Delegation to another constructor of the same class is done using the this keyword:

Code in initializer blocks effectively becomes part of the primary constructor. Delegation to the primary constructor happens as the first statement of a secondary constructor, so the code in all initializer blocks and property initializers is executed before the body of the secondary constructor.

Even if the class has no primary constructor, the delegation still happens implicitly, and the initializer blocks are still executed:

If a non-abstract class does not declare any constructors (primary or secondary), it will have a generated primary constructor with no arguments. The visibility of the constructor will be public.

If you don't want your class to have a public constructor, declare an empty primary constructor with non-default visibility:

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.

Creating instances of classes

To create an instance of a class, call the constructor as if it were a regular function:

Kotlin does not have a new keyword.

The process of creating instances of nested, inner, and anonymous inner classes is described in Nested classes.

Читайте также: