dcsimg

Java Core 9.0 Code Examples

Are you trying to recollect or revise Java SE 9 programming constructs? Are you preparing for a job interview? Do you want to just revisit Java SE 9 features? In this article we will take you back in time to the features that were introduced in Java SE 9. Join us for this series on Java 9 as the world eagerly awaits the official release of Java SE 17!

As promised, I am back with the code samples of my earlier article on Java SE 9. You may want to refer to the earlier article to better understand the newly introduced features at a higher level. This article provides the code samples for each of the features we highlighted previously. 

Java Default Interface Methods Example

An amazing new feature of Java SE 8 was the introduction of Default Interface Methods. Especially useful for Enterprise development or any piece of software that needed to easily achieve backward compatibility while allowing new method additions or the updating of API contracts.

Sometimes in legacy code that relied on interfaces that required the addition of new methods, existing code that inherits from these interfaces would break. Adding new methods would require developers to provide the implementation for each of these newly added methods in the implementing classes. This created maintenance issues.

To handle scenarios for backward compatibility while introducing newer interface methods, programmers needed to handle and solve the problem mentioned above. This is where Default Interface Methods come in. Observe the following Java code showing the implementation of default interface methods:

import java.util.List;  

interface LegacyBusinessAPI {  

    /**  
    * Additional Default Method that Can be Invoked on Each Type of Invoice that Implements the LegacyBusinessAPI.   
    * It can be over-ridden by the Implementing Invoice Types. This is an Example Usage/Benefit of the Java/JDK 8  
    * Default Interface Feature.  
    *
    * @param items  
    */  
    default void checkStatusOnEachInvoice (List invoiceItems) {  
    String invoiceItem = null;
        for(int i=0; i<invoiceItems.size(); i++) {
            invoiceItem = invoiceItems.get(i);   

            // null check is not done - always a value exists!
            if(invoiceItem.startsWith("OLD_")) {  
                invoiceItems.remove(invoiceItem);  
            }  
        }  
        return;  
    }  

} 


In the above example, the LegacyBusinessAPI shows an example of an interface in an existing application. The interface is already extended by multiple invoice types (for example, a proprietory inventory system). Re-engineering efforts require that each of the invoices have a method to remove or auto-expire an item marked as "OLD".

Prior to Java 8, this type problem would require introduction of a new method declaration in the interface. It would further require each of the implementing classes implement their own logic for handling this, equating to a lot of time, effort, code changes and being error prone.

With the introduction of default interfaces, this task becomes very simple (code is now more maintainable, extensible, and requires less effort for future changes). Let us now look at the novel possibilities with the introduction of the feature of Default Methods, which include:

No breakage of existing functionality by using the default methods. Implementing Classes may decide to Override default methods. Abstract Classes can be used to group similar implementations.

Java Language Changes in Java SE 9

Private Interface Methods

Starting in Java 9, interfaces were allowed to have private methods. This allows code sharing between non-abstract methods in the interface. All rules related to ordinary private modifiers are applicable to these methods. It should be obvious that a method cannot be both private and abstract. Also, a private interface method definitely needs to have a method body. Here is an example in code:

  package com.techilashots.java9.features;   
    
 /**   
  * @author sumith.puri   
  *    
  * prior to java 8, the default scope/visibilty of every method in an interface was same   
  * the attributes were all by default, [public static final] and the methods were [public]   
  * with java 8, [default] interface methods have been introduced but so has been a new scope/visibility   
  * with java 9, interface can have private visibility methods that can be used internally    
  */   
  public interface Java9PrivateMethodInterface {   
   
    public void preJava9BusinessMethodOne();   
   
    public void preJava9BusinessMethodTwo();   
   
    public default void newJava9BusinessMethodThree() {   
       System.out.println("Interface Methods can call Private Interface Methods!");   
       System.out.println("");   
       java9BusinessMethodThreeHelper();   
    }   
   
    private void java9BusinessMethodThreeHelper() {   
       System.out.println("Default Methods... Now Private Methods!");   
       System.out.println("Once Upon a Time, I was a Java Developer!");   
    }   
  }   


You can observe the usage of the above private method (internally by the new default method) through the following code:

    //new features of java 9   
    //private interface methods   
    private void privateInterfaceMethod() {   
   
       System.out.println("01. Private Interface Methods");   
       System.out.println("-----------------------------");   
       System.out.println("[Private Interface Method Invoked by Default Interface Method]");   
       Java9PrivateMethodInterface java9PrivateMethodInterface = new Java9PrivateMethodInterface() {   
         @Override   
         public void preJava9BusinessMethodTwo() {   
            // TODO Auto-generated method stub   
         }   
         @Override   
         public void preJava9BusinessMethodOne() {   
            // TODO Auto-generated method stub   
         }   
       };   
   
       java9PrivateMethodInterface.newJava9BusinessMethodThree();   
       System.out.println("");   
       System.out.println("================================");   
       System.out.println("");   
    }   


 

Underscore as A Variable Name Is No Longer Legal

The _ (underscore character) as a variable name became illegal starting with Java SE 9. It was already marked as a reserved keyword starting in 1.8. Using it will cause a compilation error.

Please take note that this may cause some issues when compiling legacy source code. Usually in code that was system-oriented, developers had to denote specific resources or entities using the _ (underscore). Code using this technique may have to be rewritten and may have multiple related ramifications. Consider the following:

  /**   
  * @author sumith.puri   
  *   
  */   
  public class Java9VariableNaming {   
    // pre-java 9, this was a valid variable name   
    // from java 8, _ was marked as a reserved keyword (compiler warning)   
    // from java 9, the following line of code will cause compilation failure   
    // private String _;   
    private String _socket = null;   
    /**   
     * @param args   
     */   
    public void notForDemoMethod() {   
       _socket = new String("Network Socket");   
       System.out.println("_ is no Longer a Valid Variable Name!, [Variable Value: " + _socket + "]");        
    }   
  }   

Variables and Try-with Resources

Before Java 9, every variable that had to be used within Try-with-Resources statements needed to be declared within the try statement. Only then could it be used within the try block. This was a limitation for the developer. From Java 9 onwards, this restriction has been removed and any final variable or any effectively final local variables can be used inside the try block for the purpose of a resource. All other previous rules as applicable to Try-with-Resources remain the same. Final refers to variables that are not changed even once after they have been initialized.

  package com.techilashots.java9.features;�  
   
  import java.io.BufferedReader;   
  import java.io.File;   
  import java.io.FileReader;   
  import java.io.IOException;   
    
  /**   
  * @author sumith.puri   
  */   
  public class EffectivelyFinalTryResources {   
    
    private static File file = new File("try_resources.j9");    
   
    //with java 9, you need to use either explicitly final or effectively final variables in try/resources   
    public void methodWithAnomaly() throws IOException {   
   
       file.createNewFile();   
       BufferedReader bufferedReader = new BufferedReader(new FileReader(file));   
       
       //prior to java 9, the usage would look like this   
       //try (final BufferedReader bufferedReader = new BufferedReader(new FileReader(file))) {   
       //this code will not compile before java 9   
       try (bufferedReader) {        
         System.out.println("Can Use Final or Effectively Final in Try with Resources!");   
       } finally {   
         System.out.println("Java 9 Gives More Flexibility to Developers.");   
       }        
    }   
  }   


@SafeVarargs is Allowed on Private Instance Methods

Using non-reifiable variable argument parameters in a method can cause multiple warnings when trying to compile code, such as:

 Note:�LegacyPublicInterface.java uses unchecked or unsafe operations. Note: Recompile with -Xlint:unchecked for details.  

@SafeVarargs was introduced to suppress such warnings on unchecked or unsafe operations. By using this, the developer is signalling to the compiler that he has made sure that there will be no heap pollution (such as unsafe forEach operations) caused.

Prior to Java 9, @SafeVarargs is allowed on non-overridable methods such as in static methods, final instance methods and constructors. Note that the annotation will throw an error if it is used in fixed arity methods. In Java 9, @SafeVarargscan be used on private instance methods.

  package com.techilashots.java9.features;   
   
  /**   
  * @author sumith.puri   
  */   
  public class SafeVarargsOnPrivateMethod {   
   
    public void demoSafeVarargsInJava9() {   
       safeVarargsInJava9(24.00f, 03, 19, 82);   
    }   
   
    @SafeVarargs   
    private void safeVarargsInJava9 (Float a, Integer...b) {   
   
       System.out.println("Invoked Private Instance Method with " +a+ "," +b[0]+ "," +b[1]+ "," +b[2]);   
       System.out.println("With Java 9, @SafeVarargs is Allowed on Private Instance Methods!");   
    }   
  }   

Diamond with Anonymous Classes

Prior to Java 9, using generics and diamond operators was not allowed with anonymous classes. It was primarily because the compiler could not infer whether it can represent the type in the argument passed to the diamond operator.

Beginning with Java 9, the Java compiler has changed its inference algorithm in such a way that diamond operators can now work simultaneously with anonymous classes, as long as the argument type of the inferred type is denotable. The Important point to note here is that things that fall under denotable include primitive types, raw types, and non-generic types. Non-Denotable refers to ones that cannot be written in a Java Program, like usage of extends and super along with wildcard types in generics. These are usually inferred by the compiler. So, as long as the compiler identifies that the argument type of the inferred type is denotable, you can use the diamond operator in conjunction with anonymous inner classes.

 package com.techilashots.java9.features;� �  
   
 /** * @author sumith.puri */   
 public class DiamondOperatorOnAnonymous {� �  
    
  public void diamondForInferredDenotable() {� �  
     
   //prior to java 9, anonymous inner classes were not allowed to use diamond operator   
     
   Java5ToJava8Coder<? extends Number> java9SyntaxGuru = new Java5ToJava8Coder<>() {   
   @Override   
   public void java9SyntaxMagic() {   
     System.out.println("Introduced in Java 5, Generics was Intriguing and Complex!");   
     System.out.println("With Java 9, Diamond Operator On Anonymous Classes is Allowed...");   
     System.out.println("As Long as Java 9 Infers the Type (Inferred) to be Denotable.");   
    }   
   };   
   java9SyntaxGuru.java9SyntaxMagic();   
  }   
 }� �  
   
 abstract class Java5ToJava8Coder {   
  public abstract void java9SyntaxMagic();   
 }  

Core Library Changes in Java SE 9

With Java 9, one can retrieve the PID of the process through a native call. This is achievable through the ProcessHandle . Also, we can retrieve information about the currently running Java process and info (inner class of ProcessHandle) class/object that contains details on the process. We can also enlist or return a snapshot of all currently running processes in the system:

  package com.techilashots.java9.features;   
  import java.lang.ProcessHandle.Info;   
   
  /**   
  * @author sumith.puri   
  */   
  public class ProcessAPIChanges {   
      
    public void detailedAPIInfo(ProcessHandle processHandle) {   
         
       Info processInfo = processHandle.info();   
       System.out.println("[Java 9 Developers... Check this Out!]");   
       System.out.println("[Detailed Process Info is Provided Below]");   
       System.out.println("[Executable Name] " + processInfo.command().get());   
       System.out.println("[User Name] " + processInfo.user().get());   
       System.out.println("[Start Time] " + processInfo.startInstant().get().toString());   
       System.out.println("+++++");   
    }  
    
    public static void main(String[] args) {   
         
       System.out.println("06. Process API Changes (Core Library) ");   
       System.out.println("--------------------------------------");   
       System.out.println("Check Out the Detailed Process Information in Java 9");   
         
       ProcessAPIChanges processAPIChanges = new ProcessAPIChanges();   
       ProcessHandle processHandle = ProcessHandle.current();   
         
       System.out.println("With Java 9, Process Id is Available");   
       System.out.println("[Current Process Id] " + processHandle.pid());   
       System.out.println("-------------------------------------");   
         
       processAPIChanges.detailedAPIInfo(processHandle);   
         
       System.out.println("-------------------------------------");   
       System.out.println("With Java, You can View all Processes..");   
       System.out.println("That are Visible to the Current Process!");   
         
       ProcessHandle.allProcesses()   
       .filter(ph -> ph.info().command().isPresent())   
       .limit(4)   
       .forEach((process) -> processAPIChanges.detailedAPIInfo(process));   
         
       System.out.println("");   
       System.out.println("================================");   
       System.out.println("");   
    }   
  }   

Enhanced Deprecation in Java

With an eye on maintainable and more informative code, the developer-defined deprecation now allows us to mark deprecation with additional elements of information like forRemoval and since. The forRemoval allows allows developers to mark that this item may be removed in future versions of Java and hence provides information about when it was first introduced:

 package com.techilashots.java9.features;   
    
  /**   
  * @author sumith.puri   
  */   
  public class EnhancedDeprecation {   
      
    @Deprecated(forRemoval=true)   
    public void methodMarkedForRemoval() {   
       System.out.println("Java 9 Allows Enhanced Method Deprecation");   
       System.out.println("Invoked Method is Deprecated and Marked [For Removal]");   
       this.methodDeprecatedSince();   
    }   
      
    @Deprecated(since="12.2")   
    public void methodDeprecatedSince() {   
       System.out.println("Invoked Method is Deprecated and Marked [Since]");   
    }   
  }   

Convenience Factory Methods for Collections

This addition makes it convenient for the developer to create immutable collections out of existing interfaces, be it set, map or list. A static factory method of () added to set, map or list. It is important you understand the following:

They are structurally immutable. They disallow null elements or null keys. They are serializable if all elements are serializable. They reject duplicate elements/keys at creation time. The iteration order of set elements is unspecified and is subject to change. They are value-based. Factories are free to create new instances or reuse existing ones. Therefore, identity-sensitive operations on these instances (reference equality (==), identity hash code, and synchronization) are unreliable and should be avoided. They are serialized as specified on the serialized form page.

  package com.techilashots.java9.features;   
   
  import java.util.Set;   
  /**   
  * @author sumith.puri   
  */   
  public class ConvenienceCollections {   
    public void checkItOut() {   
       System.out.println("Java 9 Introduced a Static [of()] Factory Method");   
       System.out.println("This allows Creation of Immutable Collections");   
       Set immutableCountrySet = Set.of("America", "Russia", "China", "India");   
       try {   
         immutableCountrySet.add("England");   
       } catch (Exception e) {   
         System.out.println("Caught Exception, Adding Entry to Immutable Collection!");    
       }   
    }    
  }