dcsimg

Core Java 8 Features and Examples

This is part four in an ongoing series that covers Core Java releases. This time we will be looking at Java 8 and its main new features and additions. These include:

Lambda expressions Parallel array operations Method parameter reflection Repeating annotations Method references

I have included some of the most important core Java language enhancements released in JDK 8.0, alongside a host of code samples so you can better understand how those new features work. You can paste these code snippets directly into your IDE or code editor or type them in to get a better feel for writing them as code.

Core Java 8 Release Details:

  • Java SE 8 Release Date: 18-03-2014
  • Java SE 8 Code Name: [Not Available]

You can read the previous part in this series by visiting Java SE 7 new features and code examples.

Lambda Expressions in Java

Lambda expressions offer a new way to refer to anonymous methods. It can be viewed more as a method interface that provides the accepted parameters alongside a method body. In some ways, Lambda expressions solve the vertical problem — which leads to more easily maintained code and fewer lines of code needed to achieve the same functionality.

Here is an example of Lambda expressions in Java:

 public class jdk8_LambdaExpressions {   
    public static void main(String[] args) {   
       Runnable r2 = () -> System.out.println("Son of God");   
       r2.run(); // note that this is not starting a thread   
    }   
  }   


Parallel Array Operations using Java

Another powerful new feature added to JDK 8 is the fact that parallel operations are now allowed on the same set of data. This is achieved by creating a stream out of your data. This method guarantees faster processes if your computer system has multiple processor cores.

Here is sample code showing parallel array operations in Java:

 import java.util.ArrayList;   
  import java.util.List;   
  import java.util.Random;   
  public class jdk8_ParallelArrayOperations {   
    static List projects = new ArrayList();   
    public static void main(String[] args) {   
       Project project = new Project();   
       project.setName("development");   
       projects.add(project);   
       project = new Project();   
       project.setName("testing");   
       projects.add(project);   
       project = new Project();   
       project.setName("build");   
       projects.add(project);   
       projects.stream().parallel().forEach(a->action(a));   
       for(Project p: projects) {   
         System.out.println(p.getName()+":"+p.getManager());   
       }   
    }   
    public static void action(Project x) {        
       int pmR=new Random().nextInt(1000);   
       x.setManager("john"+pmR);   
    }   
  }   
   
  class Project {   
    String name;   
    String manager;   
    public String getName() {   
       return name;   
    }   
    public void setName(String name) {   
       this.name = name;   
    }   
    public String getManager() {   
       return manager;   
    }   
    public void setManager(String manager) {   
       this.manager = manager;   
    }   
  }   


 

Method Parameter Reflection with Java

Another great feature added in Java 8 - which is particularly useful when a developer is using reflection or debugging large amounts of code - is additional introspection on real parameter names. By compiling your code using JDK 8 and the following command, programmers can enable this feature. Note that enabling method parameter reflection will lead to the use of additional memory/bytecode size.

 javac -parameters jdk8_MethodParameterReflection.java    


 

The method below illustrates the use of method parameter reflection in a Java application.

 import java.lang.reflect.Method;   
  import java.lang.reflect.Parameter;   
   
  public class jdk8_MethodParameterReflection {   
    public static void main(String[] args) {        
       Class clazz = BlackHat.class;   
       for(Method m: clazz.getDeclaredMethods()) {   
         for(Parameter p: m.getParameters()) {   
            System.out.println(p.getName());   
         }   
       }   
    }   
  }   
  class BlackHat {      
    public void payload(String hostName, int hostPort, boolean howdy) {        
       System.out.println("i can do something");   
    }   
  }   

Java Repeating Annotations

Before the release of JDK 8, there was not an easy way to manage repeating annotations other than the user - or the developer - providing a custom way for them to be handled. With the release of Java 8, however, programmers were allowed to define the same annotation more than once. To test this out, place the code below under the package 'jdk8.features'.

 package jdk8.features;   
  import java.lang.annotation.Repeatable;   
  import java.lang.annotation.Retention;   
  import java.lang.annotation.RetentionPolicy;   
  @Retention( RetentionPolicy.RUNTIME )   
  @Repeatable(value = jdk8_RepeatableAnnotationsSources.class)   
  public @interface jdk8_RepeatableAnnotationsSource {   
    String version() default "";      
  }   


 

Once you do this you can then define the annotation that you referenced as the value for the '@Repeatable' annotation. Place the following code under the package 'jdk8.features'.

 package jdk8.features;   
  import java.lang.annotation.Retention;   
  import java.lang.annotation.RetentionPolicy;   
  @Retention(RetentionPolicy.RUNTIME)   
  public @interface jdk8_RepeatableAnnotationsSources {   
    jdk8_RepeatableAnnotationsSource[] value();   
  }   
   

The following example demonstrate how you might use repeatable annotations. Additionally, a developer can use either of the methods getAnnotation() or getAnnotationsByType() to parse the annotations and process them.

 import jdk8.features.jdk8_RepeatableAnnotationsSource;   
  import jdk8.features.jdk8_RepeatableAnnotationsSources;   
   
  @jdk8_RepeatableAnnotationsSource(version = "jdk5")   
  @jdk8_RepeatableAnnotationsSource(version = "jdk6")   
  @jdk8_RepeatableAnnotationsSource(version = "jdk7")   
  public class jdk8_RepeatableAnnotations {   
    public static void main(String[] args) {   
       Class<jdk8_RepeatableAnnotations> clazz = jdk8_RepeatableAnnotations.class;   
       jdk8_RepeatableAnnotationsSources sources1 = clazz.   
            getAnnotation(jdk8_RepeatableAnnotationsSources.class);        
       jdk8_RepeatableAnnotationsSource[] sources2 = clazz.   
            getAnnotationsByType(jdk8_RepeatableAnnotationsSource.class);   
       for(jdk8_RepeatableAnnotationsSource source: sources1.value()) {   
         System.out.println(source.version());   
       }   
       for(jdk8_RepeatableAnnotationsSource source: sources2) {   
         System.out.println(source.version());   
       }   
    }   
  }   


Method References in Java 8

Method references are similar to lambda expressions, with the exception that method references allow a developer to specify or refer to an existing method by name. The operator used to refer to an existing method name is "::".You can place the following code under the package 'jdk8.features'.

 package jdk8.features;   
  public class jdk8_MethodReferencesVO {   
    Integer age;   
    String name;   
    public Integer getAge() {   
       return age;   
    }   
    public void setAge(Integer age) {   
       this.age = age;   
    }   
    public String getName() {   
       return name;   
    }   
    public void setName(String name) {   
       this.name = name;   
    }   
  }   


Note the usage below of method references. There are four types of references for methods and they include:

  • Reference to static methods [ContainingClass::staticMethodName]
  • Reference to instance methods [containingObject::instanceMethodName]
  • Reference to constructors [ClassName::new]
  • Reference to an instance method [ContainingType::methodName {Arbitary Object}]
 
 import java.util.Arrays;   
  import jdk8.features.jdk8_MethodReferencesVO;   
   
  public class jdk8_MethodReferences {   
   public int compareByName(jdk8_MethodReferencesVO a, jdk8_MethodReferencesVO b) {   
    return a.getName().compareTo(b.getName());   
   }   
   
   public int compareByAge(jdk8_MethodReferencesVO a, jdk8_MethodReferencesVO b) {   
    return a.getAge().compareTo(b.getAge());   
   }  
   
   public static void main(String[] args) {   
       jdk8_MethodReferencesVO[] refs = new jdk8_MethodReferencesVO[5];   
       jdk8_MethodReferencesVO vo1=new jdk8_MethodReferencesVO();   
       vo1.setAge(21);   
       vo1.setName("Frank");   
       refs[0]=vo1;   
       vo1=new jdk8_MethodReferencesVO();   
       vo1.setAge(22);   
       vo1.setName("Ibrahim");   
       refs[1]=vo1;   
       vo1=new jdk8_MethodReferencesVO();   
       vo1.setAge(24);   
       vo1.setName("Vinod");   
       refs[2]=vo1;   
       vo1=new jdk8_MethodReferencesVO();   
       vo1.setAge(19);   
       vo1.setName("Gurdeep");   
       refs[3]=vo1;   
       vo1=new jdk8_MethodReferencesVO();   
       vo1.setAge(25);   
       vo1.setName("Maxovitch");   
       refs[4]=vo1;   
       jdk8_MethodReferences refC = new jdk8_MethodReferences();   
       Arrays.sort(refs, refC::compareByAge);   
       for(jdk8_MethodReferencesVO rvo: refs) {   
         System.out.println(rvo.getAge()+":"+rvo.getName());   
       }   
    }   
  }