Did you realize that some classes in the application are being used only to hold or transfer data? In addition to that, these classes often contain lots of codes (equals, hasCode, and getters of fields, etc.) other than fields.
Let’s imagine that we have a DailyTask class. This class is used in only holding or transferring data, it is set when it is created. The data it holds is either never changed or rarely changed.
DailyTask
package com.practice;
import java.time.LocalDate;
import java.time.LocalTime;
import java.util.Objects;
public class DailyTask {
private final String id;
private final String topic;
private final String description;
private final LocalTime startTime;
private final LocalTime endTime;
private final LocalDate date;
public DailyTask(String id,
String topic,
String description,
LocalTime startTime,
LocalTime endTime,
LocalDate date) {
this.id = id;
this.topic = topic;
this.description = description;
this.startTime = startTime;
this.endTime = endTime;
this.date = date;
}
public String getId() {
return id;
}
public String getTopic() {
return topic;
}
public String getDescription() {
return description;
}
public LocalTime getStartTime() {
return startTime;
}
public LocalTime getEndTime() {
return endTime;
}
public LocalDate getDate() {
return date;
}
@Override
public boolean equals(final Object o) {
if (this == o) {
return true;
}
if (o == null) {
return false;
}
if (getClass() != o.getClass()) {
return false;
}
DailyTask dailyTask = (DailyTask) o;
return Objects.equals(id, dailyTask.id) &&
Objects.equals(topic, dailyTask.topic) &&
Objects.equals(description, dailyTask.description) &&
Objects.equals(startTime, dailyTask.startTime) &&
Objects.equals(endTime, dailyTask.endTime) &&
Objects.equals(date, dailyTask.date);
}
@Override
public int hashCode() {
return Objects.hash(id, topic, description, startTime, endTime, date);
}
@Override
public String toString() {
return "DailyTask{" +
"id='" + id + '\'' +
", topic='" + topic + '\'' +
", description='" + description + '\'' +
", startTime=" + startTime +
", endTime=" + endTime +
", date=" + date +
'}';
}
}
We can use it and see the output as follows.
DailyTaskTest
package com.practice;
import java.time.LocalDate;
import java.time.LocalTime;
import java.util.List;
import java.util.Map;
import java.util.UUID;
import java.util.stream.Collectors;
public class Test {
public static void main(String[] args) {
DailyTask dailyTaskProjectMeeting = new DailyTask(
UUID.randomUUID().toString(),
"Project Meeting",
"Discussion meeting about newly started project",
LocalTime.of(14, 30),
LocalTime.of(15, 30),
LocalDate.of(2024,3,10)
);
DailyTask dailyTaskFixingProductionBug = new DailyTask(
UUID.randomUUID().toString(),
"Fix Production Bug",
"In the production environment, some accounts takes technical issue about divide 0",
LocalTime.of(8, 30),
LocalTime.of(10, 30),
LocalDate.of(2024,3,10)
);
List<DailyTask> dailyTaskList = List.of(dailyTaskProjectMeeting, dailyTaskFixingProductionBug);
System.out.println("-----My Daily Tasks-----");
dailyTaskList.forEach(System.out::println);
System.out.println();
System.out.println("-----My Daily Tasks By Topic-----");
Map<String, List<DailyTask>> dailyTaskByTopic = dailyTaskList.stream().collect(Collectors.groupingBy(DailyTask::getTopic));
dailyTaskByTopic.forEach((key, value) -> System.out.println(key + "\n" + value));
}
}
Output
-----My Daily Tasks-----
DailyTask{id='c2c5645a-1401-41a5-884d-7ad2a3001139', topic='Project Meeting', description='Discussion meeting about newly started project', startTime=14:30, endTime=15:30, date=2024-03-10}
DailyTask{id='d31b6a6f-7fdb-4ea7-a290-e8f7ffc14653', topic='Fix Production Bug', description='In the production environment, some accounts takes technical issue about divide 0', startTime=08:30, endTime=10:30, date=2024-03-10}
-----My Daily Tasks By Topic-----
Fix Production Bug
[DailyTask{id='d31b6a6f-7fdb-4ea7-a290-e8f7ffc14653', topic='Fix Production Bug', description='In the production environment, some accounts takes technical issue about divide 0', startTime=08:30, endTime=10:30, date=2024-03-10}]
Project Meeting
[DailyTask{id='c2c5645a-1401-41a5-884d-7ad2a3001139', topic='Project Meeting', description='Discussion meeting about newly started project', startTime=14:30, endTime=15:30, date=2024-03-10}]
Did you realize it contains 90 lines? DailyTask has many rows despite of it class only used to hold a few data. There are a lot of boilerplate codes in here but we have an alternative approach to avoid this boilerplate code. This alternative approach is the Record introduced in Java 14. We can re write it as follows…
DailyTask as Record
package com.practice;
import java.time.LocalDate;
import java.time.LocalTime;
public record DailyTask(String id,
String topic,
String description,
LocalTime startTime,
LocalTime endTime,
LocalDate date) {
}
If we need to use it, we can use it and see the output as follows…
package com.practice;
import java.time.LocalDate;
import java.time.LocalTime;
import java.util.List;
import java.util.Map;
import java.util.UUID;
import java.util.stream.Collectors;
public class Test {
public static void main(String[] args) {
DailyTask dailyTaskProjectMeeting = new DailyTask(
UUID.randomUUID().toString(),
"Project Meeting",
"Discussion meeting about newly started project",
LocalTime.of(14, 30),
LocalTime.of(15, 30),
LocalDate.of(2024,3,10)
);
DailyTask dailyTaskFixingProductionBug = new DailyTask(
UUID.randomUUID().toString(),
"Fix Production Bug",
"In the production environment, some accounts takes technical issue about divide 0",
LocalTime.of(8, 30),
LocalTime.of(10, 30),
LocalDate.of(2024,3,10)
);
List<DailyTask> dailyTaskList = List.of(dailyTaskProjectMeeting, dailyTaskFixingProductionBug);
System.out.println("-----My Daily Tasks-----");
dailyTaskList.forEach(System.out::println);
System.out.println();
System.out.println("-----My Daily Tasks By Topic-----");
Map<String, List<DailyTask>> dailyTaskByTopic = dailyTaskList.stream().collect(Collectors.groupingBy(DailyTask::topic));
dailyTaskByTopic.forEach((key, value) -> System.out.println(key + "\n" + value));
}
}
Output
-----My Daily Tasks-----
DailyTask[id=5644fbd5-13a2-4b04-9118-9ed0274acb09, topic=Project Meeting, description=Discussion meeting about newly started project, startTime=14:30, endTime=15:30, date=2024-03-10]
DailyTask[id=5049c6bf-8b84-44be-be9e-97227b00852c, topic=Fix Production Bug, description=In the production environment, some accounts takes technical issue about divide 0, startTime=08:30, endTime=10:30, date=2024-03-10]
-----My Daily Tasks By Topic-----
Fix Production Bug
[DailyTask[id=5049c6bf-8b84-44be-be9e-97227b00852c, topic=Fix Production Bug, description=In the production environment, some accounts takes technical issue about divide 0, startTime=08:30, endTime=10:30, date=2024-03-10]]
Project Meeting
[DailyTask[id=5644fbd5-13a2-4b04-9118-9ed0274acb09, topic=Project Meeting, description=Discussion meeting about newly started project, startTime=14:30, endTime=15:30, date=2024-03-10]]
Although the previous version contained 90 lines, the new version contains 13 lines. Almost 7 times less code. It is soft and clean, isn’t it?
Methods in Record
Records can contain methods and you can use it if you want.
Practice— 1
DailyTask with Method
public record DailyTask(String id,
String topic,
String description,
LocalTime startTime,
LocalTime endTime,
LocalDate date) {
public long getTotalTime() {
return startTime.until(endTime, ChronoUnit.MINUTES);
}
}
Test
package com.practice;
import java.time.LocalDate;
import java.time.LocalTime;
import java.util.UUID;
public class Test {
public static void main(String[] args) {
DailyTask dailyTaskProjectMeeting = new DailyTask(
UUID.randomUUID().toString(),
"Project Meeting",
"Discussion meeting about newly started project",
LocalTime.of(14, 30),
LocalTime.of(15, 30),
LocalDate.of(2024,3,10)
);
System.out.println(dailyTaskProjectMeeting.getTotalTime());
}
}
Output
60
Let’s check what we can use it as various.
Practice— 2
DailyTask with Various Methods
package com.practice;
import java.time.LocalDate;
import java.time.LocalTime;
import java.time.temporal.ChronoUnit;
public record DailyTask(String id,
String topic,
String description,
LocalTime startTime,
LocalTime endTime,
LocalDate date) {
private static final String MOTIVATION_QUOTES = "Don’t wait";
private static final LocalTime lunchTime = LocalTime.of(12, 0);
public long getTotalTime() {
return startTime.until(endTime, ChronoUnit.MINUTES);
}
public LocalTime getLunchTime() {
return lunchTime;
}
public static String getMotivationQuotes() {
return MOTIVATION_QUOTES;
}
}
Test
package com.practice;
import java.time.LocalDate;
import java.time.LocalTime;
import java.util.UUID;
public class Test {
public static void main(String[] args) {
DailyTask dailyTaskProjectMeeting = new DailyTask(
UUID.randomUUID().toString(),
"Project Meeting",
"Discussion meeting about newly started project",
LocalTime.of(14, 30),
LocalTime.of(15, 30),
LocalDate.of(2024,3,10)
);
System.out.println("Out topic is " + dailyTaskProjectMeeting.topic());
System.out.println(dailyTaskProjectMeeting.getMotivationQuotes());
System.out.println("Lunch time is " + dailyTaskProjectMeeting.getLunchTime());
System.out.println("Total time of task is " + dailyTaskProjectMeeting.getTotalTime());
System.out.println(DailyTask.getMotivationQuotes());
}
}
Output
Our topic is Project Meeting
Don’t wait
Lunch time is 12:00
Total time of task is 60
Don’t wait
As you can see, we have lots of options here with method but don’t remember, if we add instance variable, we will take error!
public int highPriority;
public int lowPriority = 3;
The compiler won’t let to write it. It will give error and say “Instance field is not allowed in record”.
Records with Inheritance
Records are final classes but you can implement any interface class.
package com.practice;
import java.time.LocalDate;
import java.time.LocalTime;
public record DailyTask(String id,
String topic,
String description,
LocalTime startTime,
LocalTime endTime,
LocalDate date) extends Task {
}
The compiler will give error and say “No extends clause allowed for record”. On the other hand, if you try to implement any interface, it will not be any issue.
package com.practice;
import java.time.LocalDate;
import java.time.LocalTime;
public record DailyTask(String id,
String topic,
String description,
LocalTime startTime,
LocalTime endTime,
LocalDate date) implements CriticalTask {
}
Records with Constructor
Custom constructors can be created in a Record but non-canonical record constructor must delegate to another constructor.
package com.practice;
import java.time.LocalDate;
import java.time.LocalTime;
public record DailyTask(String id,
String topic,
String description,
LocalTime startTime,
LocalTime endTime,
LocalDate date) {
public DailyTask(String id) {
this(id, "DummyTopic");
}
public DailyTask(String id, String topic) {
this(id, topic, "DummyDescription");
}
public DailyTask(String id, String topic, String description) {
this(id, topic, description, LocalTime.now());
}
public DailyTask(String id, String topic, String description, LocalTime startTime) {
this(id, topic, description, startTime, LocalTime.now(), LocalDate.now());
}
public DailyTask(String id, String topic, String description, LocalTime startTime, LocalTime endTime) {
this(id, topic, description, startTime, endTime, LocalDate.now());
}
}
Final
I hope everything is clear for you. If you don’t understand any part, feel free to ask me I will be happy to answer your questions. In addition to that, if you have a suggestion to improve this story, feel free to give a response. I will update it as soon as possible based on the suggestions.