java8-features

Some important Java 8 features:
Some important Java 8 features:

Contents
  1. Functional Interfaces
  2. Lambda Expressions
  3. Method References
  4. Stream API
  5. Default/Static methods in Interfaces
  6. Optional Class
  7. Collectors Class
1. Functional Interfaces
A Functional Interface should contain only one abstract method and can have many default methods.
FIs are used to supply logic as variables to lambda expressions and method references, constructor method references instead of objects.
FIs are Java way of implementing Functional Programming where logic can be assigned to variable level instead of object.


     package com.blogspot.codingstreams;
    
     interface IFormatting{
     public abstract String formatText(String text);
     }
    
     // Normal Approach - Creating Implementation class
     class UpperCaseFormatting implements IFormatting{
    
     @Override
     public String formatText(String text) {
     return text.toUpperCase();
     }
     }
    
     public class FunctionalInterfacesDemo {
    
     public static void main(String[] args) {
    
     // Normal Approach - Creating object that holds the Impl. class logic.
     IFormatting ucformat = new UpperCaseFormatting();
     String formattedText = ucformat.formatText("this is a sample text");
     System.out.println(formattedText);
    
     // Anonymous Class Approach - Nameless Impl. class.
     IFormatting lowerCaseFormatting = new IFormatting() {
     @Override
     public String formatText(String text) {
     return text.toLowerCase();
     }
     };
     System.out.println(lowerCaseFormatting.formatText("SOME UPPER TEXT."));
    
     }
     }
    
     Output:
     THIS IS A SAMPLE TEXT
     some upper text.


     package com.blogspot.codingstreams;
    
     @FunctionalInterface
     interface IFormatting{
     public abstract String formatText(String text);
     }
    
     public class FunctionalInterfacesDemo {
     public static void main(String[] args) {
     // Functional Programming Approach
     IFormatting uppercaseFormatting = (String message) -> message.toUpperCase();
     System.out.println(uppercaseFormatting.formatText("SOME mixed cASE Text."));
     }
     }
    
     Output:
     SOME MIXED CASE TEXT.

2. Lambda Expressions
Lambda expressions are FP way to implement logic. In Java, Lambdas are used to prevent boilerplate code.
Improves code reusability, readability and code looks more compact. Helps to support FP & Streams API in Java.
No need to write Impl. classes and their objects for invoking logic.

     package com.blogspot.codingstreams;
    
     interface Greet{
     public abstract String greet(String username, int age);
     }
    
     class WelcomeGreet implements Greet{
    
     @Override
     public String greet(String username, int age) {
     return "Greetings, "+username+"("+age+"). Welcome to the Java World !";
     }
     }
    
     public class LambdaExpressionsTest {
    
     public static void main(String[] args) {
    
     // Normal way
     Greet greet = new WelcomeGreet();
     System.out.println(greet.greet("Abcd Efgh",23));
    
     // Lambda Expressions way
     Greet greet2 = (String username, int age) -> { return "Greetings, "+username+"("+age+"). Welcome to the Java World !"; };
     System.out.println(greet2.greet("Hjdfh Djfh",25));
    
     // Compact Lambda expression
     Greet greet3 = (username,age) -> "Greetings, "+username+"("+age+"). Welcome to the Java World !";
     System.out.println(greet3.greet("Geff Ktyf",32));
     }
     }
    
     Output:
     Greetings, Abcd Efgh(23). Welcome to the Java World !
     Greetings, Hjdfh Djfh(25). Welcome to the Java World !
     Greetings, Geff Ktyf(32). Welcome to the Java World !

3. Method Reference
It is shorthand version to refer methods of Functional Interface classes.
Makes code compact and improves readability by removing method calling with explicit parameters passing.
Used to refer(invoke) static methods, instance methods and constructors.


     package com.blogspot.codingstreams;
    
     import java.util.ArrayList;
     import java.util.Arrays;
     import java.util.HashMap;
     import java.util.List;
     import java.util.Map;
     import java.util.function.Supplier;
    
     @FunctionalInterface
     interface ProcessPairs{
     public abstract void printPairs(String k, String v);
     }
    
     public class MethodReferencesTest {
     public static void main(String[] args) {
    
     List nums = Arrays.asList(1,3,4);
    
     // Method reference from String class
     nums.forEach(System.out::println);
    
     Map kvPairs = new HashMap<>();
     kvPairs.put("PRIMARY", "RAM");
     kvPairs.put("SECONDARY", "HDD");
    
     // Below method reference don't work as println accepts only one param
     // kvPairs.forEach(System.out::println);
    
     // New Impl. to handle k,v pairs
     ProcessPairs processPairs = (k,v) -> System.out.println("key="+k+", value="+v);
     kvPairs.forEach(processPairs::printPairs);
    
     // Constructor reference
     Supplier emptyListSupplier = ArrayList::new;
     System.out.println(emptyListSupplier.get());
    
     Supplier emptyStringSupplier = String::new;
     String initString = emptyStringSupplier.get();
     System.out.println("Init. String: "+initString);
    
    
     }
     }

4. Stream API
Stream is a collection of items that can be processed sequentially or parallelly.
Streams can be formed from existing Java collections like lists, maps, arrays, etc.
Stream API provides various FP style computational operations that can be performed on a stream.
Stream operations are divided into intermediate and terminal operations and chained together to form a pipeline.
A Stream pipeline is lazy by default and will be executed only if it has terminal operation.
Pipelines produces new stream on executing rather than chaning the original stream.
Pipeline executes only once and can not reprocess the same stream.


     package com.blogspot.codingstreams;
    
     import java.util.Arrays;
     import java.util.List;
     import java.util.stream.Stream;
    
     public class StreamsTest {
    
     public static void main(String[] args) {
    
     List namesList = Arrays.asList("Ujdf","Gewr","Oygdf","Cguhy","Tjdf");
    
     // Creating Stream from list
     Stream namesStream = namesList.stream();
    
     Stream filteredStream = namesStream.filter(n -> n.startsWith("O"));
     filteredStream.forEach(System.out::println);
    
     // Parallel stream - Pipeline can be processed parallel by leveraging processor cores
     Stream filteredParallelStream = filteredStream.parallel();
    
     // Sequential Stream - converting to sequential stream for batch processing on single core
     Stream filteredSequentialStream = filteredParallelStream.sequential();
     }
     }

5. Default/Static methods in Interfaces
Both static & default methods are used to provide common behavior/functionality without implementation classes.
Static methods can not be overidden by Implementation classes, as they are out of scope
Default methods can be overidden by the Implementation classes.
Default methods provide backward compatibility by not enforcing all the Implementation classes on adding new functionality.


     package com.blogspot.codingstreams;
    
     import java.util.Locale;
    
     interface CountryCodeImpl{
    
     // static method to provide common functionality via Interface itself.
     static String getDefaultCode() {
     return "KJD-34D-E4F";
     }
    
     // default methods to provide common functionality via instance.
     default String getDefaultCodeByLocale(Locale locale) {
    
     if(locale == null)
     return getDefaultCode();
    
     String code = null;
    
     switch (locale.getCountry()) {
     case "IN": code = "IDL-H7G-F3T"; break;
     case "UK": code = "OKF-VLY-DFK"; break;
     case "US": code = "LDC-SDO-ODV"; break;
     default: code = getDefaultCode(); break;
     }
    
     return code;
     }
     }
    
     public class InterfacesTest {
     public static void main(String[] args) {
    
     // Testing Interface static method
     String defaultCode = CountryCodeImpl.getDefaultCode();
     System.out.println("Default Code:"+defaultCode);
    
     // Testing Interface default method
     // Created anonymous impl. for the Interface
     CountryCodeImpl countryCodeImpl = new CountryCodeImpl(){};
     String usCode = countryCodeImpl.getDefaultCodeByLocale(Locale.US);
     System.out.println(usCode);
    
     // Testing Country locale
     Locale localeCountry = Locale.getDefault();
     System.out.println(countryCodeImpl.getDefaultCodeByLocale(localeCountry));
    
     // Testing default code
     System.out.println(countryCodeImpl.getDefaultCodeByLocale(null));
     }
     }

6. Optional Classes
Option classes are wrapper around the given value to provide extra layer of safety in dealing with null checking.
Significantly decreases null checks and can provide default values in case of nulls.
Available Optional classes are: Optional generic, OptionalInt, OptionalDouble and OptionalLong

     package com.blogspot.codingstreams;
    
     import java.util.Optional;
     import java.util.OptionalInt;
     import java.util.function.Supplier;
    
     public class OptionalClassesTest {
    
     private static String code = "3434-3482-5344";
     private static String noCode = "0000-0000-0000";
    
     private static String noCode() {
     return noCode;
     }
    
     public static void main(String[] args) {
    
     // Wrapping optional class for safety
     Optional safeCode = Optional.of(code);
    
     // Safety method to check whether the object has value or not by avoiding Null Pointer Exception.
     System.out.println(safeCode.isPresent()); //true
    
     // Safety methods to derive the value
     String derivedCode = safeCode.orElse(noCode);
     System.out.println(derivedCode); //3434-3482-5344
    
     // orElse - Default value when finding null
     safeCode = Optional.empty();
     derivedCode = safeCode.orElse(noCode);
     System.out.println(derivedCode); //0000-0000-0000
    
     // orElseGet needs supplier
     Supplier noCodeSupplier = () -> { return noCode; };
     derivedCode = safeCode.orElseGet(noCodeSupplier);
     System.out.println(derivedCode); //0000-0000-0000
    
     // orElseGet with simple syntax of supplier
     derivedCode = safeCode.orElseGet(() -> noCode);
     System.out.println(derivedCode); //0000-0000-0000
    
     // orElseGet with method reference
     derivedCode = safeCode.orElseGet(OptionalClassesTest::noCode);
     System.out.println(derivedCode); //0000-0000-0000
    
     int defaultVal = -1;
     OptionalInt calculatedVal = OptionalInt.of(34);
    
     int derivedVal = calculatedVal.orElse(defaultVal);
     System.out.println("Derived Value is: "+derivedVal); //34
    
     calculatedVal = OptionalInt.empty();
     derivedVal = calculatedVal.orElse(defaultVal);
     System.out.println("Derived Value is: "+derivedVal); //-1
    
     calculatedVal = OptionalInt.of(100);
    
     //ifPresent with print consumer
     calculatedVal.ifPresent(System.out::println); //100
    
     // Another consumer example
     calculatedVal.ifPresent(n -> System.out.println(n*n)); // 10000
     }
     }

7. Collectors Class
Collectors is a helper class in util package helps in summarizing final elements.
Provides functionality to aggregate, accumulate and transform the end results from the streams.
Acts as reducing job at the last step of Java stream pipeline.

     package com.blogspot.codingstreams;
    
     import java.util.Arrays;
     import java.util.List;
     import java.util.Map;
     import java.util.Set;
     import java.util.stream.Collectors;
    
     public class CollectorsTest {
     public static void main(String[] args) {
    
     // List of Integer numbers
     List nums = Arrays.asList(1,2,3,4,5,6,7,8,9,10,10,2,3,4,6,1,4,6);
    
     // List of even numbers
     List evenNums = nums.stream().filter(n -> n%2 == 0).collect(Collectors.toList());
     System.out.println(evenNums); // [2, 4, 6, 8, 10, 10, 2, 4, 6, 4, 6]
    
     // Set to have unique numbers
     Set uniqueEvenNums = nums.stream().filter(n -> n%2 == 0).collect(Collectors.toSet());
     System.out.println(uniqueEvenNums); // [2, 4, 6, 8, 10]
    
     // Count the result
     Long records = nums.stream().collect(Collectors.counting());
     System.out.println(records); // 18
    
     // Calculate average
     Double numsAvg = nums.stream().collect(Collectors.averagingLong(n -> n));
     System.out.println(numsAvg); // 5.055555555555555
    
     // Get values as String
     String numsString = nums.stream().map(n -> String.valueOf(n)).collect(Collectors.joining(","));
     System.out.println(numsString); // 1,2,3,4,5,6,7,8,9,10,10,2,3,4,6,1,4,6
    
     // Grouping by value
     Map numsCount = nums.stream().collect(Collectors.groupingBy(n -> n, Collectors.counting()));
     System.out.println(numsCount); //{1=2, 2=2, 3=2, 4=3, 5=1, 6=3, 7=1, 8=1, 9=1, 10=2}
    
     }
     }

Compiled on WEDNESDAY, 16-OCTOBER-2024, 08:10:29 PM IST

Comments

Popular posts from this blog

hadoop-installation-ubuntu

jenv-tool

hive-installation-in-ubuntu