Pojo, JavaBean, DTO and Records
This document is an attempt to reveal and disambiguate the differences and similarities.
POJO
POJOs are simple objects that do not depend on any framework or library. They typically have a default constructor, fields may have any of the access modifiers or implement getters and setters.
POJOs are platform-independent because they are written in pure Java and do not rely on any specific platform or technology. It seems that it can be extended from any other class or implement other interfaces than Serializable. They can be extended without affecting the overall functionality of the system, making them ideal for building modular and scalable applications, but the childs are no longer a POJO.
public class Bill {
private String billNumber;
private Date date;
private double amount;
private boolean isPaid;
private String customerName;
private List<Item> items;
public Bill(String customerName) {
this.customerName = customerName;
this.items = new ArrayList<Item>();
}
public void addItem(Item item) {
this.items.add(item);
}
public double getTotal() {
for (Item item : this.items) {
amount+= item.getPrice() * item.getQuantity();
}
return amount;
}
public void printBill() {
System.out.println("Bill Number: " + this.billNumber);
System.out.println("Customer Name: " + this.customerName);
System.out.println("Item Name\tQuantity\tPrice");
for (Item item : this.items) {
System.out.println(item.getName() + "\t\t" + item.getQuantity() + "\t\t$" + item.getPrice());
}
System.out.println("Total Amount: $" + this.getTotal());
}
}
class Item {
private String name;
private int quantity;
private double price;
public Item(String name, int quantity, double price) {
this.name = name;
this.quantity = quantity;
this.price = price;
}
public String getName() {
return this.name;
}
public int getQuantity() {
return this.quantity;
}
public double getPrice() {
return this.price;
}
}
JavaBean
JavaBean are the next step, because all the Beans can be POJOs, but not all the POJOs are Beans. A JavaBean strictly encapsulates their state, all fields must be private and only can be accessed for public Getters and Setters. All of them must follow Java Naming Conventions, should implement the Serializable interface, this makes it easy to use frameworks that rely on introspection to automatically access and manipulate the properties of the bean.
import java.io.Serializable;
import java.util.Date;
public class Bill implements Serializable {
private String billNumber;
private Date date;
private double amount;
private boolean isPaid;
public Bill() {}
public Bill(String billNumber, Date date, double amount, boolean isPaid) {
this.billNumber = billNumber;
this.date = date;
this.amount = amount;
this.isPaid = isPaid;
}
public String getBillNumber() {
return billNumber;
}
public void setBillNumber(String billNumber) {
this.billNumber = billNumber;
}
public Date getDate() {
return date;
}
public void setDate(Date date) {
this.date = date;
}
public double getAmount() {
return amount;
}
public void setAmount(double amount) {
this.amount = amount;
}
public boolean isPaid() {
return isPaid;
}
public void setPaid(boolean isPaid) {
this.isPaid = isPaid;
}
@Override
public String toString() {
return "Bill [billNumber=" + billNumber + ", date=" + date + ", amount=" + amount + ", isPaid=" + isPaid + "]";
}
}
Record
Record is an evolution for JavaBeans ( jdk14+), because Record classes are designed to be compact, with a concise syntax that allows them to be easily created and used. Also are immutable, meaning their state cannot be changed once they are instantiated. This ensures that the values of their fields remain constant, which can be useful for guaranteeing data integrity and thread safety.
Record classes inherit several methods automatically, including a constructor, accessors for each field, and equals(), hashCode(), and toString() methods.
The main difference between his two predecessors is itself is final, meaning it cannot be subclassed. This ensures that its behavior is consistent across different contexts and avoids potential issues with overriding methods.
import java.time.LocalDate;
public record Bill(String billNumber, LocalDate date, double amount, boolean isPaid) {}
DTO
DTO is a Pattern introduced by (All raise, please) Martin Flowler, we can implement it using POJO or Records if we are using the latest version of JDK, which means it is a simple Java object that encapsulates data and has no behavior. Is used to transfer data between different layers of an application, such as between a client and a server or between different modules of the same application.
DTO should be serializable, which means it can be converted to a stream of bytes and transmitted across a network or stored in a file. This is important for transferring data between different systems.
With POJO and Record Examples
import java.io.Serializable;
import java.time.LocalDate;
public class BillDTO implements Serializable {
private String billNumber;
private LocalDate date;
private double amount;
private boolean isPaid;
public BillDTO() {}
public BillDTO(String billNumber, LocalDate date, double amount, boolean isPaid) {
this.billNumber = billNumber;
this.date = date;
this.amount = amount;
this.isPaid = isPaid;
}
public String getBillNumber() {
return billNumber;
}
public void setBillNumber(String billNumber) {
this.billNumber = billNumber;
}
public LocalDate getDate() {
return date;
}
public void setDate(LocalDate date) {
this.date = date;
}
public double getAmount() {
return amount;
}
public void setAmount(double amount) {
this.amount = amount;
}
public boolean isPaid() {
return isPaid;
}
public void setPaid(boolean isPaid) {
this.isPaid = isPaid;
}
}
import java.time.LocalDate;
public record BillDTO(String billNumber, LocalDate date, double amount, boolean isPaid) {}