Java 21 vs Java 17 vs Java 8 Implementation – Hotel Room Booking System Core Java java Java 17 Java 21 Java 8 by devs5003 - April 3, 2025April 10, 20250 Last Updated on April 10th, 2025Java 21 vs Java 17 vs Java 8 Implementation Java has offered powerful features with each new version significantly over the years, that enhance developer productivity, code readability, and performance. In this analysis based article, we will explore the Hotel Room Booking System implemented in Java 8, Java 17, and Java 21, and compare how modern Java features simplify development while maintaining backward compatibility. This comparison should also helpful as a practical guide for developers considering migration or simply wanting to leverage modern Java effectively. The Hotel Booking System is a real-world use case that involves Dynamic pricing (room types, discounts), Business rules (membership benefits, seasonal offers) & Concurrency (parallel processing for bulk bookings). In this implementation we will Compare coding styles – from verbose Java 8 to concise Java 21 Highlight key language advancements – records, pattern matching, sealed classes/interfaces, text blocks etc. Demonstrate performance & readability improvements in modern Java Table of Contents Toggle Hotel Room Booking System – Problem StatementRequirementsExample ScenariosExample#1: Standard Room (No Discounts)Example#2: Deluxe Room (Gold Member, Long Stay)Example#3: Suite Room (Platinum Member, Long Stay)Java 21 ImplementationUsed Features Till Java 21:Output: Explanation of Java Features Used1) Sealed Interfaces2) Records for Immutable Data3) Pattern Matching in Switch Expressions4) Text Blocks for Readable Multi-line StringsCode Explanation1) Booking System Execution2) Cost Calculation3) Formatted OutputSummary of Java 21 Features UsedJava 17 ImplementationUsed Features Till Java 17:Explanation of Java Features UsedExplanation of CodeOutput: Java 8 ImplementationFeatures Used Till Java 8:Explanation of Java Features UsedOutput:Java 8 Implementation Using EnumFeatures Used Till Java 8:Explanation of Java Features UsedOutput:Comparison Summary: Java 21 vs Java 17 vs Java 8Conclusion Hotel Room Booking System – Problem Statement Develop a Java program that calculates the total booking cost for hotel rooms based on: Room Type (Standard, Deluxe, Suite) Duration of Stay (number of nights) Guest Membership Status (None, Gold, Platinum) The system should apply discounts where applicable and generate a detailed booking summary. Requirements 1. Room Types & Pricing Room Type Base Price (per night) Discount Condition Standard $100 No discount Deluxe $200 10% discount if stay > 3 nights Suite $400 15% discount if stay > 5 nights 2. Membership Discounts Membership Additional Discount Gold 10% Platinum 20% None 0% 3. Business Rules Discounts stack multiplicatively (e.g., Deluxe room discount + Gold discount). The final price must be rounded to 2 decimal places. The system should print a receipt for each booking, showing: Room type Number of nights Membership status Final price Example Scenarios Example#1: Standard Room (No Discounts) Input: Room Type: Standard Nights: 2 Membership: None Calculation: Base Price: $100 × 2 = $200 No discounts apply. Output: Booking: Standard Room - Nights: 2 - Membership: None - Total: $200.00 Example#2: Deluxe Room (Gold Member, Long Stay) Input: Room Type: Deluxe Nights: 4 Membership: Gold Calculation: Base Price: $200 × 4 = $800 10% Deluxe Discount ($800 × 0.10 = $80) → $720 10% Gold Discount ($720 × 0.10 = $72) → Final Price: $648 Output: Booking: Deluxe Room - Nights: 4 - Membership: Gold - Discounts: 10% (Long Stay), 10% (Gold) - Total: $648.00 Example#3: Suite Room (Platinum Member, Long Stay) Input: Room Type: Suite Nights: 6 Membership: Platinum Calculation: Base Price: $400 × 6 = $2400 15% Suite Discount ($2400 × 0.15 = $360) → $2040 20% Platinum Discount ($2040 × 0.20 = $408) → Final Price: $1632 Output: Booking: Suite Room - Nights: 6 - Membership: Platinum - Discounts: 15% (Long Stay), 20% (Platinum) - Total: $1632.00 Now let’s dive into the implementations! Java 21 Implementation Used Features Till Java 21: Sealed Interfaces for Type Safety Record Pattern Matching Pattern Matching in switch Expressions Text Blocks for Readable Multi-line Strings import java.util.List; //Sealed interface for Room Types sealed interface Room permits Standard, Deluxe, Suite {} record Standard() implements Room {} record Deluxe() implements Room {} record Suite() implements Room {} //Sealed interface for Membership Types sealed interface Membership permits Gold, Platinum, None {} record Gold() implements Membership {} record Platinum() implements Membership {} record None() implements Membership {} record Booking(Room room, int nights, Membership membership) {} public class HotelBookingJava21 { public static void main(String[] args) { List<Booking> bookings = List.of( new Booking(new Standard(), 2, new Gold()), new Booking(new Deluxe(), 4, new Platinum()), new Booking(new Suite(), 6, new None()) ); bookings.forEach(booking -> { double totalCost = calculateTotalCost(booking); System.out.println(formatBookingDetails(booking, totalCost)); }); } private static double calculateTotalCost(Booking booking) { double basePrice = getBasePrice(booking.room()); double discount = getRoomDiscount(booking.room(), booking.nights()); double membershipDiscount = getMembershipDiscount(booking.membership()); double totalCost = basePrice * booking.nights(); totalCost -= totalCost * discount; totalCost -= totalCost * membershipDiscount; return totalCost; } private static double getBasePrice(Room room) { return switch (room) { case Standard s -> 100; case Deluxe d -> 200; case Suite su -> 400; }; } private static double getRoomDiscount(Room room, int nights) { return switch (room) { case Deluxe d -> nights > 3 ? 0.10 : 0.0; case Suite su -> nights > 5 ? 0.15 : 0.0; default -> 0.0; }; } private static double getMembershipDiscount(Membership membership) { return switch (membership) { case Gold g -> 0.10; case Platinum p -> 0.20; case None n -> 0.0; }; } private static String formatBookingDetails(Booking booking, double totalCost) { return """ Booking: %s for %d nights (%s member) Final Cost: $%.2f """.formatted(booking.room(), booking.nights(), booking.membership(), totalCost); } } Output: Booking: Standard[] for 2 nights (Gold[] member) Final Cost: $180.00 Booking: Deluxe[] for 4 nights (Platinum[] member) Final Cost: $576.00 Booking: Suite[] for 6 nights (None[] member) Final Cost: $2040.00 Explanation of Java Features Used This Java 21 implementation utilizes several modern features that enhance code clarity, safety, and efficiency. Below are the key features till Java 21 used in the program: 1) Sealed Interfaces Used in: sealed interface Room and sealed interface Membership Purpose: Restricts which classes can implement an interface, ensuring type safety and controlled inheritance. Implementation: sealed interface Room permits Standard, Deluxe, Suite {} sealed interface Membership permits Gold, Platinum, None {} The permits clause explicitly defines the allowed subclasses (Standard, Deluxe, and Suite for Room). This prevents unintended implementations and enhances maintainability. 2) Records for Immutable Data Used in: record Standard() implements Room {} record Deluxe() implements Room {} record Suite() implements Room {} record Gold() implements Membership {} record Platinum() implements Membership {} record None() implements Membership {} record Booking(Room room, int nights, Membership membership) {} Purpose: Concise data representation: No need for explicit constructors, getters, setters, equals(), hashCode(), or toString(). Immutable by default: ensures thread safety. Implementation: Booking is a record that encapsulates room type, duration, and membership. Each room type and membership type is also a record, reducing boilerplate. 3) Pattern Matching in Switch Expressions Used in: private static double getBasePrice(Room room) { return switch (room) { case Standard s -> 100; case Deluxe d -> 200; case Suite su -> 400; }; } Purpose: Eliminates the need for instanceof checks and explicit casting. Enhances readability and maintainability by making switch expressions more concise. Implementation: The switch expression directly extracts the type (Standard, Deluxe, etc.). The use of type patterns (case Deluxe d -> 200;) ensures type safety and eliminates unnecessary casting. 4) Text Blocks for Readable Multi-line Strings Used in: return """ Booking: %s for %d nights (%s member) Final Cost: $%.2f """.formatted(booking.room(), booking.nights(), booking.membership(), totalCost); Purpose: Makes multi-line strings more readable. Avoids unnecessary escape sequences (\n). Allows .formatted() for easy variable substitution. Implementation: Used in formatBookingDetails to format booking information cleanly and efficiently. Code Explanation 1) Booking System Execution The main method initializes a list of bookings and processes each one: List<Booking> bookings = List.of( new Booking(new Standard(), 2, new Gold()), new Booking(new Deluxe(), 4, new Platinum()), new Booking(new Suite(), 6, new None()) ); Uses List.of() (introduced in Java 9) to create an immutable list. Each booking is processed using: bookings.forEach(booking -> { double totalCost = calculateTotalCost(booking); System.out.println(formatBookingDetails(booking, totalCost)); }); 2) Cost Calculation Base Price Calculation private static double getBasePrice(Room room) { return switch (room) { case Standard s -> 100; case Deluxe d -> 200; case Suite su -> 400; }; } Uses pattern matching in switch expressions. Room Discount Calculation private static double getRoomDiscount(Room room, int nights) { return switch (room) { case Deluxe d -> nights > 3 ? 0.10 : 0.0; case Suite su -> nights > 5 ? 0.15 : 0.0; default -> 0.0; }; } Discounts are applied only when the required number of nights is met. Membership Discount Calculation private static double getMembershipDiscount(Membership membership) { return switch (membership) { case Gold g -> 0.10; case Platinum p -> 0.20; case None n -> 0.0; }; } Uses pattern matching switch expressions for Membership Discount Calculation. Final Cost Calculation double totalCost = basePrice * booking.nights(); totalCost -= totalCost * discount; totalCost -= totalCost * membershipDiscount; Applies room discount first. Then membership discount stacks multiplicatively. 3) Formatted Output Uses Text Blocks for improved readability: return """ Booking: %s for %d nights (%s member) Final Cost: $%.2f """.formatted(booking.room(), booking.nights(), booking.membership(), totalCost); .formatted() replaces values dynamically. Ensures two decimal places for monetary precision. Summary of Java 21 Features Used Feature Introduced In Usage in Code Sealed Interfaces Java 17 Restricts Room & Membership implementations Records Java 16 Represents Room, Membership, Booking as immutable data Pattern Matching in Switch Java 21 Cleaner switch statements for Room and Membership Enhanced Switch Expression Java 17 Eliminates break, improves readability Text Blocks Java 15 Cleaner multi-line string formatting for receipts List.of() Java 9 Immutable list for bookings Java 17 Implementation Used Features Till Java 17: record Instead of Class (Immutable Data Structure) Pattern Matching for switch (More Readable switch Expressions) Text Blocks (“””) for Readable Output Formatting import java.util.List; record Booking(String roomType, int nights, String membership) {} public class HotelBookingJava17 { public static void main(String[] args) { List<Booking> bookings = List.of( new Booking("Standard", 2, "Gold"), new Booking("Deluxe", 4, "Platinum"), new Booking("Suite", 6, "None") ); bookings.forEach(booking -> { double totalCost = calculateTotalCost(booking); System.out.println(formatBookingDetails(booking, totalCost)); }); } private static double calculateTotalCost(Booking booking) { double basePrice = getBasePrice(booking.roomType()); double discount = getRoomDiscount(booking.roomType(), booking.nights()); double membershipDiscount = getMembershipDiscount(booking.membership()); double totalCost = basePrice * booking.nights(); totalCost *= (1 - discount); totalCost *= (1 - membershipDiscount); return Math.round(totalCost * 100.0) / 100.0; } private static double getBasePrice(String roomType) { return switch (roomType) { case "Standard" -> 100; case "Deluxe" -> 200; case "Suite" -> 400; default -> 0; }; } private static double getRoomDiscount(String roomType, int nights) { return switch (roomType) { case "Deluxe" -> (nights > 3) ? 0.10 : 0.0; case "Suite" -> (nights > 5) ? 0.15 : 0.0; default -> 0.0; }; } private static double getMembershipDiscount(String membership) { return switch (membership) { case "Gold" -> 0.10; case "Platinum" -> 0.20; default -> 0.0; }; } private static String formatBookingDetails(Booking booking, double totalCost) { return """ Booking: %s for %d nights (%s member) Final Cost: $%.2f """.formatted(booking.roomType(), booking.nights(), booking.membership(), totalCost); } } Explanation of Java Features Used Records (record Booking(…)) Used for defining an immutable data structure for booking details. Auto-generates toString(), equals(), and hashCode(). Enhanced Switch (switch (booking.roomType())) Uses -> for better readability and no need for break statements. More concise and type-safe. Immutable Lists (List.of(…)) Ensures the list of bookings cannot be modified. Explanation of Code calculateTotalCost(Booking booking) → Calculates total cost with all discounts. getBasePrice(String roomType) → Returns base price per room type. getRoomDiscount(String roomType, int nights) → Determines discount based on room type and duration. getMembershipDiscount(String membership) → Returns membership-based discount. formatBookingDetails(Booking booking, double totalCost) → Formats output string properly. Output: Booking: Standard for 2 nights (Gold member) Final Cost: $180.00 Booking: Deluxe for 4 nights (Platinum member) Final Cost: $576.00 Booking: Suite for 6 nights (None member) Final Cost: $2040.00 Java 8 Implementation Features Used Till Java 8: Encapsulation with Traditional Classes (No record support) Lambda Expressions for iteration String.format(): Java 8-compatible alternative to .formatted() in newer Java versions Traditional switch Statement (No pattern matching in switch) import java.util.Arrays; import java.util.List; class Booking { private final String roomType; private final int nights; private final String membership; public Booking(String roomType, int nights, String membership) { this.roomType = roomType; this.nights = nights; this.membership = membership; } public String getRoomType() { return roomType; } public int getNights() { return nights; } public String getMembership() { return membership; } } public class HotelBookingJava8 { public static void main(String[] args) { List<Booking> bookings = Arrays.asList( new Booking("Standard", 2, "Gold"), new Booking("Deluxe", 4, "Platinum"), new Booking("Suite", 6, "None") ); bookings.forEach(booking -> { double totalCost = calculateTotalCost(booking); System.out.println(formatBookingDetails(booking, totalCost)); }); } private static double calculateTotalCost(Booking booking) { double basePrice = getBasePrice(booking.getRoomType()); double discount = getRoomDiscount(booking.getRoomType(), booking.getNights()); double membershipDiscount = getMembershipDiscount(booking.getMembership()); double totalCost = basePrice * booking.getNights(); totalCost *= (1 - discount); totalCost *= (1 - membershipDiscount); return Math.round(totalCost * 100.0) / 100.0; } private static double getBasePrice(String roomType) { switch (roomType) { case "Standard": return 100; case "Deluxe": return 200; case "Suite": return 400; default: return 0; } } private static double getRoomDiscount(String roomType, int nights) { switch (roomType) { case "Deluxe": return (nights > 3) ? 0.10 : 0.0; case "Suite": return (nights > 5) ? 0.15 : 0.0; default: return 0.0; } } private static double getMembershipDiscount(String membership) { switch (membership) { case "Gold": return 0.10; case "Platinum": return 0.20; default: return 0.0; } } private static String formatBookingDetails(Booking booking, double totalCost) { return String.format("Booking: %s for %d nights (%s member) \nFinal Cost: $%.2f", booking.getRoomType(), booking.getNights(), booking.getMembership(), totalCost); } } Explanation of Java Features Used Encapsulation with Traditional Classes (Booking class) Java 8 does not have records (record Booking(…)), so we use a class with private fields, a constructor, and getter methods to ensure encapsulation. Encapsulation keeps data safe and prevents unintended modifications. Enhanced switch Statements Java 8 only supports string-based switch statements, unlike Java 17+ which supports pattern matching in switch. We use switch statements in getBasePrice(), getRoomDiscount(), and getMembershipDiscount(). Lambdas and Functional Programming Java 8 introduced lambda expressions (->) and functional interfaces. We used forEach with a lambda function inside main() to iterate through the list of bookings: bookings.forEach(booking -> { double totalCost = calculateTotalCost(booking); System.out.println(formatBookingDetails(booking, totalCost)); }); This eliminates the need for explicit for-loops, making the code more readable. Arrays.asList() for Immutable Lists In Java 8, we created an immutable list using Arrays.asList(): List<Booking> bookings = Arrays.asList( new Booking("Standard", 2, "Gold"), new Booking("Deluxe", 4, "Platinum"), new Booking("Suite", 6, "None") ); This is an alternative to List.of(), which was introduced in Java 9. String Formatting for Output Replaced text block (“””) with a traditional string. Used String.format() instead of .formatted(). Added explicit \n for line breaks instead of relying on text block formatting. return String.format("Booking: %s for %d nights (%s member) \nFinal Cost: $%.2f", booking.getRoomType(), booking.getNights(), booking.getMembership(), totalCost); Output: Booking: Standard for 2 nights (Gold member) Final Cost: $180.00 Booking: Deluxe for 4 nights (Platinum member) Final Cost: $576.00 Booking: Suite for 6 nights (None member) Final Cost: $2040.00 Java 8 Implementation Using Enum Features Used Till Java 8: Enum (Improves type safety and eliminates raw strings) Constructor with fields in enums (Makes logic more object-oriented and encapsulated) Lambda Expressions for iteration switch-case (Supports clean conditional logic) List from Arrays.asList(): Used to create an immutable list Traditional switch Statement (No pattern matching in switch) String.format(): Java 8-compatible alternative to .formatted() in newer Java versions import java.util.Arrays; import java.util.List; enum RoomType { STANDARD(100), DELUXE(200), SUITE(400); private final double basePrice; RoomType(double basePrice) { this.basePrice = basePrice; } public double getBasePrice() { return basePrice; } public double getRoomDiscount(int nights) { switch (this) { case DELUXE: return nights > 3 ? 0.10 : 0.0; case SUITE: return nights > 5 ? 0.15 : 0.0; default: return 0.0; } } } enum MembershipType { GOLD(0.10), PLATINUM(0.20), NONE(0.0); private final double discount; MembershipType(double discount) { this.discount = discount; } public double getDiscount() { return discount; } } class Booking { private final RoomType roomType; private final int nights; private final MembershipType membership; public Booking(RoomType roomType, int nights, MembershipType membership) { this.roomType = roomType; this.nights = nights; this.membership = membership; } public RoomType getRoomType() { return roomType; } public int getNights() { return nights; } public MembershipType getMembership() { return membership; } } public class HotelBookingJava8Enum { public static void main(String[] args) { List<Booking> bookings = Arrays.asList( new Booking(RoomType.STANDARD, 2, MembershipType.GOLD), new Booking(RoomType.DELUXE, 4, MembershipType.PLATINUM), new Booking(RoomType.SUITE, 6, MembershipType.NONE) ); bookings.forEach(booking -> { double totalCost = calculateTotalCost(booking); System.out.println(formatBookingDetails(booking, totalCost)); }); } private static double calculateTotalCost(Booking booking) { double basePrice = booking.getRoomType().getBasePrice(); double roomDiscount = booking.getRoomType().getRoomDiscount(booking.getNights()); double membershipDiscount = booking.getMembership().getDiscount(); double totalCost = basePrice * booking.getNights(); totalCost -= totalCost * roomDiscount; totalCost -= totalCost * membershipDiscount; return totalCost; } private static String formatBookingDetails(Booking4 booking, double totalCost) { return String.format("Booking: %s for %d nights (%s member)\nFinal Cost: $%.2f", booking.getRoomType(), booking.getNights(), booking.getMembership(), totalCost); } } Explanation of Java Features Used 1. RoomType Enum This enum represents types of hotel rooms: STANDARD, DELUXE, and SUITE. Each enum constant is associated with a base price. basePrice is a private final field (constant for each room type). A constructor assigns the base price to the enum constant. getBasePrice() returns the fixed price for each room type. getRoomDiscount(int nights) applies custom discount logic 2. MembershipType Enum This enum represents customer membership levels. Each level gives a fixed membership discount The constructor sets the discount value for each enum constant getDiscount() returns the discount applicable for a member type Output: Booking: Standard for 2 nights (Gold member) Final Cost: $180.00 Booking: Deluxe for 4 nights (Platinum member) Final Cost: $576.00 Booking: Suite for 6 nights (None member) Final Cost: $2040.00 Comparison Summary: Java 21 vs Java 17 vs Java 8 Feature Java 8 Java 17 Java 21 Data Structure Traditional classes record Booking (immutable) sealed interfaces for strong typing Switch Case String-based switch Enhanced switch Pattern matching switch Functional Programming Lambdas (forEach) Lambdas + record Lambdas + pattern matching Type Safety Less (String-based types) Medium (record) High (Sealed interfaces) Performance Moderate Good Best (avoids redundant checks) Code Readability Good Better Best Conclusion Java 8 is still a strong choice if you’re working in legacy system where newer versions aren’t supported. Java 17 & Java 21 offer better type safety and readability, but Java 8 remains widely used. Java 17 is already quite optimized with records and enhanced switch. Java 21 takes it further by making the program more type-safe and pattern-matching-friendly. If working with simple data models, Java 17 is sufficient. If you want better structure and robustness, Java 21 is the better choice. You may also go through: How to migrate from Java 8 to Java 17? Additionally, another Java 21 Coding Example use case: Hospital Management System. Related