- Ключевое слово data
- toString()
- equals()
- hashCode()
- Деструктурирующее присваивание
- Несколько конструкторов
- Java 14 – Record data class
- 1. Decompile the Java 14 record class.
- 2. How to use Java 14 Record?
- 3. Override the default methods of Record.
- 4. Is Record Serializable?
- 5. Preview Language Feature
- Kotlin. Классы данных (Data classes)
- Переопределяемые функции
- toString()
- equals()
- hashCode()
- Мультидекларации
- Стандартные классы данных
- Влияние data-классов на вес приложения
- Полезные ссылки
- Data Classes in Java: Introduction to Java Records
- The problem to be solved with Records
- Structure and structure of records
Ключевое слово data
Если у класса указать ключевое слово 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.
Источник
Java 14 – Record data class
By mkyong | Last updated: May 22, 2020
Viewed: 5,688 (+30 pv/w)
This article is introducing the Java 14 new feature – record or data class, defined in JEP 359.
P.S This record data class is a preview feature
Why record?
Java is too verbose, if we want to declare a class, we need to create a lot of tedious and repetitive methods like constructors, accessors, equals() , hashCode() , toString() . Finally, Java 14 introduced the record class to simplify the process by automatically creating all the tedious methods.
A record data class.
1. Decompile the Java 14 record class.
Try to decompile the above Point.class , and we will get the following source code.
Java 14 record will generate the following variables and methods automatically.
- final class extends java.lang.Record .
- private final instance variables.
- constructors, (with instance variables as arguments)
- toString()
- hashCode() and equals() , for object comparation.
- getter() for the instance variable, no setters, all final variables.
That’s good, and it saved us a few clicks in IDEs.
2. How to use Java 14 Record?
This example shows you how to use a record class.
Java developer’s life is much simple now рџ™‚
3. Override the default methods of Record.
3.1 Review a Location record class, we will override the default record’s constructor and toString() .
4. Is Record Serializable?
Can we read or write the record class to a file? The answer is yes, makes record class implements Serializable and it works.
5. Preview Language Feature
5.1 This record class is a preview feature. We need to use the —enable-preview option to enable it manually.
5.2 For IntelliJ IDE, please update to the latest version 2020.1.1; it should support the Java 14 new preview features.
Источник
Kotlin. Классы данных (Data classes)
В процессе разработки нам часто приходится создавать классы, предназначенные исключительно для хранения каких-либо данных. При этом, чтобы такой класс стал максимально удобным переопределяются методы toString() , equals() и hashCode() .
Обычно данные методы имеют одинаковую реализацию и чтобы каждый раз не писать один и тот же код можно просто отметить класс ключевым словом data — все необходимые методы будут сгенерированы автоматически. В Kotlin такие классы называются классами данных (data classes).
Не каждый класс можно отметить ключевым словом data. Для этого он должен соответствовать определённым требованиям:
- В основном конструкторе должен быть как минимум один параметр.
- Все параметры основного конструктора должны быть отмечены ключевыми слова val или var (рекомендуется val).
- Классы данных не могут быть отмечены ключевыми словами abstract, open, sealed, inner.
Переопределяемые функции
Возможно у кого-то возникнет вопрос: для чего вообще нужны методы toString() , equals() и hashCode() ? В данном разделе остановимся на этом подробнее, а также опишем какие ещё функции в классах данных генерируются автоматически.
toString()
Часто, особенно при отладке, возникает необходимость вывести в лог информацию об экземпляре класса. Если метод toString() не переопределён, то при обращении к экземпляру в лог будет выведена ссылка на него.
Это не очень информативно и вряд ли чем-то поможет. Чтобы исправить ситуацию достаточно переопределить метод toString() и указать в нём, что именно нужно выводить в лог при обращении к экземпляру класса.
Если же мы отметим класс ключевым словом data, метод toString() будет переопределён автоматически. При этом в лог будут выводиться все поля, указанные в конструкторе, в порядке их добавления.
equals()
Иногда нам может потребоваться сравнить между собой два объекта таким образом, чтобы они считались равными, если содержат одни и те же данные.
Объекты не равны, потому что по умолчанию сравниваются не данные, которые они хранят, а ссылки на объекты. Чтобы задать свой алгоритм сравнения переопределяется метод equals() .
Если же мы отметим класс ключевым словом data, метод equals() будет переопределён автоматически. При этом работать будет точно также, как и в примере выше: будет проверять на равенство все значения, указанные в основном конструкторе.
Так как оператор == за кулисами вызывает метод equals() , для сравнения ссылок объектов используется оператор === .
hashCode()
Экземпляр класса можно использовать как ключ в структурах данных на основе хэш-функций. Это возможно благодаря тому, что каждому объекту присваивается уникальный хэш-код, даже если значения этих объектов идентичны.
Но по логике, если два объекта содержат одинаковые значения, значит и хэш-код у них должен быть одинаковым. Для этого и переопределяется метод hashCode() .
Обратите внимание, что метод hashCode() работает совместно с методом equals() . Это означает, что если переопределить метод hashCode() без переопределения метода equals() , каждому объекту будет присвоен уникальный хэш-код, даже если значения этих объектов равны. Связано это с тем, что перед присвоением хэш-кода происходит сравнение объектов. А без метода equals() объекты сравниваются по их ссылкам, а не значениям.
Опять же, чтобы обо всём этом не думать, достаточно отметить класс ключевым словом data и метод hashCode() (и все остальные) будет переопределён автоматически. При этом работать будет точно также, как и в примере выше: будет возвращать значение, зависящее от хэш-кодов всех свойств, объявленных в основном конструкторе.
Ещё один метод, который генерируется автоматически для всех классов данных. Он позволяет копировать экземпляры класса, изменяя значения некоторых свойств.
Разработчики Kotlin пытаются заложить нам в голову идею о том, что вместо модификации объекта лучше создать новый объект. Поэтому и была добавлена данная функция — упростить создание новых объектов.
На практике это может выглядеть примерно следующим образом:
Можно реализовать самостоятельно следующим образом:
Мультидекларации
Мультидекларации (destructuring declarations) — это особенность, характерная для классов данных, которая позволяет распаковать объект и использовать его значения для инициализации сразу нескольких переменных.
Достигается это все благодаря тому, что для каждой переменной, объявленной в основном конструкторе, автоматически генерируются функции componentN(), где N — номер позиции переменной в конструкторе. Что делает данная функция? Возвращает значение переменной. Такую функцию можно создать самому для класса, который не является классом данных.
Стандартные классы данных
В стандартной библиотеке Kotlin есть два класса данных: Pair и Triple . Из названий понятно, что они позволяют хранить две и три переменных разного типа одновременно. В данной статье подробно их описывать не буду, добавлено в познавательных целях.
Влияние data-классов на вес приложения
Мне встречались споры о том, стоит ли в принципе использовать data-классы в своём приложении. Ведь порой мы хоть и создаём data-класс, но не используем его функциональность, потому что, например, нам нужно просто получить ответ от сервера и передать данные в другое место. И в этом случае выходит так, что все эти автоматически сгенерированные методы впустую занимают место. К тому же, если действительно понадобится любой из вышеперечисленных методов, то его можно самостоятельно реализовать.
И вот перед разработчиками встал вопрос: стоит ли при создании класса задаваться вопросом “а нужен ли мне здесь data-класс”?
Чтобы ответить на этот вопрос, сначала нужно ответить на другой: насколько велико влияние data-классов на вес приложения?
И вот я наткнулась на статью, где автор провёл исследование, чтобы ответить на этот вопрос:
Влияние Kotlin data-классов на вес приложения.
В этой статье автор описал, как он создал плагин для удаления всех автогенерируемых методов с целью сравнить вес приложения с этими методами и без них. Разница составила 4%, подробнее о деталях читайте в статье.
Так каков в результате ответ? Использовать везде или использовать с осторожностью?
Тут всё достаточно просто. При создании класса для хранения данных подумайте для чего он будет использоваться. Понадобится ли функциональность data-класса? Если этот класс нужен только как переходник для данных, то подойдёт и обычный класс.
Хотя на мой взгляд оптимизация в 4% слишком мала, чтобы тратить время на раздумья. Но в любом случае проведённое исследование похвально.
Полезные ссылки
Data Classes — официальная документация.
Destructuring Declarations — официальная документация.
Классы данных — перевод на русский.
Мультидекларации — перевод на русский.
Источник
Data Classes in Java: Introduction to Java Records
Data classes, i.e. Java classes whose sole purpose is to hold data and make it accessible via getters and setters, are among the largest collection points of boilerplate code in many software projects. To create constructors for each new class, the methods equals, hashCode and toString and for each field, a getter and a setter has become a hated ceremony for many developers – unless they directly use libraries like Lombok to avoid it. JEP 359 should help.
With JEP 359, records are introduced into the JVM – although for the time being only as a preview feature. In order to try it out, both the compiler and the created program must be executed with the -enable-preview flag.
The problem to be solved with Records
What exactly makes creating data classes in Java so tedious? The core of such classes is the defined list of instance variables that represent the state description of an object. If developers want to design a class that reflects a cube, they can get by with three variables: height, width, and depth. To work with instances of the type cube, however, they must fulfill further formalities: The class requires constructors, getters, and setters. The methods equals , hashCode , and toString must also be overwritten. In most cases, all of this work runs so uniformly that developers have it done automatically by their development environment: Getter and setter are created for each instance variable. All of them should be considered in hashCode , equals , and toString (or worse: hashCode and equals are not implemented at all). Often a constructor is also defined, which can contain all variables as parameters to create a fully initialized object. After this procedure, the class then has just under 65 lines of code, of which about five are sufficient to describe the most important information – the name, the modifier, and what state it holds.
Of course, the surface, edge length, or volume of a cube can also be interesting – but these values are derived from its state description and do not represent new variables. To get the volume of the cube, developers provide a calculateVolume() method, which multiplies the length, height, and width of the cube. These methods also contain information that is important for all developers on the team to understand the cube’s properties. Identifying them among the other nine methods generated earlier by the development environment may be difficult at first glance. The class mixes the “what” with the “how”; it has an unnecessarily high cognitive complexity. The introduction of records is intended to avoid exactly this problem by also modeling data as data and accessing it from the context.
Structure and structure of records
Records are a new type declaration in Java and represent a specialized form of a class. They are intended for a clearly defined purpose but have limitations compared to conventional classes, similar to Enums. The state description of the record is made by defining so-called components, which consist of a type and a name. A simple record describing a cube with the components “height”, “width”, and “depth” is given below:
3
Listing 1 shows a comparable conventional class.
Источник