google-site-verification: google61fe8ba583a51912.html
Pattern Matching for instanceof in Java: Complete Guide

Pattern matching for instanceof is a powerful feature introduced in Java 16 (as a preview in Java 14) that simplifies type checking and casting, making code more readable and less error-prone.


1. Basic Pattern Matching with instanceof

Traditional vs Pattern Matching Approach

public class BasicPatternMatching { public static void main(String[] args) { demonstrateBasicPatternMatching(); demonstrateWithCollections(); demonstrateNullSafety(); } public static void demonstrateBasicPatternMatching() { System.out.println("=== Basic Pattern Matching ==="); Object obj = "Hello, Pattern Matching!"; // ❌ Traditional approach (verbose) if (obj instanceof String) { String str = (String) obj; System.out.println("Traditional: " + str.toUpperCase()); } // ✅ Pattern matching approach (concise) if (obj instanceof String str) { System.out.println("Pattern Matching: " + str.toUpperCase()); } // More examples with different types Object number = 42; Object list = List.of(1, 2, 3); processObject(number); processObject(list); processObject("Test String"); } public static void processObject(Object obj) { System.out.println("\nProcessing object: " + obj); if (obj instanceof Integer i) { System.out.println("It's an integer: " + (i * 2)); } else if (obj instanceof String s) { System.out.println("It's a string: " + s.length() + " characters"); } else if (obj instanceof List<?> list) { System.out.println("It's a list with " + list.size() + " elements"); } else { System.out.println("Unknown type: " + obj.getClass().getSimpleName()); } } public static void demonstrateWithCollections() { System.out.println("\n=== Pattern Matching with Collections ==="); List<Object> mixedList = List.of( "String value", 42, 3.14, List.of("a", "b", "c"), new StringBuilder("builder") ); for (Object item : mixedList) { // Pattern matching in for-each loop if (item instanceof String s) { System.out.println("String: " + s.toUpperCase()); } else if (item instanceof Integer i) { System.out.println("Integer: " + (i + 10)); } else if (item instanceof Double d) { System.out.println("Double: " + (d * 2)); } else if (item instanceof List<?> sublist) { System.out.println("List: " + sublist); } else { System.out.println("Other: " + item); } } } public static void demonstrateNullSafety() { System.out.println("\n=== Null Safety with Pattern Matching ==="); Object possiblyNull = Math.random() > 0.5 ? "I'm not null" : null; // Pattern matching safely handles null if (possiblyNull instanceof String s) { System.out.println("Not null string: " + s); } else { System.out.println("Object is null or not a String"); } // Traditional approach would need explicit null check if (possiblyNull != null && possiblyNull instanceof String) { String s = (String) possiblyNull; System.out.println("Traditional: " + s); } } }

2. Advanced Pattern Matching Features

Pattern Variables and Scope

public class AdvancedPatternMatching { public static void main(String[] args) { demonstratePatternVariableScope(); demonstrateComplexConditions(); demonstrateNestedPatterns(); } public static void demonstratePatternVariableScope() { System.out.println("=== Pattern Variable Scope ==="); Object obj = "Scope Demonstration"; // Pattern variable 's' is only in scope within the if block if (obj instanceof String s) { System.out.println("Inside if block: " + s.toUpperCase()); // 's' is available here } // 's' is not available here - compilation error // System.out.println(s); // Pattern variable with logical operators if (obj instanceof String s && s.length() > 5) { System.out.println("Long string: " + s); } // This works because s is in scope for the entire && expression if (obj instanceof String s && !s.isEmpty()) { System.out.println("Non-empty string: " + s); } // This doesn't work - s is not in scope for the || expression // if (obj instanceof String s || s.length() > 0) { } // Compilation error } public static void demonstrateComplexConditions() { System.out.println("\n=== Complex Conditions ==="); Object[] objects = { "Hello", 100, "", " ", null, "Another string" }; for (Object obj : objects) { // Complex condition with pattern matching if (obj instanceof String s && s != null && !s.trim().isEmpty()) { System.out.println("Valid non-empty string: '" + s + "'"); } else if (obj instanceof String s && (s == null || s.trim().isEmpty())) { System.out.println("Invalid or empty string: " + (s == null ? "null" : "'" + s + "'")); } else if (obj instanceof Integer i) { System.out.println("Integer: " + i); } else { System.out.println("Other type: " + (obj == null ? "null" : obj.getClass().getSimpleName())); } } } public static void demonstrateNestedPatterns() { System.out.println("\n=== Nested Patterns ==="); // Simulating complex object structures Object complexObj = Map.of( "user", Map.of("name", "John", "age", 30), "settings", List.of("dark-mode", "notifications") ); // Nested pattern matching if (complexObj instanceof Map<?, ?> map && map.get("user") instanceof Map<?, ?> userMap && userMap.get("name") instanceof String userName) { System.out.println("User name: " + userName); } // Processing nested collections Object nestedData = List.of( List.of(1, 2, 3), "String item", Map.of("key", "value") ); if (nestedData instanceof List<?> list) { for (Object item : list) { if (item instanceof List<?> innerList) { System.out.println("Nested list: " + innerList); } else if (item instanceof String s) { System.out.println("String item: " + s); } else if (item instanceof Map<?, ?> map) { System.out.println("Map item: " + map); } } } } // Method with return values using pattern matching public static String processWithReturn(Object obj) { // Using pattern matching in return scenarios if (obj instanceof String s) { return "String: " + s.toUpperCase(); } else if (obj instanceof Integer i) { return "Integer squared: " + (i * i); } else if (obj instanceof List<?> list && !list.isEmpty()) { return "Non-empty list with " + list.size() + " elements"; } else { return "Unknown type: " + (obj == null ? "null" : obj.getClass().getSimpleName()); } } }

3. Real-World Use Cases

Data Processing and Validation

import java.util.*; import java.time.*; public class RealWorldPatternMatching { public static void main(String[] args) { demonstrateDataValidation(); demonstrateAPIParsing(); demonstrateEventProcessing(); } public static void demonstrateDataValidation() { System.out.println("=== Data Validation with Pattern Matching ==="); List<Object> userInputs = Arrays.asList( "[email protected]", "invalid-email", 25, -5, LocalDate.of(1990, 1, 1), LocalDate.of(2025, 1, 1) // Future date ); for (Object input : userInputs) { ValidationResult result = validateUserInput(input); System.out.println("Input: " + input + " -> " + result); } } public static ValidationResult validateUserInput(Object input) { // Email validation if (input instanceof String s && isValidEmail(s)) { return ValidationResult.valid("Email"); } // Age validation else if (input instanceof Integer age) { if (age >= 0 && age <= 150) { return ValidationResult.valid("Age: " + age); } else { return ValidationResult.invalid("Age must be between 0 and 150"); } } // Date validation else if (input instanceof LocalDate date) { if (!date.isAfter(LocalDate.now())) { return ValidationResult.valid("Date: " + date); } else { return ValidationResult.invalid("Date cannot be in the future"); } } // Invalid type else { return ValidationResult.invalid("Unsupported input type"); } } private static boolean isValidEmail(String email) { return email != null && email.contains("@") && email.contains("."); } static class ValidationResult { private final boolean valid; private final String message; private ValidationResult(boolean valid, String message) { this.valid = valid; this.message = message; } public static ValidationResult valid(String message) { return new ValidationResult(true, message); } public static ValidationResult invalid(String message) { return new ValidationResult(false, message); } @Override public String toString() { return (valid ? "VALID" : "INVALID") + ": " + message; } } public static void demonstrateAPIParsing() { System.out.println("\n=== API Response Parsing ==="); // Simulating different API responses Object[] apiResponses = { Map.of("status", "success", "data", Map.of("user", "John")), Map.of("status", "error", "message", "Invalid request"), "Unexpected response type", 404, null }; for (Object response : apiResponses) { processAPIResponse(response); } } public static void processAPIResponse(Object response) { System.out.println("\nProcessing API response: " + response); if (response instanceof Map<?, ?> map) { // Success response if (map.get("status") instanceof String status && "success".equals(status)) { if (map.get("data") instanceof Map<?, ?> data) { System.out.println("Success! Data: " + data); } else { System.out.println("Success but no data"); } } // Error response else if (map.get("status") instanceof String status && "error".equals(status)) { if (map.get("message") instanceof String message) { System.out.println("Error: " + message); } else { System.out.println("Error but no message"); } } // Unknown status else { System.out.println("Unknown response status"); } } // HTTP status code else if (response instanceof Integer code) { if (code >= 200 && code < 300) { System.out.println("HTTP Success: " + code); } else if (code >= 400 && code < 500) { System.out.println("HTTP Client Error: " + code); } else if (code >= 500) { System.out.println("HTTP Server Error: " + code); } else { System.out.println("HTTP Other: " + code); } } // Unexpected response type else if (response instanceof String s) { System.out.println("Unexpected string response: " + s); } // Null response else if (response == null) { System.out.println("Null response received"); } // Other types else { System.out.println("Unknown response type: " + response.getClass().getSimpleName()); } } public static void demonstrateEventProcessing() { System.out.println("\n=== Event Processing ==="); List<Object> events = Arrays.asList( new UserLoginEvent("john_doe", LocalDateTime.now()), new PaymentEvent(100.50, "USD", "order_123"), new SystemAlertEvent("HIGH", "CPU usage at 95%"), "Invalid event string", 12345 ); for (Object event : events) { processEvent(event); } } public static void processEvent(Object event) { if (event instanceof UserLoginEvent login && login.getUsername() != null) { System.out.println("User login: " + login.getUsername() + " at " + login.getTimestamp()); } else if (event instanceof PaymentEvent payment) { System.out.printf("Payment: %s %.2f for %s%n", payment.getCurrency(), payment.getAmount(), payment.getOrderId()); } else if (event instanceof SystemAlertEvent alert && "HIGH".equals(alert.getSeverity())) { System.out.println("HIGH ALERT: " + alert.getMessage()); } else if (event instanceof String s) { System.out.println("Unexpected string event: " + s); } else { System.out.println("Unknown event type: " + (event == null ? "null" : event.getClass().getSimpleName())); } } } // Event classes for demonstration class UserLoginEvent { private final String username; private final LocalDateTime timestamp; public UserLoginEvent(String username, LocalDateTime timestamp) { this.username = username; this.timestamp = timestamp; } public String getUsername() { return username; } public LocalDateTime getTimestamp() { return timestamp; } } class PaymentEvent { private final double amount; private final String currency; private final String orderId; public PaymentEvent(double amount, String currency, String orderId) { this.amount = amount; this.currency = currency; this.orderId = orderId; } public double getAmount() { return amount; } public String getCurrency() { return currency; } public String getOrderId() { return orderId; } } class SystemAlertEvent { private final String severity; private final String message; public SystemAlertEvent(String severity, String message) { this.severity = severity; this.message = message; } public String getSeverity() { return severity; } public String getMessage() { return message; } }

4. Pattern Matching with Records and Sealed Classes

Records and Pattern Matching

public class RecordPatternMatching { public static void main(String[] args) { demonstrateRecordPatterns(); demonstrateSealedHierarchy(); } // Record definitions record Point(int x, int y) {} record Circle(Point center, double radius) {} record Rectangle(Point topLeft, Point bottomRight) {} public static void demonstrateRecordPatterns() { System.out.println("=== Pattern Matching with Records ==="); Object[] shapes = { new Point(10, 20), new Circle(new Point(5, 5), 10.0), new Rectangle(new Point(0, 0), new Point(10, 10)), "Not a shape" }; for (Object shape : shapes) { double area = calculateArea(shape); System.out.println("Shape: " + shape + " -> Area: " + area); } } public static double calculateArea(Object shape) { // Pattern matching with records (Java 19+ preview) if (shape instanceof Point p) { return 0; // Point has no area } else if (shape instanceof Circle c) { return Math.PI * c.radius() * c.radius(); } else if (shape instanceof Rectangle r) { int width = Math.abs(r.bottomRight().x() - r.topLeft().x()); int height = Math.abs(r.bottomRight().y() - r.topLeft().y()); return width * height; } else { return -1; // Unknown shape } } // Enhanced pattern matching with record decomposition (Java 21+ preview) public static void processShape(Object shape) { // Record patterns - decomposing records in patterns if (shape instanceof Point(int x, int y)) { System.out.println("Point at (" + x + ", " + y + ")"); } else if (shape instanceof Circle(Point center, double radius)) { System.out.println("Circle with center " + center + " and radius " + radius); } else if (shape instanceof Rectangle(Point tl, Point br)) { System.out.println("Rectangle from " + tl + " to " + br); } } } // Sealed classes for exhaustive pattern matching sealed interface Shape permits CircleShape, RectangleShape, TriangleShape { double area(); } record CircleShape(double radius) implements Shape { @Override public double area() { return Math.PI * radius * radius; } } record RectangleShape(double width, double height) implements Shape { @Override public double area() { return width * height; } } record TriangleShape(double base, double height) implements Shape { @Override public double area() { return 0.5 * base * height; } } class SealedPatternMatching { public static void demonstrateSealedHierarchy() { System.out.println("\n=== Sealed Classes and Exhaustive Pattern Matching ==="); Shape[] shapes = { new CircleShape(5.0), new RectangleShape(4.0, 6.0), new TriangleShape(3.0, 4.0) }; for (Shape shape : shapes) { describeShape(shape); } } public static void describeShape(Shape shape) { // Exhaustive pattern matching with sealed classes // The compiler knows all possible implementations String description = switch (shape) { case CircleShape c -> "Circle with radius " + c.radius() + " and area " + c.area(); case RectangleShape r -> "Rectangle " + r.width() + "x" + r.height() + " with area " + r.area(); case TriangleShape t -> "Triangle base " + t.base() + " height " + t.height() + " with area " + t.area(); // No default needed - all permitted types are covered }; System.out.println(description); } // Pattern matching in instanceof with sealed classes public static void processShape(Shape shape) { if (shape instanceof CircleShape c) { System.out.println("Processing circle: " + c.radius()); } else if (shape instanceof RectangleShape r) { System.out.println("Processing rectangle: " + r.width() + "x" + r.height()); } else if (shape instanceof TriangleShape t) { System.out.println("Processing triangle: " + t.base() + "x" + t.height()); } } }

5. Switch Expressions with Pattern Matching

Pattern Matching in Switch (Java 21+)

public class SwitchPatternMatching { public static void main(String[] args) { demonstrateSwitchPatterns(); demonstrateGuardedPatterns(); demonstrateNullHandling(); } public static void demonstrateSwitchPatterns() { System.out.println("=== Pattern Matching in Switch ==="); Object[] objects = { "Hello World", 42, 3.14, List.of(1, 2, 3), new int[]{1, 2, 3}, null, 'A' }; for (Object obj : objects) { String result = describeObject(obj); System.out.println(obj + " -> " + result); } } public static String describeObject(Object obj) { return switch (obj) { case String s -> "String: \"" + s + "\" with length " + s.length(); case Integer i -> "Integer: " + i + " (squared: " + (i * i) + ")"; case Double d -> "Double: " + d + " (formatted: " + String.format("%.2f", d) + ")"; case List<?> list -> "List with " + list.size() + " elements: " + list; case int[] array -> "int array with " + array.length + " elements"; case null -> "Null object"; default -> "Unknown type: " + obj.getClass().getSimpleName(); }; } public static void demonstrateGuardedPatterns() { System.out.println("\n=== Guarded Patterns ==="); Object[] numbers = {10, -5, 0, 100, -1, 50}; for (Object number : numbers) { String category = categorizeNumber(number); System.out.println(number + " -> " + category); } } public static String categorizeNumber(Object number) { return switch (number) { case Integer i when i > 0 -> "Positive integer: " + i; case Integer i when i < 0 -> "Negative integer: " + i; case Integer i -> "Zero"; case Double d when d > 0 -> "Positive double: " + d; case Double d when d < 0 -> "Negative double: " + d; case Double d -> "Zero double"; case null -> "Null number"; default -> "Not a number: " + number.getClass().getSimpleName(); }; } public static void demonstrateNullHandling() { System.out.println("\n=== Null Handling in Switch ==="); Object[] possiblyNull = {"Hello", null, "World", null, "!"}; for (Object obj : possiblyNull) { String result = handlePossiblyNull(obj); System.out.println(obj + " -> " + result); } } public static String handlePossiblyNull(Object obj) { return switch (obj) { case null -> "Handling null case explicitly"; case String s when s.length() > 5 -> "Long string: " + s; case String s -> "Short string: " + s; default -> "Other: " + obj; }; } // Complex example with multiple patterns public static String processComplexObject(Object obj) { return switch (obj) { case String s when s.isEmpty() -> "Empty string"; case String s when s.length() == 1 -> "Single character: " + s; case String s -> "String: " + s; case Integer i when i == 0 -> "Zero"; case Integer i when i > 0 && i < 100 -> "Small positive integer"; case Integer i when i >= 100 -> "Large positive integer"; case Integer i -> "Negative integer"; case List<?> list when list.isEmpty() -> "Empty list"; case List<?> list when list.size() == 1 -> "Single element list"; case List<?> list -> "List with " + list.size() + " elements"; case Map<?, ?> map when map.isEmpty() -> "Empty map"; case Map<?, ?> map -> "Map with " + map.size() + " entries"; case null -> "Null object"; default -> "Unhandled type: " + obj.getClass().getSimpleName(); }; } }

6. Performance Considerations

Benchmarking Pattern Matching

import java.util.*; public class PatternMatchingPerformance { public static void main(String[] args) { demonstratePerformanceComparison(); } public static void demonstratePerformanceComparison() { System.out.println("=== Pattern Matching Performance ==="); List<Object> testData = generateTestData(10000); // Warm up for (int i = 0; i < 1000; i++) { traditionalApproach(testData.get(i % testData.size())); patternMatchingApproach(testData.get(i % testData.size())); } // Benchmark traditional approach long traditionalStart = System.nanoTime(); for (Object obj : testData) { traditionalApproach(obj); } long traditionalTime = System.nanoTime() - traditionalStart; // Benchmark pattern matching approach long patternStart = System.nanoTime(); for (Object obj : testData) { patternMatchingApproach(obj); } long patternTime = System.nanoTime() - patternStart; System.out.printf("Traditional approach: %,d ns%n", traditionalTime); System.out.printf("Pattern matching: %,d ns%n", patternTime); System.out.printf("Difference: %.2f%%%n", ((double) patternTime / traditionalTime - 1) * 100); } public static String traditionalApproach(Object obj) { if (obj instanceof String) { String s = (String) obj; return s.toUpperCase(); } else if (obj instanceof Integer) { Integer i = (Integer) obj; return String.valueOf(i * 2); } else if (obj instanceof List) { List<?> list = (List<?>) obj; return "List size: " + list.size(); } else { return "Unknown"; } } public static String patternMatchingApproach(Object obj) { if (obj instanceof String s) { return s.toUpperCase(); } else if (obj instanceof Integer i) { return String.valueOf(i * 2); } else if (obj instanceof List<?> list) { return "List size: " + list.size(); } else { return "Unknown"; } } private static List<Object> generateTestData(int size) { List<Object> data = new ArrayList<>(); Random random = new Random(); for (int i = 0; i < size; i++) { int type = random.nextInt(4); switch (type) { case 0 -> data.add("String_" + i); case 1 -> data.add(i); case 2 -> data.add(List.of(i, i+1, i+2)); case 3 -> data.add((double) i); } } return data; } // Performance tips public static void showPerformanceTips() { System.out.println("\n=== Performance Tips ==="); System.out.println(""" 1. Pattern matching has similar performance to traditional approach 2. The JVM can optimize both approaches effectively 3. Focus on readability rather than micro-optimizations 4. Complex patterns with guards may have slight overhead 5. Use the most specific pattern first for better performance """); } }

7. Best Practices and Common Pitfalls

Pattern Matching Best Practices

import java.util.*; public class PatternMatchingBestPractices { public static void main(String[] args) { demonstrateBestPractices(); demonstrateCommonPitfalls(); demonstrateRefactoringExamples(); } public static void demonstrateBestPractices() { System.out.println("=== Best Practices ==="); Object obj = "Best Practices Example"; // ✅ GOOD: Simple and readable if (obj instanceof String s) { System.out.println("String: " + s); } // ✅ GOOD: Combine with other conditions if (obj instanceof String s && s.length() > 10) { System.out.println("Long string: " + s); } // ✅ GOOD: Use in switch expressions for exhaustive handling String result = switch (obj) { case String s -> "String: " + s; case Integer i -> "Integer: " + i; case null -> "Null"; default -> "Other"; }; System.out.println("Switch result: " + result); } public static void demonstrateCommonPitfalls() { System.out.println("\n=== Common Pitfalls ==="); Object obj = "Test"; // ❌ BAD: Unnecessary pattern variable if (obj instanceof String s) { System.out.println(s.toUpperCase()); // s is used } // ✅ GOOD: Use traditional instanceof if you don't need the variable if (obj instanceof String) { System.out.println("It's a string"); } // ❌ BAD: Pattern variable scope misunderstanding // if (obj instanceof String s || s.length() > 0) { } // Compilation error // ✅ GOOD: Understand pattern variable scope if (obj instanceof String s && s.length() > 0) { System.out.println("Valid string: " + s); } // ❌ BAD: Overly complex patterns Object complex = Map.of("data", List.of(1, 2, 3)); if (complex instanceof Map<?, ?> map && map.get("data") instanceof List<?> list && list.size() > 0 && list.get(0) instanceof Integer first) { System.out.println("First element: " + first); } // ✅ GOOD: Consider extracting complex logic processComplexObject(complex); } private static void processComplexObject(Object obj) { if (obj instanceof Map<?, ?> map) { Object data = map.get("data"); if (data instanceof List<?> list && !list.isEmpty()) { Object first = list.get(0); if (first instanceof Integer i) { System.out.println("First element: " + i); } } } } public static void demonstrateRefactoringExamples() { System.out.println("\n=== Refactoring Examples ==="); // Before refactoring - traditional approach Object traditionalObj = "Hello"; String traditionalResult; if (traditionalObj instanceof String) { String s = (String) traditionalObj; traditionalResult = s.toUpperCase(); } else if (traditionalObj instanceof Integer) { Integer i = (Integer) traditionalObj; traditionalResult = String.valueOf(i * 2); } else { traditionalResult = "Unknown"; } System.out.println("Traditional: " + traditionalResult); // After refactoring - pattern matching Object patternObj = "Hello"; String patternResult = switch (patternObj) { case String s -> s.toUpperCase(); case Integer i -> String.valueOf(i * 2); default -> "Unknown"; }; System.out.println("Pattern matching: " + patternResult); // Complex refactoring example processUserInputRefactored("[email protected]"); processUserInputRefactored(25); processUserInputRefactored(null); } // Before refactoring public static void processUserInputTraditional(Object input) { if (input instanceof String) { String s = (String) input; if (s.contains("@")) { System.out.println("Email: " + s); } else { System.out.println("Username: " + s); } } else if (input instanceof Integer) { Integer i = (Integer) input; if (i >= 0) { System.out.println("Age: " + i); } else { System.out.println("Invalid age"); } } else if (input == null) { System.out.println("Null input"); } } // After refactoring with pattern matching public static void processUserInputRefactored(Object input) { switch (input) { case String s when s.contains("@") -> System.out.println("Email: " + s); case String s -> System.out.println("Username: " + s); case Integer i when i >= 0 -> System.out.println("Age: " + i); case Integer i -> System.out.println("Invalid age: " + i); case null -> System.out.println("Null input"); default -> System.out.println("Unknown input type"); } } public static void showCodeQualityTips() { System.out.println("\n=== Code Quality Tips ==="); System.out.println(""" ✅ DO: - Use pattern matching to eliminate boilerplate casting - Combine with switch expressions for exhaustive handling - Use guards for additional conditions - Consider sealed classes for exhaustive pattern matching - Use meaningful variable names in patterns ❌ DON'T: - Use pattern variables you don't need - Create overly complex nested patterns - Forget about pattern variable scope rules - Use pattern matching where simple instanceof suffices - Ignore null handling in patterns """); } }

Conclusion

Key Benefits of Pattern Matching for instanceof:

  1. Reduced Boilerplate: Eliminates explicit casting
  2. Improved Readability: More concise and expressive code
  3. Enhanced Safety: Compiler helps prevent type-related bugs
  4. Better Maintainability: Clearer intent and structure

When to Use Pattern Matching:

ScenarioRecommendation
Type checking with casting✅ Perfect use case
Complex conditional logic✅ Great for readability
Simple type checking only❌ Use traditional instanceof
Performance-critical code⚠️ Benchmark both approaches

Java Version Support:

  • Java 14-15: Preview feature (enable with --enable-preview)
  • Java 16+: Standard feature
  • Java 21+: Enhanced with switch pattern matching

Pattern matching for instanceof is a significant improvement to Java's type system, making code more concise, readable, and less error-prone. It's particularly powerful when combined with records, sealed classes, and switch expressions.

Leave a Reply

Your email address will not be published. Required fields are marked *


Macro Nepal Helper