Java 8 provides multiple good features for Java Programming. In this article we will learn Java 8 core features along with working examples. Java 8 provides good features to make developers much more productive.
Before starting I have very Basic Quesitons:
Since, Functional Programming has no state, there’s no blocking or concurrency issue. Functional Programming removes moving parts and optimizes operation for concise and error-free code. Wouldn’t adopting this particular way of coding (no state and no mutable variables) in OOP make code more effective and efficient?
A functional interface in Java is an interface that contains only a single abstract (unimplemented) method. A functional interface can contain default and static methods which do have an implementation, in addition to the single unimplemented method.
The :: operator is used in method reference to separate the class or object from the method name. In simple words, it is a shorthand notation of a lambda expression to call a method.
An interface and an abstract class is almost similar except that we can create constructor in the abstract class whereas we can’t create constructor in an interface.
A method in java has name, parameter list, body and its return type. While, lambda expression only has a body and a parameter list. It makes my code cleaner.
I like using Lambda Expression because it makes my code cleaner. Lambda expression is a new feature which is introduced in Java 8. A lambda expression is an anonymous function. A function that doesn’t have a name and doesn’t belong to any class.
Basically, a method (or function) in Java has these main parts:
A Lambda Expression in Java has these main parts:
//Syntax of lambda expression (parameter_list) -> {function_body}
The term Java functional interface was introduced in Java 8. A functional interface in Java is an interface that contains only a single abstract (unimplemented) method. A functional interface can contain default and static methods which do have an implementation, in addition to the single unimplemented method. For example, the Runnable interface from package java.lang; is a functional interface because it constitutes only one method i.e. run().
Predefined Functional Interfaces: Java has provided us with several functional interfaces.
Here is a Java functional interface example:
public interface MyFunctionalInterface { public void execute(); }
Only one method is there named execute()
MyFunctionalInterface lambda = () -> { System.out.println("Executing…"); }
Example 1: Java Lambda Expression with no parameter
@FunctionalInterface interface MyFunctionalInterface { //A method with no parameter public String printMessage(); } public class Example { public static void main(String args[]) { // lambda expression MyFunctionalInterface msg = () -> { return "Hello Developer"; }; System.out.println(msg.printMessage()); } }
Output: Hello Developer
Example 2: Java Lambda Expression with single parameter
public class Java8Features { @FunctionalInterface interface MyFunctionalInterface { // A method with a single parameter public int incrementTheNumber(int a); } public static void main(String[] args) { // lambda expression with a single parameter number MyFunctionalInterface f = (number) -> number + 50; System.out.println(f.incrementTheNumber(20)); } }
Output: 70
Example 3: Java Lambda Expression with Multiple Parameters
@FunctionalInterface interface MyFunctionalInterface { public String strconcat(String str1, String str2); } public class Java8Features { public static void main(String args[]) { // lambda expression with multiple arguments MyFunctionalInterface f = (str1, str2) -> str1 + str2; System.out.println(f.strconcat("Hello ", "Developer")); } }
Output: Hello Developer
It is a shorthand notation of a lambda expression to call a method.
If your lambda expression is like this: str -> System.out.println(str) then you can replace it with a method reference like this: System.out::println The :: operator is used in method reference to separate the class or object from the method name.
In Java 8 there are 4 types of Method references: 1. Method reference to an instance method of an object – object::instanceMethod 2. Method reference to a static method of a class – Class::staticMethod 3. Method reference to an instance method of an arbitrary object of a particular type – Class::instanceMethod 4. Method reference to a constructor – Class::new public static class Car { public static Car create( final Supplier< Car > supplier ) { return supplier.get(); } public static void collide( final Car car ) { System.out.println( "Collided " + car.toString() ); } public void follow( final Car another ) { System.out.println( "Following the " + another.toString() ); } public void repair() { System.out.println( "Repaired " + this.toString() ); } } Examples: 1. Method reference to an instance method of an object::instanceMethod final Car police = Car.create( Car::new ); cars.forEach( police::follow ); 2. Method reference to a static method of a class – Class::staticMethod cars.forEach( Car::collide ); 3. Method reference to an instance method of an arbitrary object of a particular type – Class::instanceMethod cars.forEach( Car::repair ); 4. Method reference to a constructor – Class::new final Car police = Car.create( Car::new ); cars.forEach( police::follow );
@FunctionalInterface interface MyFunctionalInterface { /* * This is a default method so we need not to implement this method in the * implementation classes */ default void newMethod() { System.out.println("Newly added default method"); } /* * Already existing public and abstract method We must need to implement this * method in implementation classes. */ void existingMethod(String str); } public class DefaultMethodExample implements MyFunctionalInterface { // implementing abstract method public void existingMethod(String str) { System.out.println("Existing Method String is: " + str); } public static void main(String[] args) { DefaultMethodExample obj = new DefaultMethodExample(); // calling the default method of interface obj.newMethod(); // calling the abstract method of interface obj.existingMethod("Default Method Test."); } } Output: Newly added default method Existing Method String is: Default Method Test.
@FunctionalInterface interface MyFunctionalInterface{ /* This is a default method so we need not * to implement this method in the implementation * classes */ default void newMethod(){ System.out.println("Newly added default method"); } /* This is a static method. Static method in interface is * similar to default method except that we cannot override * them in the implementation classes. * Similar to default methods, we need to implement these methods * in implementation classes so we can safely add them to the * existing interfaces. */ static void anotherNewMethod(){ System.out.println("Newly added Static Method"); } /* Already existing public and abstract method * We must need to implement this method in * implementation classes. */ void existingMethod(String str); } public class StaticMethodExample implements MyFunctionalInterface{ // implementing abstract method public void existingMethod(String str){ System.out.println("Existing Method String is: "+str); } public static void main(String[] args) { StaticMethodExample obj = new StaticMethodExample(); //calling the default method of interface obj.newMethod(); //calling the static method of interface MyFunctionalInterface.anotherNewMethod(); //calling the abstract method of interface obj.existingMethod("Static Method Test."); } }
Output:
Newly added default method
Newly added Static Method
Existing Method String is: Static Method Test.
interface MyFunctionalInterface { default void newMethod() { System.out.println("Newly added default method"); } void existingMethod(String str); } interface MyFunctionalInterface2 { default void newMethod() { System.out.println("Newly added default method"); } void disp(String str); } public class DiamondProblemExample implements MyFunctionalInterface, MyFunctionalInterface2 { // implementing abstract methods public void existingMethod(String str) { System.out.println("Existig Method String is: " + str); } public void disp(String str) { System.out.println("Disp Method String is: " + str); } public static void main(String[] args) { DiamondProblemExample obj = new DiamondProblemExample(); // calling the default method of interface obj.newMethod(); } } Output: Compilation Error: Duplicate default methods named newMethod with the parameters () and () are inherited from the types MyFunctionalInterface2 and MyFunctionalInterface
interface MyFunctionalInterface { default void newMethod() { System.out.println("Newly added default method"); } void existingMethod(String str); } interface MyFunctionalInterface2 { default void newMethod() { System.out.println("Newly added default method"); } void disp(String str); } public class DiamondProblemExample implements MyFunctionalInterface, MyFunctionalInterface2 { // implementing abstract methods public void existingMethod(String str) { System.out.println("Existing Method String is: " + str); } public void disp(String str) { System.out.println("Disp Method String is: " + str); } // Implementation of duplicate default method public void newMethod() { System.out.println("Implementation of default method"); } public static void main(String[] args) { DiamondProblemExample obj = new DiamondProblemExample(); // calling the default method of interface obj.newMethod(); } } Output: Implementation of default method
Optional Class – java.util package – This class is introduced to avoid NullPointerException that we frequently encounters if we do not perform null checks in our code. Optional is just a container: it can hold a value of some type T or just be null.
public class OptionalClassExample { public static void main(String[] args) { // String array of length 5 String[] str = new String[5]; // Getting the substring String str2 = str[4].substring(1, 3); // Displaying substring System.out.print(str2); } } Output Exception in thread "main" java.lang.NullPointerException
import java.util.Optional; public class OptionalClassExample { public static void main(String[] args) { // String array of length 5 String[] str = new String[5]; Optional<String> isNull = Optional.ofNullable(str[4]); if (isNull.isPresent()) { // Getting the substring String str2 = str[4].substring(1, 3); // Displaying substring System.out.print(str2); } else { // else part System.out.println("Cannot get the substring from an empty string"); } } } Output Cannot get the substring from an empty string
import java.util.Arrays; import java.util.List; import java.util.stream.Collectors; public class StreamExample { public static void main(String[] args) { List<String> list = Arrays.asList("Java8", "StreamAPI", null, "Test", null); List<String> result = list.stream().collect(Collectors.toList()); result.forEach(System.out::println); } } Output Java8 StreamAPI null Test null
import java.util.Arrays; import java.util.List; import java.util.stream.Collectors; public class StreamExample { public static void main(String[] args) { List<String> list = Arrays.asList("Java8", "StreamAPI", null, "Test", null); List<String> result = list.stream().filter(str -> str != null).collect(Collectors.toList()); result.forEach(System.out::println); } } Output Java8 StreamAPI Test
After having default and static methods inside the interface, we think about the need of abstract class in Java. An interface and an abstract class is almost similar except that you can create constructor in the abstract class whereas you can’t do this in interface.
abstract class AbstractClass{ public AbstractClass() { // constructor System.out.println("You can create constructor in abstract class"); } abstract int add(int a, int b); // abstract method int sub(int a, int b){ // non-abstract method return a-b; } static int multiply(int a, int b){ // static method return a*b; } } public class AbstractTest extends AbstractClass{ public int add(int a, int b){ // implementing abstract method return a+b; } public static void main(String[] args) { AbstractTest a = new AbstractTest(); int result1 = a.add(20, 10); // calling abstract method int result2 = a.sub(20, 10); // calling non-abstract method int result3 = AbstractClass.multiply(20, 10); // calling static method System.out.println("Addition: "+result1); System.out.println("Substraction: "+result2); System.out.println("Multiplication: "+result3); } }
Result (Output):
You can create constructor in abstract class Addition: 30 Substraction: 10 Multiplication: 200
package com.the.basic.tech.info.java8.base64; import java.nio.charset.StandardCharsets; import java.util.Base64; public class Base64s { public static void main(String[] args) { final String text = "Base64 finally in Java 8!"; final String encoded = Base64 .getEncoder() .encodeToString( text.getBytes( StandardCharsets.UTF_8 ) ); System.out.println( encoded ); final String decoded = new String( Base64.getDecoder().decode( encoded ), StandardCharsets.UTF_8 ); System.out.println( decoded ); } } The console output from program run shows both encoded and decoded text: QmFzZTY0IGZpbmFsbHkgaW4gSmF2YSA4IQ== Base64 finally in Java 8!
The PermGen space is gone and has been replaced with Metaspace (JEP 122). The JVM options -XX:PermSize and –XX:MaxPermSize have been replaced by -XX:MetaSpaceSize and -XX:MaxMetaspaceSize respectively.
Have a great day. Happy learning 🙂