-> Operator in Java
The -> operator in Java is used to define lambda expressions
It separates the parameters (on the left) from the body (on the right) of the lambda expression. Lambda expressions provide a concise way to implement functional interfaces.
Example: Lambda Expression
// Using a lambda expression to implement Runnable
Runnable r = () -> System.out.println("Running...");
r.run();
public class RunnableComparison {
public static void main(String[] args) {
// ---------------- OLD WAY (Pre-Java 8) ----------------
Runnable oldWay = new Runnable() {
@Override
public void run() {
System.out.println("Running... (Old Way)");
}
};
oldWay.run();
// ---------------- NEW WAY (Java 8+ Lambda) ----------------
Runnable lambdaWay = () -> System.out.println("Running... (Lambda Way)");
lambdaWay.run();
}
}
In this example,
() represents no parameters, and the body System.out.println("Running..."); is executed when run() is called.
Syntax
The general syntax of a lambda expression is:
(parameters) -> { body }
- Parameters: Can be omitted if there’s only one parameter and no type declaration.
- Body: Can be a single statement or a block of code.
Example: Functional Interface
@FunctionalInterface
interface Calculator {
int calculate(int a, int b);
}
public class Main {
public static void main(String[] args) {
// Lambda implementing Calculator interface
Calculator add = (a, b) -> a + b;
System.out.println("Sum: " + add.calculate(5, 3)); // Output: Sum: 8
}
}
Key Points
- Lambda expressions are primarily used with functional interfaces (interfaces with a single abstract method).
- They simplify code by eliminating the need for anonymous inner classes.
- Commonly used in Java Streams and Collections for operations like filtering, mapping, and reducing.
Limitations
- Lambdas cannot access non-final local variables.
- Overuse can make code harder to read if not used judiciously.
💡 Tip: Lambdas work only with functional interfaces (interfaces with exactly one abstract method), such as Runnable, Callable, Comparator, etc.
If you want, I can also show you how to run these in separate threads so you can see them in action concurrently.
Additional example
public class RunnableThreadExample {
public static void main(String[] args) {
// -------- OLD WAY (Pre-Java 8) --------
Thread oldThread = new Thread(new Runnable() {
@Override
public void run() {
System.out.println("Running in a thread... (Old Way)");
}
});
oldThread.start(); // Starts a new thread
// -------- NEW WAY (Java 8+ Lambda) --------
Thread lambdaThread = new Thread(() ->
System.out.println("Running in a thread... (Lambda Way)")
);
lambdaThread.start(); // Starts a new thread
// Optional: Wait for both threads to finish
try {
oldThread.join();
lambdaThread.join();
} catch (InterruptedException e) {
System.err.println("Thread interrupted: " + e.getMessage());
Thread.currentThread().interrupt(); // Restore interrupt status
}
System.out.println("Both threads have finished execution.");
}
}
How it works:
Old Way
- Create a
Threadby passing an anonymous inner class implementingRunnable. - Override
run()to define the task.
Lambda Way
- Pass a lambda expression directly to the
Threadconstructor. - Much shorter and cleaner.
Thread Execution
start()launches the thread asynchronously.join()ensures the main thread waits for both threads to finish.
public class RunnableThreadConcurrentExample {
public static void main(String[] args) {
// -------- OLD WAY (Pre-Java 8) --------
Thread oldThread = new Thread(new Runnable() {
@Override
public void run() {
for (int i = 1; i <= 5; i++) {
System.out.println("[Old Way] Message " + i);
try {
Thread.sleep(500); // Sleep for 0.5 seconds
} catch (InterruptedException e) {
System.err.println("Old thread interrupted.");
Thread.currentThread().interrupt();
break;
}
}
}
});
// -------- NEW WAY (Java 8+ Lambda) --------
Thread lambdaThread = new Thread(() -> {
for (int i = 1; i <= 5; i++) {
System.out.println("[Lambda Way] Message " + i);
try {
Thread.sleep(500); // Sleep for 0.5 seconds
} catch (InterruptedException e) {
System.err.println("Lambda thread interrupted.");
Thread.currentThread().interrupt();
break;
}
}
});
// Start both threads
oldThread.start();
lambdaThread.start();
// Wait for both threads to finish
try {
oldThread.join();
lambdaThread.join();
} catch (InterruptedException e) {
System.err.println("Main thread interrupted.");
Thread.currentThread().interrupt();
}
System.out.println("Both threads have finished execution.");
}
}
- What you’ll see when running:Both threads will print messages interleaved (order may vary each run).
Thread.sleep(500)simulates work and makes the concurrency visible.start()is used (notrun()), so both threads run in parallel.
💡 Key takeaway:
run()→ Executes in the current thread (no concurrency).start()→ Launches a new thread and callsrun()internally.