Java 8 new features
- Lambda Expressions
- Functional Interfaces
- Method References
- Stream API
- Optional Class
- Default Methods
- Date and Time API
- Parallel Arrays
Lambda Expressions
Section titled “Lambda Expressions”Syntax
Section titled “Syntax”(parameters) -> expression(parameters) -> { statements; }Examples
Section titled “Examples”// Traditional approachRunnable r1 = new Runnable() { @Override public void run() { System.out.println("Hello World"); }};
// Lambda approachRunnable r2 = () -> System.out.println("Hello World");
// With parametersComparator<Integer> comp = (a, b) -> a.compareTo(b);
// Multiple statementsFunction<String, Integer> func = (s) -> { System.out.println("Processing: " + s); return s.length();};Key Points
Section titled “Key Points”- Enable functional programming in Java
- Reduce boilerplate code
- Type inference allows omitting parameter types
- Must be compatible with functional interface
Functional Interfaces
Section titled “Functional Interfaces”An interface with exactly one abstract method, often called SAM.
It can contain multiple default or static methods.
Key Characteristics
Section titled “Key Characteristics”- Single Abstract Method (SAM): Must have exactly one abstract method
- Default Methods: Can have any number of default methods with implementations
- Static Methods: Can have any number of static methods
- Inheritance: Can extend another interface only if it doesn’t add any abstract methods
- Annotation:
@FunctionalInterfaceis optional but provides compile-time checking
Built-in Functional Interfaces Table
Section titled “Built-in Functional Interfaces Table”| Interface | Method Signature | Description | Common Use Cases |
|---|---|---|---|
| Function<T,R> | R apply(T t) | Takes one argument, produces a result | Transformations, mapping operations |
| Predicate | boolean test(T t) | Takes one argument, returns boolean | Filtering, conditional checks |
| Consumer | void accept(T t) | Takes one argument, returns void | Side-effects, consuming operations |
| Supplier | T get() | Takes no arguments, returns a value | Lazy initialization, factory methods |
| UnaryOperator | T apply(T t) | Takes one argument, returns same type | Operations where input = output type |
| BinaryOperator | T apply(T t1, T t2) | Takes two arguments, returns same type | Reduction operations, mathematical operations |
Examples
Section titled “Examples”// Function example - convert String to IntegerFunction<String, Integer> stringToInt = s -> Integer.parseInt(s);Integer result = stringToInt.apply("123"); // Returns 123
// Predicate example - check if string is emptyPredicate<String> isEmpty = s -> s.isEmpty();boolean test = isEmpty.test(""); // Returns true
// Consumer example - print elementConsumer<String> printer = s -> System.out.println(s);printer.accept("Hello World"); // Prints "Hello World"
// Supplier example - generate random numberSupplier<Double> randomSupplier = () -> Math.random();Double random = randomSupplier.get(); // Returns random number
// Method reference examplesFunction<String, Integer> parser = Integer::parseInt;Consumer<String> printerRef = System.out::println;Supplier<List<String>> listSupplier = ArrayList::new;Custom Functional Interface
Section titled “Custom Functional Interface”@FunctionalInterfaceinterface StringProcessor { String process(String input);
// Default method default StringProcessor andThen(StringProcessor after) { return input -> after.process(this.process(input)); }
// Static method static StringProcessor createDefault() { return input -> input.toUpperCase(); }}
// UsageStringProcessor toUpper = String::toUpperCase;StringProcessor addExclamation = s -> s + "!";StringProcessor combined = toUpper.andThen(addExclamation);
String result = combined.process("hello"); // Returns "HELLO!"Q: What makes an interface a functional interface? A: A functional interface must have exactly one abstract method, but can have any number of default or static methods.
Q: Why is @FunctionalInterface annotation useful? A: It’s optional but provides compile-time checking to ensure the interface has exactly one abstract method, preventing accidental addition of extra abstract methods.
Q: Can a functional interface extend another interface? A: Yes, but only if the parent interface doesn’t have any abstract methods, or if it has exactly one abstract method that the child interface doesn’t override with another abstract method.
Q: What are the most commonly used functional interfaces? A: The most common are Function, Predicate, Consumer, and Supplier from java.util.function package.
Method References
Section titled “Method References”Types of Method References
Section titled “Types of Method References”graph LR
A[Method References] --> B[Static Method]
A --> C[Instance Method
of particular object]
A --> D[Instance Method
of arbitrary object]
A --> E[Constructor Reference]
Syntax Examples
Section titled “Syntax Examples”// Static method referenceFunction<String, Integer> parser = Integer::parseInt;
// Instance method of particular objectString prefix = "Hello";Predicate<String> startsWith = prefix::startsWith;
// Instance method of arbitrary objectFunction<String, String> upperCase = String::toUpperCase;
// Constructor referenceSupplier<List<String>> listSupplier = ArrayList::new;Function<Integer, String[]> arrayCreator = String[]::new;Equivalent Lambda Expressions
Section titled “Equivalent Lambda Expressions”// Method referenceConsumer<String> printer = System.out::println;
// Equivalent lambdaConsumer<String> printerLambda = s -> System.out.println(s);Stream API
Section titled “Stream API”Stream Pipeline Concept
Section titled “Stream Pipeline Concept”Stream Creation
Section titled “Stream Creation”// From CollectionList<String> list = Arrays.asList("a", "b", "c");Stream<String> stream = list.stream();
// From ArraysStream<String> arrayStream = Arrays.stream(new String[]{"a", "b", "c"});
// Static factory methodsStream<String> ofStream = Stream.of("a", "b", "c");Stream<Integer> iterateStream = Stream.iterate(0, n -> n + 2).limit(10);Stream<Double> generateStream = Stream.generate(Math::random).limit(5);Intermediate Operations
Section titled “Intermediate Operations”| Operation | Description | Example |
|---|---|---|
filter() | Filters elements | .filter(s -> s.length() > 3) |
map() | Transforms elements | .map(String::toUpperCase) |
flatMap() | Flattens streams | .flatMap(list -> list.stream()) |
distinct() | Removes duplicates | .distinct() |
sorted() | Sorts elements | .sorted() |
peek() | Debugging operation | .peek(System.out::println) |
limit() | Limits size | .limit(10) |
skip() | Skips elements | .skip(5) |
Terminal Operations
Section titled “Terminal Operations”| Operation | Description | Return Type |
|---|---|---|
forEach() | Iterates elements | void |
collect() | Collects to collection | Collection |
toArray() | Converts to array | Array |
reduce() | Reduces to single value | Optional |
min()/max() | Finds min/max | Optional |
count() | Counts elements | long |
anyMatch()/allMatch()/noneMatch() | Condition checking | boolean |
findFirst()/findAny() | Finds elements | Optional |
Examples
Section titled “Examples”// Filter and collectList<String> filtered = list.stream() .filter(s -> s.startsWith("A")) .collect(Collectors.toList());
// Map and reduceint totalLength = list.stream() .map(String::length) .reduce(0, Integer::sum);
// GroupingMap<Integer, List<String>> groupedByLength = list.stream() .collect(Collectors.groupingBy(String::length));
// Parallel streamList<String> parallelResult = list.parallelStream() .map(String::toUpperCase) .collect(Collectors.toList());Optional Class
Section titled “Optional Class”Purpose
Section titled “Purpose”Container object which may or may not contain a non-null value
Creation Methods
Section titled “Creation Methods”Optional<String> empty = Optional.empty();Optional<String> of = Optional.of("value"); // throws NPE if nullOptional<String> ofNullable = Optional.ofNullable(maybeNull);Usage Patterns
Section titled “Usage Patterns”// Traditional null checkif (value != null) { System.out.println(value);}
// Optional approachoptionalValue.ifPresent(System.out::println);
// With default valueString result = optionalValue.orElse("default");
// With supplier for defaultString result = optionalValue.orElseGet(() -> generateDefault());
// Throw exception if emptyString result = optionalValue.orElseThrow(() -> new IllegalArgumentException());Method Chaining
Section titled “Method Chaining”Optional<String> result = Optional.ofNullable(user) .map(User::getAddress) .map(Address::getCity) .filter(city -> city.startsWith("New")) .orElse("Unknown");Default Methods
Section titled “Default Methods”Definition
Section titled “Definition”Methods in interfaces with implementation
interface Vehicle { // Traditional abstract method String getBrand();
// Default method default String turnAlarmOn() { return "Turning vehicle alarm on"; }
// Static method static int getHorsePower(int rpm, int torque) { return (rpm * torque) / 5252; }}Multiple Inheritance Resolution
Section titled “Multiple Inheritance Resolution”interface A { default void hello() { System.out.println("A"); }}
interface B { default void hello() { System.out.println("B"); }}
class C implements A, B { @Override public void hello() { A.super.hello(); // Explicitly choose which to call }}Date and Time API
Section titled “Date and Time API”Key Classes
Section titled “Key Classes”Examples
Section titled “Examples”// Current date/timeLocalDate today = LocalDate.now();LocalTime now = LocalTime.now();LocalDateTime current = LocalDateTime.now();
// Specific date/timeLocalDate date = LocalDate.of(2023, Month.DECEMBER, 25);LocalTime time = LocalTime.of(14, 30, 0);
// ManipulationLocalDate tomorrow = today.plusDays(1);LocalTime earlier = now.minusHours(2);
// FormattingDateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm");String formatted = current.format(formatter);
// ParsingLocalDateTime parsed = LocalDateTime.parse("2023-12-25 14:30", formatter);
// Duration and PeriodDuration duration = Duration.between(startTime, endTime);Period period = Period.between(startDate, endDate);Nashorn JavaScript Engine
Section titled “Nashorn JavaScript Engine”// Create JavaScript engineScriptEngineManager manager = new ScriptEngineManager();ScriptEngine engine = manager.getEngineByName("nashorn");
// Execute JavaScriptengine.eval("print('Hello from JavaScript!');");
// Call Java from JavaScriptengine.eval("var ArrayList = Java.type('java.util.ArrayList');");engine.eval("var list = new ArrayList();");Parallel Arrays
Section titled “Parallel Arrays”Parallel Operations
Section titled “Parallel Operations”int[] numbers = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
// Parallel array processingArrays.parallelSort(numbers);Arrays.parallelPrefix(numbers, (a, b) -> a + b);Arrays.parallelSetAll(numbers, i -> i * 2);Interview Questions & Answers
Section titled “Interview Questions & Answers”Q: What are the main advantages of Lambda Expressions?
Section titled “Q: What are the main advantages of Lambda Expressions?”A: Reduced boilerplate code, improved readability, enable functional programming, better API design, and parallel processing support.
Q: Explain the difference between map() and flatMap()
Section titled “Q: Explain the difference between map() and flatMap()”A: map() transforms each element to another element, while flatMap() transforms each element to a stream and then flattens all streams into one.
Q: When would you use Optional?
Section titled “Q: When would you use Optional?”A: When a method might return null, to avoid NullPointerException, and to explicitly indicate that a value might be absent.
Q: What is the purpose of default methods?
Section titled “Q: What is the purpose of default methods?”A: To add new methods to interfaces without breaking existing implementations, enabling interface evolution.
Q: How do streams support parallel processing?
Section titled “Q: How do streams support parallel processing?”A: Through parallelStream() and intermediate operations that can be executed concurrently, leveraging multi-core processors.
Q: Difference between Predicate and Function?
Section titled “Q: Difference between Predicate and Function?”A: Predicate takes input and returns boolean, Function takes input and returns output of any type.
Performance Considerations
Section titled “Performance Considerations”- Use parallel streams for large datasets only
- Prefer method references over lambdas for better readability
- Avoid stateful lambdas in parallel streams
- Use primitive streams (IntStream, etc.) for better performance
- Consider using
Arrays.parallelSort()for large arrays