Java 8 Functional Programming Examples
Java 8 Functional Programming Examples
In Java, functional interfaces are leveraged with lambda expressions to perform operations such as checking if a number is prime or composite. In the given document, a Predicate<Integer> is used to evaluate whether a number is prime. It checks divisibility of the number by any integer up to its half. The Predicate has a test method that returns true for prime and false for composite numbers. A BiConsumer is then used to output the result as 'Prime' or 'Composite' .
The bi-consumer enhances flexibility by allowing operations dependent on multiple conditions or inputs. In the primeOrComposite method, the BiConsumer<Boolean, Integer> accepts a boolean result (from the primality test on an integer n) and the integer n itself, enabling decision-making based on the combined input. This usage facilitates a clean way to handle both the determination and display logic by bifurcating functionality—testing primality and then conveying output conditionally based on results, promoting a separation of concerns and enhancing code readability .
The provided Java snippets feature elements of the Strategy and Command design patterns. The Strategy pattern is realized through lambda expressions for determining the sorting strategy for Employee objects. This pattern's effectiveness is evident in the separation of strategy (comparator logic) from the actual sorting mechanism. The Command pattern is displayed via Consumer and Bi-Consumer use to separate operation encapsulation (e.g., printing elements). Both patterns heighten code modularity and flexibility, substantiating efficient design choice, albeit with potential verbosity when not utilizing Streams or method references for more concise syntax .
The document explains calculating a factorial using Java's functional interfaces through the Consumer interface. A Consumer<Integer> is defined to compute the factorial by multiplying numbers in a loop from 1 to the given number, updating a variable 'f' to hold the product. If the input number is negative, the Consumer prints a message stating the number should be non-negative. This showcases using the Consumer interface to encapsulate the logic required to calculate and display the factorial of a number .
Predicates enhance code modularity by abstracting the condition testing logic into reusable units. In the document, a Predicate is used to encapsulate the logic for testing if a number is prime. The design improves modularity by isolating the logic required to determine primality, making it extensible and reusable across different methods. Nonetheless, their limitation arises from their single-input nature, as more complex conditions requiring multiple inputs need bi-predicates or more cumbersome combinatorial logic .
Lambda expressions in Java serve as a means to implement functional programming, enabling developers to write succinct and expressive code particularly for operations typically performed via anonymous classes. In the context of sorting a list of objects, lambda expressions are used to define a Comparator inline. For example, to sort a list of Employee objects by name, a lambda expression is used like: Collections.sort(empList, (o1, o2) -> o1.name.compareTo(o2.name)); This line of code sorts the list by employee names in ascending order .
The procedural approach in storeElements is prone to inefficiencies, such as unnecessary loops and mutable state management. One pitfall is iterating twice over essentially the same data for adding and then filtering. To avoid this, one could leverage functional programming paradigms, employing stream operations to handle both processes succinctly. Additionally, using Streams would mitigate potential issues of incorrect element comparison (`==` instead of `.equals()`) by allowing the natural flow of filtering and processing data through method references or lambda expressions seamlessly .
The method storeElements demonstrates functional programming by using a Consumer to add elements to a list. Functional programming principles are underscored by lambda expressions used in conjunction with the list operations. However, for improved efficiency, the body of the method could utilize Java Streams for both the list population and filtering processes. For example, converting the string input directly into a List using Stream operations like Arrays.stream(input.split(",")).collect(Collectors.toList()) would enhance performance and code clarity by minimizing intermediate steps .
Streams in Java provide a high-level abstraction for operations on collections, enabling functional-style programming. They interact seamlessly with functional interfaces to perform operations such as filter, map, and collect with less boilerplate code. In the documented method storeElements, while Streams are not mandatory, their typical use aligns with the lambda-driven operations and Consumers/Suppliers approach introduced. Through stream operations, tasks like filtering elements satisfying specific conditions or mapping operations can be efficiently executed, though the document employs traditional iteration .
In Java, Consumers and Suppliers are functional interfaces applied to handle data without returning and a means to retrieve data without taking any parameters, respectively. According to the document, a Consumer is used to add strings to a list. For instance, in a method storeElements, a Consumer<String> c is employed to add each element from an input string, split by commas, into a List<String>. Although a Supplier was defined to print elements, it was commented out and not used, indicating the capabilities of Suppliers to retrieve and process data .


![c.accept(str[i]);
}
// Supplier<Void> supp=()->{
//](/p?url=https%3A%2F%2Fscreenshots.scribd.com%2FScribd%2F252_100_85%2F356%2F534587777%2F3.jpeg&__src=https%3A%2F%2Fwww.scribd.com%2Fdocument%2F534587777%2Fjava8-innards-txt&__type=image)