Local Variable Type Inference in Java 10
Type Inference is the ability of a programming language to automatically detect the data type of an expression or variable declarations. Type inference is a common concept you would find in modern programming languages. It is often a characteristic of functional programming languages.
In Java type inference was first introduced through generics type inference in Java 7. This refers to the ability to understand the diamond operator <>. E.g:
List<Stirng> list = new ArrayList<>();
Here the diamond operator <> is inferred as String.
Local Variable Type Inference
Java 10 has enhanced the language specification by introducing ‘local variable type inference’. Java traditionally has a problem with a higher degree of boilerplate coding that is required as compared to other languages. The motivation behind the enhancements in the type inference is to improve the language to reduce these boilerplate coding to some extent.
A simple example
For example, prior to Java 10, local variables are initialized as shown below.
//String variable String name = "Janeve George"; // Initializing object StringBuilder stringBuilder = new StringBuilder(); DateFormat dateFormat = new SimpleDateFormat("dd/MM/yy"); //Initialization with Generic types List<String> list = new ArrayList<>();
The issue with these statements is that the left-hand side seems to be redundant. The right-hand side of the expression makes it clear what type we are expecting. The local variable type inference in Java 10 helps the programmer not to worry too much on the left-hand side of the expression. The above code can be written with local variable type inference using the var keyword.
//String variable var name = "Janeve George"; // Initializing object var stringBuilder = new StringBuilder(); var dateFormat = new SimpleDateFormat("dd/MM/yy"); //Initialization with Generic types var list = new ArrayList<String>();
A more complex example
Let us take a more complex scenario so that we can appreciate the benefits of the local variable type inference. Let us consider an example class ComplexExampleBeforeJava10:
public class ComplexExampleBeforeJava10 { static final Random randomGenerator = new Random(); static final NumberFormat numberFormatter = NumberFormat.getIntegerInstance(); public static void main(String[] args) { List<HashMap<String, Integer>> list = new ArrayList<>(); for (int i = 1; i <= 50; i++) { list.add(getItem(i)); } Stream<HashMap<String, Integer>> stream = list.stream(); int length = list.size(); List<HashMap<String, Integer>> subList = list.subList(10, 20); HashMap<String, Integer> map = list.get(10); Set<String> keys = map.keySet(); // Usages of local variables. Integer wrapperInteger = length; stream.forEach(System.out::println); int subListSize = subList.size(); int noOfKeys = map.size(); keys.add("New String"); } private static HashMap<String, Integer> getItem(int numOfElements) { HashMap<String, Integer> map = new HashMap<>(numOfElements); for (int i = 0; i < numOfElements; i++) { int number = randomGenerator.nextInt(); map.put(numberFormatter.format(number), number); } return map; } }
Observe the highlighted lines 13-17. The developer will have to make more effort in writing this section. The developer will have to manually evaluate the expression while coding and declaring the local variable with an appropriate type.
The Local Variable Type Inferrence capability in Java 10 improves readability and productivity. The above code can be written as follows using the local variable type inference.
public class ComplexExampleJava10 { static final Random randomGenerator = new Random(); static final NumberFormat numberFormatter = NumberFormat.getIntegerInstance(); public static void main(String[] args) { var list = new ArrayList<HashMap<String, Integer>>(); // inferred as ArrayList<HashMap<String, Integer>> for (int i = 1; i <= 50; i++) { list.add(getItem(i)); } // Benefits of Local Variables Type Inference show below var stream = list.stream(); // inferred as Stream<HashMap<String, Integer>> var length = list.size(); // inferred as int var subList = list.subList(10, 20); // inferred as List<HashMap<String, Integer>> var map = list.get(10); // inferred as HashMap<String, Integer> var keys = map.keySet(); // inferred as Set<String> // Usages of local variables. Integer wrapperInteger = length; // length is considered as int stream.forEach(System.out::println); // forEach of Stream<HashMap<String, Integer>> invoked int subListSize = subList.size(); // size of List<HashMap<String, Integer>> invoked int noOfKeys = map.size(); // size of HashMap<String, Integer> invoked keys.add("New String"); // add of Set<String> invoked } private static HashMap<String, Integer> getItem(int numOfElements) { HashMap<String, Integer> map = new HashMap<>(numOfElements); for (int i = 0; i < numOfElements; i++) { int number = randomGenerator.nextInt(); map.put(numberFormatter.format(number), number); } return map; } }
Rules for using the ‘var’ keyword
There are some valid and invalid ways of using the var keyword and local variable type inference. The rules for using the var keyword are as follows:
- It can be used within static and instance blocks within a class.
- It can be used for a local variable initialization.
- It can be used for declaring the index variable in the for-loop.
- It can be used for declaring the iteration variable in the enhanced for-loop.
- It cannot be used for declaring class properties.
- It cannot be used for local variables without initialization.
- It cannot be used for method parameter variables.
- It cannot be used for a method return type.
- It cannot be used for local variables which are initialized with null in the right-hand side of the expression.
- It cannot be used with an array initializer without any explicit target-types.
- It cannot be used for lambda expressions without any explicit target-types.
public class InvalidUseOfVarKeyword { // INVALID - It cannot be used for declaring class properties. var classProperty; // INVALID - It cannot be used for declaring class properties. var dateProperty = new Date(); public void aMethod(){ // INVALID - It cannot be used for local variables without initialization. var localVariable; // INVALID - It cannot be used for local variables which are initialized with null. var nullVariable = null; // INVALID - It cannot be used with an array initializer without explicit target-types. var arrayVariable = {1, 2, 3}; // The code below is valid since it has an explicit target-type int[] var intArrayVariable = new int[]{1, 2, 3}; } // INVALID - It cannot be used for method parameter variables. public void aMethodWithParameters (String param1, var param2) { } // INVALID - It cannot be used for a method return type. public var aMethodWithReturnType{ return "Hello"; } }
You can find all the source code in my github repository.