google-site-verification: google61fe8ba583a51912.html
Method References in Java

Table of Contents

  1. Introduction to Method References
  2. Types of Method References
  3. Static Method References
  4. Instance Method References
  5. Arbitrary Object Method References
  6. Constructor References
  7. Method References vs Lambda Expressions
  8. Best Practices
  9. Common Use Cases
  10. Complete Examples

Introduction to Method References

Method references are a shorthand notation of lambda expressions to call methods. They provide a way to refer to methods without executing them, making code more readable and concise.

Syntax:

ClassName::methodName

Method References vs Lambda Expressions:

// Lambda expression Function<String, Integer> lambda = s -> Integer.parseInt(s); // Method reference Function<String, Integer> methodRef = Integer::parseInt;

Types of Method References

There are four types of method references in Java:

  1. Reference to static method - ClassName::staticMethodName
  2. Reference to instance method of particular object - object::instanceMethodName
  3. Reference to instance method of arbitrary object - ClassName::instanceMethodName
  4. Reference to constructor - ClassName::new

Static Method References

Basic Static Method References:

import java.util.*; import java.util.function.*; public class StaticMethodReferences { // Static methods for demonstration public static boolean isEven(int number) { return number % 2 == 0; } public static String toUpperCase(String str) { return str.toUpperCase(); } public static int square(int number) { return number * number; } public static void print(String message) { System.out.println(message); } public static void main(String[] args) { // 1. Using static method references with different functional interfaces // Function<T, R> - takes T, returns R Function<String, Integer> parseInt = Integer::parseInt; System.out.println("Parsed integer: " + parseInt.apply("123")); // Predicate<T> - takes T, returns boolean Predicate<Integer> isEvenPredicate = StaticMethodReferences::isEven; System.out.println("Is 10 even? " + isEvenPredicate.test(10)); // Function<T, R> with custom method Function<String, String> toUpper = StaticMethodReferences::toUpperCase; System.out.println("Uppercase: " + toUpper.apply("hello")); // UnaryOperator<T> - takes T, returns T (subtype of Function) UnaryOperator<Integer> squareOperator = StaticMethodReferences::square; System.out.println("Square of 5: " + squareOperator.apply(5)); // Consumer<T> - takes T, returns void Consumer<String> printer = StaticMethodReferences::print; printer.accept("Hello from method reference!"); // 2. Using with streams List<String> numbers = Arrays.asList("1", "2", "3", "4", "5"); // Convert strings to integers using method reference List<Integer> integers = numbers.stream() .map(Integer::parseInt) // Static method reference .toList(); System.out.println("Parsed integers: " + integers); // Filter even numbers List<Integer> evenNumbers = integers.stream() .filter(StaticMethodReferences::isEven) .toList(); System.out.println("Even numbers: " + evenNumbers); // Square each number List<Integer> squares = integers.stream() .map(StaticMethodReferences::square) .toList(); System.out.println("Squares: " + squares); } }

Output:

Parsed integer: 123 Is 10 even? true Uppercase: HELLO Square of 5: 25 Hello from method reference! Parsed integers: [1, 2, 3, 4, 5] Even numbers: [2, 4] Squares: [1, 4, 9, 16, 25]

Common Built-in Static Method References:

import java.util.*; import java.util.function.*; public class BuiltInStaticReferences { public static void main(String[] args) { List<String> strings = Arrays.asList("1", "2", "3", "4", "5"); // Common static method references // Integer.parseInt(String) Function<String, Integer> parseInt = Integer::parseInt; // String.valueOf(Object) Function<Object, String> valueOf = String::valueOf; // Math.max(int, int) IntBinaryOperator max = Math::max; // Math.pow(double, double) DoubleBinaryOperator pow = Math::pow; // System.out.println(String) Consumer<String> printer = System.out::println; // Examples List<Integer> numbers = strings.stream() .map(Integer::parseInt) .toList(); System.out.println("Numbers: " + numbers); int maximum = numbers.stream() .reduce(0, Math::max); System.out.println("Max: " + maximum); // Using method references in different contexts BiFunction<Integer, Integer, Integer> maxFunction = Math::max; System.out.println("Max of 10 and 20: " + maxFunction.apply(10, 20)); // Method reference for sorting List<String> names = Arrays.asList("John", "Alice", "Bob", "Diana"); names.sort(String::compareToIgnoreCase); System.out.println("Sorted names: " + names); } }

Output:

Numbers: [1, 2, 3, 4, 5] Max: 5 Max of 10 and 20: 20 Sorted names: [Alice, Bob, Diana, John]

Instance Method References

Reference to Instance Method of Particular Object:

import java.util.*; import java.util.function.*; class StringProcessor { private String prefix; public StringProcessor(String prefix) { this.prefix = prefix; } public String process(String input) { return prefix + input.toUpperCase(); } public boolean containsWord(String sentence, String word) { return sentence.contains(word); } public void printWithTimestamp(String message) { System.out.println(new Date() + ": " + message); } } class Calculator { public int add(int a, int b) { return a + b; } public int multiply(int a, int b) { return a * b; } } public class InstanceMethodReferences { public static void main(String[] args) { // 1. Reference to instance method of specific object StringProcessor processor = new StringProcessor("PREFIX: "); // Using method reference for instance method Function<String, String> processorFunction = processor::process; System.out.println(processorFunction.apply("hello")); // Using method reference that takes multiple parameters BiFunction<String, String, Boolean> containsFunction = processor::containsWord; System.out.println("Contains 'Java': " + containsFunction.apply("I love Java programming", "Java")); // Consumer with instance method reference Consumer<String> printer = processor::printWithTimestamp; printer.accept("Hello World!"); // 2. Using with Calculator Calculator calculator = new Calculator(); IntBinaryOperator adder = calculator::add; IntBinaryOperator multiplier = calculator::multiply; System.out.println("10 + 20 = " + adder.applyAsInt(10, 20)); System.out.println("10 * 20 = " + multiplier.applyAsInt(10, 20)); // 3. Using with streams and specific objects List<String> words = Arrays.asList("apple", "banana", "cherry", "date"); // Using instance method reference in map List<String> processedWords = words.stream() .map(processor::process) .toList(); System.out.println("Processed words: " + processedWords); // 4. Using System.out instance List<String> messages = Arrays.asList("Error", "Warning", "Info"); messages.forEach(System.out::println); } }

Output:

PREFIX: HELLO Contains 'Java': true Wed Jan 10 14:30:00 IST 2024: Hello World! 10 + 20 = 30 10 * 20 = 200 Processed words: [PREFIX: APPLE, PREFIX: BANANA, PREFIX: CHERRY, PREFIX: DATE] Error Warning Info

Arbitrary Object Method References

Reference to Instance Method of Arbitrary Object:

import java.util.*; import java.util.function.*; public class ArbitraryObjectMethodReferences { public static void main(String[] args) { List<String> names = Arrays.asList("John", "Alice", "Bob", "Diana", "Charlie"); // 1. Reference to instance method of arbitrary object // The first parameter becomes the target object // Equivalent to: (str1, str2) -> str1.compareToIgnoreCase(str2) Comparator<String> ignoreCaseComparator = String::compareToIgnoreCase; names.sort(ignoreCaseComparator); System.out.println("Sorted ignoring case: " + names); // 2. Using with map - String::toUpperCase // Equivalent to: str -> str.toUpperCase() List<String> upperCaseNames = names.stream() .map(String::toUpperCase) .toList(); System.out.println("Uppercase names: " + upperCaseNames); // 3. Using with filter - String::isEmpty // Equivalent to: str -> str.isEmpty() List<String> strings = Arrays.asList("", "Hello", "", "World", ""); List<String> nonEmpty = strings.stream() .filter(String::isEmpty) .toList(); System.out.println("Empty strings: " + nonEmpty); // 4. Using String::length // Equivalent to: str -> str.length() List<Integer> nameLengths = names.stream() .map(String::length) .toList(); System.out.println("Name lengths: " + nameLengths); // 5. More complex examples with custom objects List<Person> people = Arrays.asList( new Person("John", 25), new Person("Alice", 30), new Person("Bob", 22), new Person("Diana", 28) ); // Using Person::getName List<String> personNames = people.stream() .map(Person::getName) .toList(); System.out.println("Person names: " + personNames); // Using Person::getAge List<Integer> ages = people.stream() .map(Person::getAge) .toList(); System.out.println("Ages: " + ages); // Sorting by age using Person::getAge people.sort(Comparator.comparing(Person::getAge)); System.out.println("Sorted by age: " + people); // 6. Method references with multiple parameters List<String> words = Arrays.asList("Apple", "Banana", "Cherry"); // Using method reference in reduce Optional<String> concatenated = words.stream() .reduce(String::concat); System.out.println("Concatenated: " + concatenated.orElse("")); } } class Person { private String name; private int age; public Person(String name, int age) { this.name = name; this.age = age; } public String getName() { return name; } public int getAge() { return age; } @Override public String toString() { return name + "(" + age + ")"; } }

Output:

Sorted ignoring case: [Alice, Bob, Charlie, Diana, John] Uppercase names: [ALICE, BOB, CHARLIE, DIANA, JOHN] Empty strings: [, , ] Name lengths: [5, 3, 7, 5, 4] Person names: [John, Alice, Bob, Diana] Ages: [25, 30, 22, 28] Sorted by age: [Bob(22), John(25), Diana(28), Alice(30)] Concatenated: AppleBananaCherry

Constructor References

Reference to Constructors:

import java.util.*; import java.util.function.*; class Product { private String name; private double price; public Product(String name) { this.name = name; this.price = 0.0; } public Product(String name, double price) { this.name = name; this.price = price; } public Product() { this.name = "Unknown"; this.price = 0.0; } @Override public String toString() { return String.format("Product{name='%s', price=%.2f}", name, price); } } class Person { private String name; public Person(String name) { this.name = name; } @Override public String toString() { return "Person{name='" + name + "'}"; } } public class ConstructorReferences { public static void main(String[] args) { // 1. Reference to no-argument constructor Supplier<Product> productSupplier = Product::new; Product defaultProduct = productSupplier.get(); System.out.println("Default product: " + defaultProduct); // 2. Reference to constructor with parameters Function<String, Product> productCreator = Product::new; Product laptop = productCreator.apply("Laptop"); System.out.println("Created product: " + laptop); // 3. Reference to constructor with multiple parameters BiFunction<String, Double, Product> productCreatorWithPrice = Product::new; Product expensiveLaptop = productCreatorWithPrice.apply("Gaming Laptop", 1999.99); System.out.println("Expensive product: " + expensiveLaptop); // 4. Using constructor references with streams List<String> productNames = Arrays.asList("Mouse", "Keyboard", "Monitor", "CPU"); // Create products from names List<Product> products = productNames.stream() .map(Product::new) .toList(); System.out.println("Products from names: " + products); // 5. Using with different types List<String> personNames = Arrays.asList("Alice", "Bob", "Charlie"); List<Person> people = personNames.stream() .map(Person::new) .toList(); System.out.println("People: " + people); // 6. Array constructor references IntFunction<int[]> intArrayCreator = int[]::new; int[] intArray = intArrayCreator.apply(5); System.out.println("Int array length: " + intArray.length); Function<Integer, String[]> stringArrayCreator = String[]::new; String[] stringArray = stringArrayCreator.apply(3); System.out.println("String array length: " + stringArray.length); // 7. Complex example with custom functional interface interface TriFunction<A, B, C, R> { R apply(A a, B b, C c); } class TripleProduct { private String name; private double price; private String category; public TripleProduct(String name, double price, String category) { this.name = name; this.price = price; this.category = category; } @Override public String toString() { return String.format("TripleProduct{name='%s', price=%.2f, category='%s'}", name, price, category); } } TriFunction<String, Double, String, TripleProduct> tripleCreator = TripleProduct::new; TripleProduct triple = tripleCreator.apply("Tablet", 499.99, "Electronics"); System.out.println("Triple product: " + triple); } }

Output:

Default product: Product{name='Unknown', price=0.00} Created product: Product{name='Laptop', price=0.00} Expensive product: Product{name='Gaming Laptop', price=1999.99} Products from names: [Product{name='Mouse', price=0.00}, Product{name='Keyboard', price=0.00}, Product{name='Monitor', price=0.00}, Product{name='CPU', price=0.00}] People: [Person{name='Alice'}, Person{name='Bob'}, Person{name='Charlie'}] Int array length: 5 String array length: 3 Triple product: TripleProduct{name='Tablet', price=499.99, category='Electronics'}

Method References vs Lambda Expressions

Comparison and When to Use:

import java.util.*; import java.util.function.*; public class MethodRefVsLambda { public static void main(String[] args) { List<String> names = Arrays.asList("John", "Alice", "Bob", "Diana"); System.out.println("Original list: " + names); // Scenario 1: Static method reference vs lambda // Using lambda List<String> upperCaseLambda = names.stream() .map(s -> s.toUpperCase()) .toList(); // Using method reference (better) List<String> upperCaseMethodRef = names.stream() .map(String::toUpperCase) .toList(); System.out.println("Uppercase (lambda): " + upperCaseLambda); System.out.println("Uppercase (method ref): " + upperCaseMethodRef); // Scenario 2: Instance method reference vs lambda String prefix = "Hello "; // Using lambda List<String> greetingsLambda = names.stream() .map(name -> prefix + name) .toList(); // Using method reference with custom method Greeter greeter = new Greeter(prefix); List<String> greetingsMethodRef = names.stream() .map(greeter::greet) .toList(); System.out.println("Greetings (lambda): " + greetingsLambda); System.out.println("Greetings (method ref): " + greetingsMethodRef); // Scenario 3: When to prefer lambda over method reference List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5); // Complex transformation - lambda is better List<String> complexLambda = numbers.stream() .map(n -> { String result = "Number: " + n; if (n % 2 == 0) { result += " (even)"; } else { result += " (odd)"; } return result; }) .toList(); System.out.println("Complex transformation: " + complexLambda); // Simple transformation - method reference is better List<Integer> squaresMethodRef = numbers.stream() .map(MethodRefVsLambda::square) .toList(); System.out.println("Squares: " + squaresMethodRef); // Scenario 4: Constructor references // Using lambda Supplier<List<String>> listSupplierLambda = () -> new ArrayList<>(); // Using constructor reference (better) Supplier<List<String>> listSupplierMethodRef = ArrayList::new; List<String> newList = listSupplierMethodRef.get(); newList.add("New element"); System.out.println("New list: " + newList); } public static int square(int n) { return n * n; } } class Greeter { private String prefix; public Greeter(String prefix) { this.prefix = prefix; } public String greet(String name) { return prefix + name; } }

Output:

Original list: [John, Alice, Bob, Diana] Uppercase (lambda): [JOHN, ALICE, BOB, DIANA] Uppercase (method ref): [JOHN, ALICE, BOB, DIANA] Greetings (lambda): [Hello John, Hello Alice, Hello Bob, Hello Diana] Greetings (method ref): [Hello John, Hello Alice, Hello Bob, Hello Diana] Complex transformation: [Number: 1 (odd), Number: 2 (even), Number: 3 (odd), Number: 4 (even), Number: 5 (odd)] Squares: [1, 4, 9, 16, 25] New list: [New element]

Best Practices

1. Prefer Method References for Simple Cases:

// ✅ Good - method reference is cleaner list.stream().map(String::toUpperCase).toList(); // ❌ Less readable - lambda is unnecessary list.stream().map(s -> s.toUpperCase()).toList();

2. Use Lambda for Complex Logic:

// ✅ Good - lambda for complex logic list.stream().map(s -> { if (s == null) return "NULL"; return s.trim().toUpperCase(); }).toList(); // ❌ Bad - don't create complex method just to use method reference

3. Choose Descriptive Method Names:

// ✅ Good - descriptive method names list.stream().filter(ValidationUtils::isValidEmail).toList(); // ❌ Avoid - unclear method names list.stream().filter(Utils::check).toList();

4. Be Consistent:

// ✅ Good - consistent usage list.stream() .map(String::trim) .filter(String::isEmpty) .forEach(System.out::println); // ❌ Inconsistent - mixing styles unnecessarily list.stream() .map(s -> s.trim()) .filter(str -> str.isEmpty()) .forEach(x -> System.out.println(x));

Common Use Cases

1. Stream Processing:

import java.util.*; import java.util.stream.*; public class StreamProcessingExamples { public static void main(String[] args) { List<String> names = Arrays.asList("John", "Alice", "", "Bob", "", "Diana"); // Common stream operations with method references // 1. Filtering List<String> nonEmpty = names.stream() .filter(String::isEmpty) .toList(); System.out.println("Empty strings: " + nonEmpty); // 2. Mapping List<String> upperCase = names.stream() .filter(s -> !s.isEmpty()) .map(String::toUpperCase) .toList(); System.out.println("Uppercase names: " + upperCase); // 3. Sorting List<String> sorted = names.stream() .filter(s -> !s.isEmpty()) .sorted(String::compareToIgnoreCase) .toList(); System.out.println("Sorted names: " + sorted); // 4. Collection to map List<Person> people = Arrays.asList( new Person("John", 25), new Person("Alice", 30), new Person("Bob", 22) ); Map<String, Integer> nameToAge = people.stream() .collect(Collectors.toMap( Person::getName, Person::getAge )); System.out.println("Name to age map: " + nameToAge); // 5. Grouping Map<Integer, List<Person>> peopleByAge = people.stream() .collect(Collectors.groupingBy( Person::getAge )); System.out.println("People by age: " + peopleByAge); // 6. Method reference in reduce Optional<String> concatenated = names.stream() .filter(s -> !s.isEmpty()) .reduce(String::concat); System.out.println("Concatenated: " + concatenated.orElse("")); } }

Output:

Empty strings: [, ] Uppercase names: [JOHN, ALICE, BOB, DIANA] Sorted names: [Alice, Bob, Diana, John] Name to age map: {John=25, Alice=30, Bob=22} People by age: {22=[Bob(22)], 25=[John(25)], 30=[Alice(30)]} Concatenated: JohnAliceBobDiana

2. Event Handling and Callbacks:

import java.util.*; import java.util.function.*; public class EventHandlingExamples { public static void main(String[] args) { Button button = new Button(); TextField textField = new TextField(); // Method references for event handlers // Static method reference button.setOnClick(EventHandlingExamples::handleButtonClick); // Instance method reference TextProcessor processor = new TextProcessor(); textField.setOnInput(processor::processText); // System method reference textField.setOnError(System.err::println); // Simulate events button.simulateClick(); textField.simulateInput("Hello World"); textField.simulateError("Input too long"); // Timer example Timer timer = new Timer(); timer.scheduleRepeating(EventHandlingExamples::printTimestamp); // Simulate timer ticks timer.simulateTicks(3); } public static void handleButtonClick() { System.out.println("Button clicked!"); } public static void printTimestamp() { System.out.println("Timestamp: " + new Date()); } } // Supporting classes for event handling class Button { private Runnable onClickHandler; public void setOnClick(Runnable handler) { this.onClickHandler = handler; } public void simulateClick() { if (onClickHandler != null) { onClickHandler.run(); } } } class TextField { private Consumer<String> onInputHandler; private Consumer<String> onErrorHandler; public void setOnInput(Consumer<String> handler) { this.onInputHandler = handler; } public void setOnError(Consumer<String> handler) { this.onErrorHandler = handler; } public void simulateInput(String text) { if (onInputHandler != null) { onInputHandler.accept(text); } } public void simulateError(String error) { if (onErrorHandler != null) { onErrorHandler.accept(error); } } } class TextProcessor { public void processText(String text) { System.out.println("Processing text: " + text.toUpperCase()); } } class Timer { private Runnable onTickHandler; public void scheduleRepeating(Runnable handler) { this.onTickHandler = handler; } public void simulateTicks(int count) { for (int i = 0; i < count; i++) { if (onTickHandler != null) { onTickHandler.run(); } } } }

Output:

Button clicked! Processing text: HELLO WORLD Input too long Timestamp: Wed Jan 10 14:30:00 IST 2024 Timestamp: Wed Jan 10 14:30:01 IST 2024 Timestamp: Wed Jan 10 14:30:02 IST 2024

Complete Examples

Complete Working Example:

import java.util.*; import java.util.function.*; import java.util.stream.*; public class MethodReferencesCompleteExample { public static void main(String[] args) { System.out.println("=== Method References Complete Example ===\n"); // Example 1: Static Method References staticMethodReferences(); // Example 2: Instance Method References instanceMethodReferences(); // Example 3: Arbitrary Object Method References arbitraryObjectMethodReferences(); // Example 4: Constructor References constructorReferences(); // Example 5: Real-world Use Case - Data Processing dataProcessingExample(); } public static void staticMethodReferences() { System.out.println("1. Static Method References:"); List<String> numbersAsString = Arrays.asList("1", "2", "3", "4", "5"); // Using static method references List<Integer> numbers = numbersAsString.stream() .map(Integer::parseInt) // Integer.parseInt() .toList(); System.out.println("Parsed numbers: " + numbers); // Using custom static method List<Integer> squares = numbers.stream() .map(MethodReferencesCompleteExample::square) .toList(); System.out.println("Squares: " + squares); // Using Math static methods Optional<Integer> max = numbers.stream() .reduce(Math::max); // Math.max() System.out.println("Max: " + max.orElse(0)); System.out.println(); } public static void instanceMethodReferences() { System.out.println("2. Instance Method References:"); String prefix = "Hello, "; Greeter greeter = new Greeter(prefix); List<String> names = Arrays.asList("Alice", "Bob", "Charlie"); // Using instance method reference List<String> greetings = names.stream() .map(greeter::greet) .toList(); System.out.println("Greetings: " + greetings); // Using System.out instance names.forEach(System.out::println); System.out.println(); } public static void arbitraryObjectMethodReferences() { System.out.println("3. Arbitrary Object Method References:"); List<String> names = Arrays.asList("John", "Alice", "Bob", "Diana"); // Using arbitrary object method references List<String> upperCase = names.stream() .map(String::toUpperCase) // str -> str.toUpperCase() .toList(); System.out.println("Uppercase: " + upperCase); List<Integer> lengths = names.stream() .map(String::length) // str -> str.length() .toList(); System.out.println("Lengths: " + lengths); // Sorting with method reference names.sort(String::compareToIgnoreCase); System.out.println("Sorted: " + names); System.out.println(); } public static void constructorReferences() { System.out.println("4. Constructor References:"); List<String> productNames = Arrays.asList("Laptop", "Mouse", "Keyboard"); // Using constructor reference List<Product> products = productNames.stream() .map(Product::new) .toList(); System.out.println("Products: " + products); // Using no-arg constructor reference Supplier<List<String>> listSupplier = ArrayList::new; List<String> newList = listSupplier.get(); newList.add("New item"); System.out.println("New list: " + newList); System.out.println(); } public static void dataProcessingExample() { System.out.println("5. Real-world Use Case - Data Processing:"); List<Employee> employees = Arrays.asList( new Employee("Alice", "Engineering", 75000), new Employee("Bob", "Marketing", 65000), new Employee("Charlie", "Engineering", 80000), new Employee("Diana", "HR", 60000), new Employee("Eve", "Engineering", 90000) ); // Process data using method references System.out.println("All employees:"); employees.forEach(System.out::println); // Filter engineering department List<Employee> engineers = employees.stream() .filter(Employee::isEngineer) .toList(); System.out.println("\nEngineering team:"); engineers.forEach(System.out::println); // Get names only List<String> names = employees.stream() .map(Employee::getName) .toList(); System.out.println("\nEmployee names: " + names); // Group by department Map<String, List<Employee>> byDepartment = employees.stream() .collect(Collectors.groupingBy( Employee::getDepartment )); System.out.println("\nEmployees by department:"); byDepartment.forEach((dept, emps) -> { System.out.println(dept + ": " + emps); }); // Calculate average salary by department Map<String, Double> avgSalaryByDept = employees.stream() .collect(Collectors.groupingBy( Employee::getDepartment, Collectors.averagingDouble(Employee::getSalary) )); System.out.println("\nAverage salary by department:"); avgSalaryByDept.forEach((dept, avg) -> { System.out.printf("%s: $%.2f\n", dept, avg); }); } // Supporting static method public static int square(int n) { return n * n; } } // Supporting classes class Greeter { private String prefix; public Greeter(String prefix) { this.prefix = prefix; } public String greet(String name) { return prefix + name; } } class Product { private String name; public Product(String name) { this.name = name; } @Override public String toString() { return "Product{name='" + name + "'}"; } } class Employee { private String name; private String department; private double salary; public Employee(String name, String department, double salary) { this.name = name; this.department = department; this.salary = salary; } public String getName() { return name; } public String getDepartment() { return department; } public double getSalary() { return salary; } public boolean isEngineer() { return "Engineering".equals(department); } @Override public String toString() { return String.format("Employee{name='%-8s', dept='%-12s', salary=$%7.2f}", name, department, salary); } }

Expected Output:

=== Method References Complete Example === 1. Static Method References: Parsed numbers: [1, 2, 3, 4, 5] Squares: [1, 4, 9, 16, 25] Max: 5 2. Instance Method References: Greetings: [Hello, Alice, Hello, Bob, Hello, Charlie] Alice Bob Charlie 3. Arbitrary Object Method References: Uppercase: [JOHN, ALICE, BOB, DIANA] Lengths: [4, 5, 3, 5] Sorted: [Alice, Bob, Diana, John] 4. Constructor References: Products: [Product{name='Laptop'}, Product{name='Mouse'}, Product{name='Keyboard'}] New list: [New item] 5. Real-world Use Case - Data Processing: All employees: Employee{name='Alice ', dept='Engineering', salary=$75000.00} Employee{name='Bob ', dept='Marketing ', salary=$65000.00} Employee{name='Charlie ', dept='Engineering', salary=$80000.00} Employee{name='Diana ', dept='HR ', salary=$60000.00} Employee{name='Eve ', dept='Engineering', salary=$90000.00} Engineering team: Employee{name='Alice ', dept='Engineering', salary=$75000.00} Employee{name='Charlie ', dept='Engineering', salary=$80000.00} Employee{name='Eve ', dept='Engineering', salary=$90000.00} Employee names: [Alice, Bob, Charlie, Diana, Eve] Employees by department: HR: [Employee{name='Diana ', dept='HR ', salary=$60000.00}] Engineering: [Employee{name='Alice ', dept='Engineering', salary=$75000.00}, Employee{name='Charlie ', dept='Engineering', salary=$80000.00}, Employee{name='Eve ', dept='Engineering', salary=$90000.00}] Marketing: [Employee{name='Bob ', dept='Marketing ', salary=$65000.00}] Average salary by department: HR: $60000.00 Engineering: $81666.67 Marketing: $65000.00

Key Takeaways

  1. Static Method References - ClassName::staticMethodName
  2. Instance Method References - object::instanceMethodName
  3. Arbitrary Object Method References - ClassName::instanceMethodName
  4. Constructor References - ClassName::new

When to Use:

  • Use method references when you're simply calling an existing method
  • Use lambda expressions when you need complex logic or multiple statements
  • Method references improve readability for simple method calls
  • They make code more concise and expressive

Method references are a powerful feature that makes functional programming in Java more readable and maintainable, especially when working with streams and functional interfaces.

Leave a Reply

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


Macro Nepal Helper