Java mein Data-Oriented Programming: Ek Naya Paradigm Aur Tumhare Placement Code Pe Iska Real Impact
By Divyansh Bhardwaj | April 2026 ~10 min read · Prerequisite: Records aur Sealed Classes ka naam suna ho, basic Java OOP comfortable ho
Sochо — tumhara last semester ka Java project kuch aisa dikhta tha:
UserDTO, UserVO, UserMapper, UserResponse, UserBuilder...
Ek User ke liye paanch classes. Har class mein getters, setters, equals(), hashCode(). Aur phir bhi koi nahi bata sakta tha ki is object ki actual state kya thi runtime pe.
Agar yeh relatable laga — toh yeh blog tumhare liye hai.
Object-Oriented Programming ne ek powerful idea diya: state aur behavior ko saath rakhao. User object ke andar login() bhi hoga, getAddress() bhi hoga, updateProfile() bhi hoga.
Real systems mein yeh model kaam karta hai — jab objects genuinely behavior-rich hote hain. Lekin modern software ka ek bada hissa kuch aur hai: data ko ek jagah se doosri jagah carry karna. REST API response, event payload, database row, gRPC message — inhe behavior nahi chahiye. Inhe sirf accurate, immutable, type-safe data representation chahiye.
Aur usi gap ke liye OOP ka toolbox thoda insufficient feel hota hai.
Data-Oriented Programming ka pitch simple hai:
Data ko pehle socho. Behavior ko baad mein, alag rakhо. Aur data ko immutable banao.
Brian Goetz — Java Language Architect at Oracle — ne June 2022 mein formally Java ke liye describe kiya. Original 4 principles the:
- Data is immutable — ek baar banao, phir change mat karo
- Model the data, the whole data, and nothing but the data — sirf wahi represent karo jo zaruri hai
- Make illegal states unrepresentable — invalid combinations ko type system se hi rokao
- Validate at the boundary — data enter karte waqt validate karo, andar jaane ke baad trust karo
May 2024 mein version 1.1 aaya jisme 4th principle revise hua: "Separate operations from data." Matlab behavior ko data ke structure ke andar mat cramming karo.
Yeh OOP ki jagah nahi le raha — yeh data modeling layer ke liye ek better lens hai. OOP bilkul valid rehta hai apni jagah pe. Dono kab use karne chahiye — woh thoda aage clearly delineate karenge.
Java 21 (current LTS) mein teen features hain jo milke DOP enable karte hain. Inhe Project Amber kehte hain — aur yeh teen "amigos" ek dusre ke saath designed hain.
// Traditional approach — ~20 lines
public class Point {
private final int x;
private final int y;
public Point(int x, int y) {
this.x = x;
this.y = y;
}
public int getX() { return x; }
public int getY() { return y; }
@Override public boolean equals(Object o) { /* ... */ }
@Override public int hashCode() { /* ... */ }
@Override public String toString() { /* ... */ }
}
// Record — ek line
record Point(int x, int y) {}Record basically ek "nominal tuple" hai — tum compiler ko clearly bol rahe ho ki "yeh class sirf data carry karne ke liye hai." Automatic equals(), hashCode(), toString(), aur canonical constructor — sab milta hai.
⚠️ Placement Interview Gotcha — Record Accessor NamingRecords JavaBeans convention follow nahi karte. Accessor
x()hoga —getX()nahi.Point p = new Point(3, 4); p.x(); // ✅ correct p.getX(); // ❌ compile error — record mein getX() exist nahi kartaSpring aur JPA background wale developers yahan commonly trip karte hain — interviewers yeh jaante hain.
Records kab use karein: DTOs, API responses, value objects, event payloads — koi bhi class jo sirf data carry kare.
Records kab use na karein:
⚠️ JPA/Hibernate Entities ke liye Records mat use karoJPA ko no-arg constructor chahiye aur mutable state chahiye — records dono ko disallow karte hain. Database entities ke liye traditional classes hi theek hain. Records ka use karo DTO layer mein — entity layer mein nahi.
Maan lo tumhara order kuch states mein ho sakta hai. Traditional approach:
class Order {
String status; // "pending"? "PENDING"? "Pending "? typo?
// Koi bhi string valid hai — compiler ko kya pata?
}DOP approach:
sealed interface OrderStatus
permits Pending, Shipped, Delivered, Cancelled {}
record Pending(LocalDateTime placedAt) implements OrderStatus {}
record Shipped(String trackingId, LocalDateTime shippedAt) implements OrderStatus {}
record Delivered(LocalDateTime deliveredAt) implements OrderStatus {}
record Cancelled(String reason) implements OrderStatus {}Ab ek Shipped order ke paas trackingId hoga hi — kyunki type system enforce kar raha hai. Koi null check nahi. Koi status.equals("SHIPPED") nahi. Agar koi invalid state banana chaaho — compile time pe rokega.
double calculateDiscount(OrderStatus status) {
// Production code mein BigDecimal preferred hoti hai financial values ke liye
return switch (status) {
case Pending p -> 0.0;
case Shipped s -> 0.05;
case Delivered d -> 0.10;
case Cancelled c -> 0.0;
};
}Notice karo — koi default case nahi hai. Agar kal koi naya OrderStatus add karo, compiler turant bata dega ki yahan handle nahi hua. Yeh exhaustiveness — aur placement interviews mein yeh exactly woh concept hai jo interviewers dekhna chahte hain.
Ek notification system banate hain. Teen types hain: Email, SMS, Push.
OOP approach — traditional:
class Notification {
private String type; // "EMAIL", "SMS", "PUSH"
private String recipient;
private String subject; // sirf email ke liye — SMS mein null rahega
private String phoneNumber; // sirf SMS ke liye — email mein null rahega
private String deviceToken; // sirf push ke liye — baaki dono mein null
// getters, setters, null checks har jagah...
}Problem: phoneNumber ek Email notification object mein bhi exist karta hai — as null. Tumhara code null check karta rahega. Aur agar koi setPhoneNumber() kisi Email notification pe call kare — koi roknewala nahi.
DOP approach — sealed + records:
sealed interface Notification
permits EmailNotification, SmsNotification, PushNotification {}
record EmailNotification(
String to,
String subject,
String body
) implements Notification {}
record SmsNotification(
String phoneNumber,
String message
) implements Notification {}
record PushNotification(
String deviceToken,
String title,
String body
) implements Notification {}phoneNumber sirf SmsNotification mein exist karta hai — kyunki wahi uska ghar hai. Ab processing:
void send(Notification n) {
switch (n) {
case EmailNotification e ->
emailService.send(e.to(), e.subject(), e.body());
case SmsNotification s ->
smsService.send(s.phoneNumber(), s.message());
case PushNotification p ->
pushService.send(p.deviceToken(), p.title(), p.body());
}
}Koi null nahi. Koi instanceof chain nahi. Kal ek naya WhatsAppNotification add karo — compiler batayega ki send() mein handle karna hai.
Yeh question interviews mein directly aata hai: "Toh aap records kab use karenge aur kab nahi?"
| DOP (Records + Sealed + Pattern Matching) | OOP (Traditional Classes) | |
|---|---|---|
| Best fit | DTOs, API responses, event payloads, value objects, state machines with fixed variants | Business logic services, infrastructure classes, behavior-rich domain objects |
| Data | Immutable, transparent | Encapsulated, mutable as needed |
| Behavior | Separate (external functions/methods) | Co-located with data |
| Null safety | Structural — type system se enforce | Manual — null checks required |
| Inheritance | Sealed hierarchy — controlled | Open — flexible |
| JPA Entities | ❌ Not suitable | ✅ Use traditional classes |
Rule of thumb: Agar class sirf data carry kar rahi hai — DOP. Agar class kuch karta bhi hai — OOP. Real applications mein dono saath chalte hain — yeh either/or nahi hai.
January 2026 mein Brian Goetz ne OpenJDK pe ek major design note publish kiya: "Data-Oriented Programming for Java: Beyond Records."
Problem: Records ke saath ek "cliff" hai. Jaise hi records se thoda deviate karo — mutable field chahiye, ya internal representation API se different ho, ya inheritance chahiye — saare record benefits chale jaate hain. Tum wapas blank traditional class pe ho, sab manually likhna padta hai.
Proposed solution: Carrier Classes.
// ⚠️ EXPLORATORY SYNTAX — yeh Java mein abhi exist nahi karta
carrier class Money(long amount, Currency currency) {
component long amount;
component Currency currency;
// Internal representation API se different ho sakti hai
// Mutable fields allowed hain
// Inheritance possible hai
}Key idea: carrier class ek "almost record" hai. State description wahi hai, pattern matching wahi hai — lekin constraints relaxed hain.
Status as of April 2026: Purely exploratory. Koi JEP number nahi, koi target Java version nahi, koi release timeline nahi — sirf ek design note aur active Amber mailing list discussion (last update: Brian Goetz, February 25, 2026). Production planning mein include mat karo — lekin Project Amber ka direction clearly samajh aata hai: DOP Java ka primary data modeling paradigm ban raha hai.
Java 21 (current LTS) pe yeh sab production-ready hai:
| Feature | Java Version | Status | Kab Use Karein |
|---|---|---|---|
| Records | Java 16 | ✅ Stable | DTOs, value objects, responses |
| Sealed Classes | Java 17 | ✅ Stable | Fixed variants, state machines |
Pattern Matching (instanceof) |
Java 16 | ✅ Stable | Type-safe casting |
Pattern Matching (switch) |
Java 21 | ✅ Stable | Exhaustive data processing |
| Record Patterns | Java 21 | ✅ Stable | Nested deconstruction |
| Carrier Classes | TBD | 🔬 Exploratory | — |
Teen concrete steps jinhe aaj start kar sakte ho:
1. DTOs aur Value Objects ko Records mein convert karo
Agar koi class sirf data carry kar rahi hai — UserDTO, ProductResponse, AddressVO — woh record ban sakti hai. Exception: JPA entities nahi.
2. Status/State classes ko Sealed Interfaces mein shift karo Jab tumhare paas fixed set of variants hain aur har variant ka alag data hoga — sealed interface + records perfect fit hai.
3. instanceof chains ko Pattern Matching se replace karo
// Purana — verbose aur unsafe cast
if (notification instanceof EmailNotification) {
EmailNotification e = (EmailNotification) notification;
// ...
}
// Naya — concise aur type-safe
if (notification instanceof EmailNotification e) {
// e directly available hai, no cast needed
}DOP especially important hai microservices aur distributed systems ke context mein — jo ki 2026 mein har company ka default architecture hai.
Jab data ek service se doosri service mein jaata hai — REST response, Kafka event, gRPC message — woh naturally immutable aur transparent hota hai. OOP ka "encapsulation" wahan meaningful nahi hota kyunki state cross-boundary nahi jaati, sirf data jaata hai. DOP usi reality ko code mein accurately represent karta hai.
BellSoft ke 2024 Java Developer Survey ke according, Records already 55% developer adoption pe hain — sabse popular modern Java feature. Yeh number tumhare interviews mein directly relevant hai: interviewers expect karte hain ki final year students records aur pattern matching comfortable ho ke use karein.
Java ab sirf "everything is an object" nahi raha.
Records, Sealed Classes, Pattern Matching — yeh teen features milke data ko Java mein first-class citizen banate hain. Aur 2026 mein, Project Amber team Carrier Classes ke saath isko aur aage le jaane ki taiyari mein hai.
Toh agli baar jab tum ek UserDTO banana shuru karo — ek second ruko aur poochho:
"Kya yeh ek record ho sakta hai?"
Chances hain — haan, ho sakta hai.
| Resource | Type | Start Karo Agar... |
|---|---|---|
| Baeldung: DOP in Java (Yahtzee example) | Tutorial | Pehli baar DOP practically try karna hai |
| Inside Java Newscast #105 — Carrier Classes | Video | 2026 mein Java kahan ja raha hai jaanna hai |
| Manning: Data-Oriented Programming in Java — Chris Kiehl | Book | Deeply samajhna aur production mein apply karna hai |
| Brian Goetz — Original DOP Article (InfoQ, June 2022) | Article | Concept ki origin aur philosophy samajhni hai |
| DOP v1.1 Introduction — Nicolai Parlog (inside.java, May 2024) | Article Series | Principles ko ek-ek karke formally padhna hai |
| "Beyond Records" Design Note (openjdk.org, Jan 2026) | Design Doc | Language design mein genuinely curious ho |
| Inside Java Podcast Ep. 47 — Carrier Classes | Podcast | Commute pe sunna chahte ho |
| Devoxx Belgium 2025 — "The Three Amigos in Java" | Conference Talk | Visual, example-driven explanation prefer karte ho |