-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathRecords.txt
317 lines (228 loc) · 12.9 KB
/
Records.txt
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
****** Records ******
Начиная с версии Java 16 в язык была добавлена новая функциональность - Records
(на русском нередко называют "записями"). Records представляют классы, которые
предназначены для создания контейнеров неизменяемых данных. Кроме того, records
позволяют упростить разработку, сократив объем кода.
Для определения классов record применяется ключевое слово record, после которого
идет название и далее в круглых скобках список полей record:
********************************************************************************
record название (поле1, поле2,...полеN){
// тело record
}
********************************************************************************
Date.valueOf(ticket.departureDate().toLocalDate())
Для начала рассмотрим следующий пример:
********************************************************************************
import java.util.Objects;
public class Program{
public static void main (String args[]){
Person tom = new Person("Tom", 36);
System.out.println(tom.toString());
}
}
class Person {
private final String name;
private final int age;
Person(String name, int age) {
this.name = name;
this.age = age;
}
String name() { return name; }
int age() { return age; }
public boolean equals(Object o) {
if (!(o instanceof Person)) return false;
Person other = (Person) o;
return other.name == name && other.age == age;
}
public int hashCode() {
return Objects.hash(name, age);
}
public String toString() {
return String.format("Person[name=%s, age=%d]", name, age);
}
}
********************************************************************************
Здесь определен класс Person, который определяет две константы - name и age:
********************************************************************************
private final String name;
private final int age;
********************************************************************************
Их значения устанавливаются в конструкторе. Больше никак их установить мы не
можем. Таким образом, после создания объекта Person они будут хранить
неизменяемые данные.
Для получения значений name и age предусмотрены одноименные методы без
префикса *.get... :
********************************************************************************
String name() { return name; }
int age() { return age; }
********************************************************************************
Кроме того, здесь переопределены унаследованные от класса Object методы
equals(), hashCode() и toString().
В методе main создаем один объект класса Person и выводит на консоль его
текстовое представление:
********************************************************************************
Person tom = new Person("Tom", 36);
System.out.println(tom.toString());
********************************************************************************
В итоге на экране мы увидим: Person[name=Tom, age=36]
Теперь посмотрим, что нам предлагают Records - определим record, которая будет
полностью аналогична вышеопределенному классу:
********************************************************************************
public class Program{
public static void main (String args[]){
Person tom = new Person("Tom", 36);
System.out.println(tom.toString());
}
}
record Person(String name, int age) { }
********************************************************************************
Records определяются с помощью ключевого слова record, за которым следует название
записи. Дальше идет список полей записи. То есть в данном случае определяется два
поля - name и age. Причем по умолчанию все они будут приватными и иметь
модификатор final.
Также будет создаваться конструктор с двумя параметрами name и age. У каждого поля
автоматически будет создаваться одноименный общедоступный метод для получения
значения это поля. Например, для поля name создается метод name(), который
возвращает значение поля name.
И также автоматически будут создаваться методы equals, hashCode и toString. В общем,
данная record будет полностью аналогична вышеопределенному классу, но при этом
содержит гораздо меньше кода.
При необходимости мы можем вызывать все имеющиеся методы:
********************************************************************************
public class Program{
public static void main (String args[]){
Person tom = new Person("Tom", 36);
System.out.println(tom.name()); // Tom
System.out.println(tom.age()); // 36
System.out.println(tom.hashCode());
Person bob = new Person("Bob", 21);
Person tomas = new Person("Tom", 36);
System.out.println(tom.equals(bob)); // false
System.out.println(tom.equals(tomas)); // true
}
}
record Person(String name, int age){ }
********************************************************************************
****** Конструктор record и его переопределение ******
В примере, выше, применялась форма record:
********************************************************************************
record Person(String name, int age) { }
********************************************************************************
Которая фактически создавала конструктор:
********************************************************************************
Person(String name, int age) {
this.name = name;
this.age = age;
}
********************************************************************************
Этот конструктор называется каноническим. Он принимает параметры, которые
называются также, как и поля record, и передает полям значения соответствующих
параметров.
Тем не менее при необходимости мы можем изменить логику конструктора. Например,
что если при создании объекта будет передан невалидный возраст? Предусмотрим
эту ситуацию, переопределив логику конструктора:
********************************************************************************
public class Program{
public static void main (String args[]){
Person tom = new Person("Tom", -116);
System.out.println(tom.toString());
}
}
record Person(String name, int age) {
Person{
if(age<1 || age > 110){
age = 18;
}
}
}
********************************************************************************
В данном случае если передано невалидное значение, то применяем некоторое значение
по умолчанию (число 18). В итоге фактически мы получим конструктор со следующим
действием:
********************************************************************************
Person(String name, int age) {
if(age<1 || age > 110){
age = 18;
}
this.name = name;
this.age = age;
}
********************************************************************************
В итоге программа выведет на экран следующее: Person[name=Tom, age=18]
Мы можем полностью переопределить канонический конструктор:
********************************************************************************
public class Program{
public static void main (String args[]){
Person tom = new Person("Tom", 36);
System.out.println(tom.toString());
System.out.println(tom.name());
}
}
record Person(String name, int age) {
Person(String name, int age){
if(age < 0 || age > 120) age = 18;
this.name = name;
this.age = age;
}
}
********************************************************************************
Также мы можем определять какие-то другие конструкторы, но все они должны вызывать
канонический конструктор:
********************************************************************************
public class Program{
public static void main (String args[]){
Person tom = new Person("Tom", "Smith", 36);
System.out.println(tom.toString());
}
}
record Person(String name, int age) {
Person(String firstName, String lastName, int age){
this(firstName + " " + lastName, age);
}
}
********************************************************************************
Здесь определен конструктор, который условно принимает имя, фамилию и возраст
пользователя. Этот конструктор вызывает канонический конструктор, передавая ему
значения для полей name и age: this(firstName + " " + lastName, age).
Вывод программы на экран будет: Person[name=Tom Smith, age=36]
****** Переопределение методов ******
Также мы можем переопределить методы, которые имеет record по умолчанию. А это
методы equals(), hashCode() и toString() и методы которые называются также, как
и поля записи, и которые возвращают значения соответствующих полей.
Например, переопределим для записи Person методы toString() и name():
********************************************************************************
public class Program{
public static void main (String args[]){
Person tom = new Person("Tom", 36);
System.out.println(tom.toString());
System.out.println(tom.name());
}
}
record Person(String name, int age) {
public String name() { return "Mister " + name; }
public String toString() {
return String.format("Person %s, Age: %d", name, age);
}
}
********************************************************************************
Вывод программы на экран:
********************************************************************************
Person Tom, Age: 36
Mister Tom
********************************************************************************
****** Ограничения records ******
Следует учитывать, что мы не можем наследовать запись record от других классов.
Также нельзя наследовать классы от records. Однако классы record могут реализовать
интерфейсы. Кроме того, классы record не могут быть абстрактными.
В record нельзя явным образом определять нестатические поля и инициализаторы. Но
можно определять статические переменные и инициализаторы, также как статические и
нестатические методы:
********************************************************************************
record Person(String name, int age){
static int minAge;
static{
minAge = 18;
System.out.println("Static initializer");
}
}
********************************************************************************