Java – Local variable Type Inference

Advertisements

Local variable type inference was introduced in Java 10 as a new feature of the language, and enhanced in Java 11. It basically allows the developer to use the word var instead of the type when declaring local variables (into methods), the compiler is able to infer the type from the right side of the assignment.

public String myMethod(int n){
  var counter = 10;
  var idToNameMap = new HashMap<Integer, String>();
  idToNameMap.foreach(e -> {/*some code*/});
  var message = "Hello World"; 
  message = message.toUpperCase();
  return message;
}

Not a new concept

When it comes to “Type Inference” this is not a new concept or feature in Java. For example, since Java 5 for generics methods the type was inferred based on the context. In Java 7, with collections using generics, it was not required to define the “generic” type on both sides, it was inferred by the left side of the statement.

Map<String, Long> myMap = new Hash<>();      //Type inference added in Java 7
List<Address> myAddress = new ArrayList<>(); //Type inference added in Java 7

However, “Local Type variable Inference” in Java 10 takes those advantages or capacities of the compiler a further step, providing this new feature which helps a lot on the readability and maintainability of the new code.

Important considerations

  • var only works for local variables into a method. Field members, method parameters, return types can’t be var.
public class Test {
  public var user;  //Won't work, var scope is local 
  private String fileName = "file-name.txt";
	
  public void myMethod(var myParam){//Won't work, conflicts with overloaded methods
	var n = 1L; 							//THIS IS VALID. infers Long
    var list = new ArrayList<String>();    // THIS IS VALID. infers ArrayList<String>
	var stream = list.stream();            // THIS IS VALID. infers Stream<String>
	var path = Paths.get(fileName);        // THIS IS VALID. infers Path
	var bytes = Files.readAllBytes(path);  // THIS IS VALID. infers bytes[]
  }
  
  public var myOtherMethod(){//Won't work, Return type of the method can not be var.
	var n = 1L; //THIS IS VALID
    return n;
  }
  
  public Long myLastMethod(){//Will work, return type is Long (not var)
	var n = 1L;
    return n;
  }
}
  • var won’t work if you don’t initialize the local variable or initialize it with null. Local variable should be initialized at time of declaration otherwise compiler will not be infer and will throw error.
public void myMethod(){
  var n; 			// Won't work, not able to infer the type
  var t = null 	// Won't work, not able to infer the type
}
  • Local variable inference type is available inside initialization block of loop statements, try-with-resources and lambda expressions.
List<String> myList = Arrays.asList("a", "b", "c");
for (var element : myList) {...}  // infers String

try (var input = new FileInputStream("validation.txt")) {...} 

(var a, var b) -> a + b;  //Valid since Java 11.
  • No dynamic type change. Once type of local variable is inferred it cannot be changed.
public void myMethod(){
  var myHello = "World"; //Type is inferred as String
  myHello = 123L;        //Won't work. Not possible to assign another type value
}
  • No runtime overhead. As compiler infers the type based on value provided, there is no performance loss.

Recommendations

Local variable declarations can make code more readable by eliminating redundant information. However, it can also make code less readable by omitting useful information. Consequently, use this feature with judgment; no strict rule exists about when it should and shouldn’t be used.

Oracle, Java 13 documentation

As a good practice, when declaring variables with var , it’s recommended to add meaningful name to that variable, which helps with the readability, and maintainability of the fragment of code.

public void myMethod(){
  var n = "A string value"; 		//This is not a meaningful name 
  var message = "A String value";  	//This is a meaningful name
  
  var path = Paths.get(fileName);   //This a meaningful name 
  var p2 = Paths.get(fileName2);    //This is not a meaningful name
}

The word var is not a keyword in Java. Because of backward compatibility to existent code with variables, methods, classes named var , it still can be used as name of methods or variables. However, the compiler is able to differentiate when var is used the type or the name of the variable. Therefore, as recommendation, let’s avoid naming variables with as var, it’s valid but it can confuse other developers.

public void method(){
   var var = "String value";  //This is valid. But let's avoid it 
}

public void method2(){
  String var = "Another String"; //This is still valid. Using var as name. But lets avoid it
}

Conclusions

Local type inference is a great feature allowing that complex boilerplate code can be reduced. For sure, there are developers that will prefer using the classic type definition on most of the scenarios, but using wisely can improve a lot the readability and maintainability of our code.

Developer from other languages (Kotlin, Python, JavaScript) usually complaints avoid verbosity of Java, this is a good way to reduce that verbosity, specially when working generics collections.

References

Advertisements

Leave a Reply

Your email address will not be published. Required fields are marked *