Java
Completed
-
Section 1: Getting Started
-
Section 2: Core Syntax
-
Section 3: Object-Oriented Java
-
Section 4: Collections Framework
-
Section 5: Exception Handling
-
Section 6: Generics
-
Section 7: Functional Java
-
Section 8: Concurrency
-
Section 9: I/O and NIO
-
Section 10: JVM Internals
-
Section 11: Modern Java Features
-
Section 12: Build Tools and Project Structure
-
Section 13: Testing
-
Section 14: Databases and Persistence
-
Section 15: Networking
-
Section 16: Design and Best Practices
-
Section 17: Reflection and Annotations
-
Section 18: Logging and Diagnostics
-
Section 19: Date, Time, and Internationalization
-
Section 20: Java Platform Module System
-
Section 21: Security in Java
-
Section 22: Advanced Collections and Data Structures
-
Section 23: More Concurrency Patterns
-
Section 24: Compression, Files, and System Integration
-
Section 25: GUI Programming
-
Section 26: Practical Projects
-
Section 27: Data Structures and Algorithms
-
Section 28: Interview and Algorithm Practice
-
Section 29: JSON and Data Interchange
-
Section 30: More Concurrency Utilities
-
Section 31: More Collections and Streams Practice
-
Section 32: More File and System Programming
-
Section 33: Standard Library Deep Dive
-
Section 34: More Practice and Drills
-
Section 35: More Testing and Quality
-
Section 36: More Design Patterns and Architecture
-
Section 37: Career and Ecosystem
-
Section 38: More OOP and Architecture Practice
-
Section 39: More Enterprise Concepts
-
Section 40: Advanced JavaFX
-
Section 41: More Interview Practice
110: Pattern Matching for instanceof
If you've been writing Java for a while, you've probably spent a fair amount of time writing the same three lines of code over and over: check if an object is a certain type, cast it to that type, and then finally use it. It's repetitive, and honestly, it's a bit of a waste of space. Pattern matching for instanceof finally fixes this.
Defining our Notification system
To show you how this actually cleans up a codebase, let's build a simple notification dispatcher. Imagine we have a base Notification class and a few specific types: one for emails and one for SMS. I'll keep the classes lean since we're focusing on the logic, not the boilerplate.
class Notification {}
class EmailNotification extends Notification {
String emailAddress = "dev@example.com";
String subject = "Hello!";
}
class SmsNotification extends Notification {
String phoneNumber = "555-0123";
String message = "Your code is 1234";
}
The clunky way we used to do it
Before pattern matching, if I wanted to process these notifications, I had to do a "check-then-cast" dance. I've written this thousands of times, and it always felt like I was stuttering in code. Look at this:
public void send(Notification n) {
if (n instanceof EmailNotification) {
EmailNotification email = (EmailNotification) n; // This line is redundant
System.out.println("Sending email to " + email.emailAddress);
} else if (n instanceof SmsNotification) {
SmsNotification sms = (SmsNotification) n; // And again...
System.out.println("Sending SMS to " + sms.phoneNumber);
}
}
See that? I'm telling Java "is this an EmailNotification?" and then immediately saying "Okay, I know it is, now treat it like one." It's tedious.
Using pattern matching to cut the noise
Now, here is the modern way. We can declare the variable right inside the instanceof check. If the check returns true, Java automatically casts the object and assigns it to that variable for us.
public void send(Notification n) {
if (n instanceof EmailNotification email) {
// 'email' is already cast and ready to go
System.out.println("Sending email to " + email.emailAddress);
} else if (n instanceof SmsNotification sms) {
System.out.println("Sending SMS to " + sms.phoneNumber);
}
}
It's much tighter. I'm just saying, "If this is an EmailNotification, call it email and let me use it."
A mistake I made with variable scope
When I first started using this, I tried to be a bit too clever. I thought I could declare a variable in the if block and then use it later in the method. I tried something like this:
if (n instanceof EmailNotification email) {
System.out.println("Found an email!");
}
// I tried to use 'email' here, but the compiler yelled at me.
System.out.println(email.subject);
I forgot that the binding variable (email) only exists within the scope where the instanceof check is true. If the check fails, the variable is never created. You can't just leak that variable into the rest of the method. Once you exit that if block, email is gone. It's a small detail, but it tripped me up for a few minutes the first time I used it.
One last tip: you can even use these variables inside more complex boolean expressions. For example, if (n instanceof EmailNotification email && email.subject.contains("Urgent")) works perfectly because Java evaluates from left to right. If it's not an EmailNotification, it stops right there and never tries to access the subject, avoiding a NullPointerException or a class cast error.
📋 Practical Task
Refactoring the Payment Processor
You are working on a legacy payment system. The current processPayment method uses the old "check-then-cast" style. Your task is to refactor this method to use Pattern Matching for instanceof to make the code more concise.
Starting Code:
class Payment {}
class CreditCardPayment extends Payment { String cardNumber = "1234-5678"; }
class PayPalPayment extends Payment { String email = "user@example.com"; }
public class PaymentProcessor {
public void process(Payment p) {
if (p instanceof CreditCardPayment) {
CreditCardPayment cc = (CreditCardPayment) p;
System.out.println("Processing card: " + cc.cardNumber);
} else if (p instanceof PayPalPayment) {
PayPalPayment pp = (PayPalPayment) p;
System.out.println("Processing PayPal: " + pp.email);
}
}
}
Requirements:
- Remove the explicit casting lines.
- Incorporate the variable declarations directly into the
instanceofchecks. - Ensure the logic remains exactly the same.
There are no comments for now.