Tags: books
OOP (Object-Oriented Programming) is a paradigm where everything is treated as an object — encapsulating state (fields) and behavior (methods) together.
A stream is a sequence of data items that are conceptually produced one at a time.
Parallelism vs Concurrency. Parallelism is executing the code across multiple GPU cores. However Concurrency is running various code accross single core. You want to use paralleism for CPU bound work. Concurrency for I/O bound work or slow task.
Functional programming focuses on:
Method references are a shorthand for lambda expressions that simply call a method. They make your code cleaner and easier to read.
File[] hiddenFiles = new File(".").listFiles(file -> file.isHidden());
You can use a method reference:
File[] hiddenFiles = new File(".").listFiles(File::isHidden);
Double a list using stream
import java.util.*;
import java.util.stream.Collectors;
public class Test {
public static void main(String[] args) {
List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5);
numbers
.stream()
.map(x -> x * x)
.collect(Collectors.toList())
.forEach(System.out::println);
}
}
Here collect is a terminal operation. It is eager—it triggers the processing of the stream and gathers the results. map is lazy, meaning it doesn’t process elements until a terminal operation (like collect) is called.
In programming language theory, the concept of first-class citizens (or first-class objects) refers to entities that can be used freely in all standard ways that other values can be used in a language.
Being passed as an argument to a function
Being returned from a function
Being assigned to a variable
Being stored in data structures
Functions Are Not First-Class Citizens (Before Java 8)
You couldn't pass a method directly as an argument to another method.
You couldn't assign a method to a variable.
You couldn't return a method from another method.
Functions are now first class citizens
Starting with Java 8 , functions became first-class citizens thanks to the introduction of:
Lambda expressions
Method references
Functional interfaces
The java.util.function
package
// Assigning a lambda to a variable
Function<Integer, Integer> square = x -> x * x;
// Invoke it
int result = square.apply(5);
// Passing a function as an argument
List<Integer> numbers = Arrays.asList(1, 2, 3, 4);
numbers.forEach(n -> System.out.println(n));
// Returning a function from a method
public Function<Integer, Integer> getMultiplier(int factor) {
return x -> x * factor;
}
Classes Are Also Not First-Class Citizens
Pass a class definition directly as a parameter
Return a class definition from a method (though you can return a Class<?> reference, which is metadata, not the class body itself)
public static void printClassName(Class<?> clazz) {
System.out.println("Class name: " + clazz.getName());
}
printClassName(String.class); // Passing metadata, not the class itself
A method reference allows you to refer to a method without executing it.
Before Java 8, if you wanted to list only the hidden files in a directory, you might write:
File[] hiddenFiles = new File(".").listFiles(new FileFilter() {
public boolean accept(File file) {
return file.isHidden();
}
});
Using Method Reference
File[] hiddenFiles = new File(".").listFiles(File::isHidden);
Here, File::isHidden
is a method reference to the isHidden()
method of the File class. It’s equivalent to this lambda expression:
file -> file.isHidden()
Lets you add two filters like
public static List<Apple> filterGreenApples(List<Apple> inventory) {
List<Apple> result = new ArrayList<>();
for (Apple apple : inventory) {
if (GREEN.equals(apple.getColor())) {
result.add(apple);
}
}
return result;
}
public static List<Apple> filterHeavyApples(List<Apple> inventory) {
List<Apple> result = new ArrayList<>();
for (Apple apple : inventory) {
if (apple.getWeight() > 150) {
result.add(apple);
}
}
return result;
}
Refactor this like where a method is passes a predicate parameter named p.
public static boolean isGreenApple(Apple apple) {
return Green.equals(apple.getColor());
}
public static boolean isHeavyApple(Apple apple) {
return apple.getWeight() > 150;
}
import java.util.function.Predicate;
static List<Apple> filterApples(List<Apple> inventory, Predicate<Apple> p) {
List<Apple> result = new ArrayList<>();
for (Apple apple : inventory) {
if (p.test(apple)) {
result.add(apple);
}
}
return result;
}
To test this you can call
filterApples(inventory, Apple::isGreenApple);
// or
filterApples(inventory, Apple::isHeavyApple);
In Java, the Predicate<T>
interface is part of the java.util.function package and plays a central role in functional-style programming.
@FunctionalInterface
public interface Predicate<T> {
boolean test(T t);
}
It defines a single abstract method : test(T t)
This makes it a functional interface , which means you can assign lambda expressions or method references to it.
It represents a boolean-valued function (i.e., a condition) on one input.
import java.util.function.Predicate;
public class Main {
public static void main(String[] args) {
// Define a predicate using a lambda
Predicate<String> isLongerThan5 = s -> s.length() > 5;
// Use the test() method
System.out.println(isLongerThan5.test("hello")); // false
System.out.println(isLongerThan5.test("longstring")); // true
}
}
List<String> words = List.of("apple", "bat", "carrot", "dog", "elephant");
List<String> longWords = words.stream()
.filter(s -> s.length() > 5)
.toList();
Predicate
Predicate<String> startsWithA = s -> s.startsWith("a");
Predicate<String> endsWithZ = s -> s.endsWith("z");
// Combine with .and(), .or(), .negate()
Predicate<String> combined = startsWithA.and(endsWithZ);
System.out.println(combined.test("applez")); // true
System.out.println(combined.test("apple")); // false
public class MeaningOfThis {
public final int value = 4;
public void doIt() {
int value = 6;
Runnable r = new Runnable() {
public final int value = 5;
public void run() {
int value = 10;
System.out.println(this.value);
}
};
r.run();
}
public static void main(String[] args) {
MeaningOfThis m = new MeaningOfThis();
m.doIt();
}
}
First, MeaningOfThis has a variable value with value 4. Next, the doIt method declares a local value with value 6, shadowing the field. Then, an inner class is defined with its own value set to 5. Inside its run method, another local value is declared with value 10. However, this.value refers to the inner class's field, so the output is 5.
Before Java 5, a common way to execute code in a separate thread was to implement Runnable and pass it to a Thread object:
Runnable r = new Runnable() {
@Override
public void run() {
System.out.println("Running in a thread");
}
};
Thread t = new Thread(r);
t.start(); // This actually starts a new thread
r.run(), which does not start a new thread — it just calls the method directly on the current thread.
Java 5 introduced the java.util.concurrent package, including ExecutorService, which abstracts thread creation and management. You submit tasks to a thread pool, and you can optionally get a result via a Future.
import java.util.concurrent.*;
public class Example {
public static void main(String[] args) throws Exception {
ExecutorService executor = Executors.newFixedThreadPool(2);
// Runnable example (no result)
executor.submit(() -> System.out.println("Running in a thread pool"));
// Callable example (returns a result)
Future<String> future = executor.submit(() -> {
Thread.sleep(500);
return "Task completed!";
});
System.out.println(future.get()); // Wait and get the result
executor.shutdown();
}
}
Java 8 (2014) introduced CompletableFuture in java.util.concurrent.
It implements both Future and CompletionStage.
Adds non-blocking, chainable async programming
CompletableFuture.supplyAsync(() -> "Hello")
.thenApply(s -> s + " World")
.thenAccept(System.out::println);
No manual get() needed — it reacts when data is ready.
Supports chaining (thenApply, thenCompose) and combining multiple tasks (allOf, anyOf).
it's the anonymous functions
that can be passed around. So there are first class citizens in java. It has three parts
Lambda parameters
Arrow
Lambda body
(String s ) -> s.length() // takes one parameter of tyep string and returns an int
(Apple a) -> a.getWeight() > 2 // return a boolean (implied)
(int x, int y) -> {
return 2;
}
() -> 42 //takes no parameters and returns an int
Interface that only has exactly one abstract method. e.g. predicate, runnable, comparator.