Java10 series (ii) Local-Variable Type Inference

  java10

Order

This article mainly interprets the Local-Variable Type Inference of java10.

Example

    @Test
    public void testVar(){
        var list = List.of(1,2,3,4,5);
        var strList = list.stream()
                .map(e -> "hello" + e)
                .collect(Collectors.toList());
        var result = strList.stream()
                .collect(Collectors.joining(","));
        System.out.println(result);
    }

    @Test
    public void testVarInForEach(){
        var data = Map.of("k1",1,"k2",2,"k3",3,"k4",4,"k5",5);
        for(var entry : data.entrySet()){
            System.out.println(entry.getKey() + ":" + entry.getValue());
        }
    }

    @Test
    public void testVarInTry() throws IOException {
        try(var input = this.getClass().getClassLoader().getResourceAsStream("demo.txt")) {
            int data = input.read();
            while(data != -1){
                System.out.print((char) data);
                data = input.read();
            }
        }
    }

Var was introduced only to simplify the code. Note that var can only be used for local variables. It cannot be used for class member variables, method parameters, etc.

Style Guidelines

The introduction of var is a double-edged sword. on the one hand, it simplifies the code, but at the same time it may affect the readability, especially those types you are not familiar with. For this reason Stuart W. Marks gave a guide to useStyle Guidelines for Local Variable Type Inference in Java. The main points are as follows:

Main principles

  • Reading code is more important than writing code.
  • Using var should enable readers to clearly infer the type
  • Code readability should not depend on IDE
  • Explicit type is a compromise, although sometimes lengthy, but the type is clear

Var usage guide

  • Variable names should provide useful information
// ORIGINAL
List<Customer> x = dbconn.executeQuery(query);
// GOOD
var custList = dbconn.executeQuery(query);
  • Minimize the scope of local variables
var items = new HashSet<Item>(...);

// ... 100 lines of code ...

items.add(MUST_BE_PROCESSED_LAST);
for (var item : items) ...

Var’s declaration is too far from being used, and it is not easy to see the type of items clearly.

  • Consider providing sufficient information to readers when var initialization
// ORIGINAL
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();

// GOOD
var outputStream = new ByteArrayOutputStream();

For example, on the right is the display type, which is very clear.

  • Expressions for decomposing links or nesting using var local variables
Map<String, Long> freqMap = strings.stream()
                                   .collect(groupingBy(s -> s, counting()));
Optional<Map.Entry<String, Long>> maxEntryOpt = freqMap.entrySet()
                                                       .stream()
                                                       .max(Map.Entry.comparingByValue());
return maxEntryOpt.map(Map.Entry::getKey);

You can declare intermediate variables with var

var freqMap = strings.stream()
                     .collect(groupingBy(s -> s, counting()));
var maxEntryOpt = freqMap.entrySet()
                         .stream()
                         .max(Map.Entry.comparingByValue());
return maxEntryOpt.map(Map.Entry::getKey);
  • Be careful with diamond syntax and generics
// OK: both declare variables of type PriorityQueue<Item>
PriorityQueue<Item> itemQueue = new PriorityQueue<>();
var itemQueue = new PriorityQueue<Item>();

// DANGEROUS: infers as PriorityQueue<Object>
var itemQueue = new PriorityQueue<>();

// DANGEROUS: infers as List<Object>
var list = List.of();

// OK: itemQueue infers as PriorityQueue<String>
Comparator<String> comp = ... ;
var itemQueue = new PriorityQueue<>(comp);

// OK: infers as List<BigInteger>
var list = List.of(BigInteger.ZERO);
  • Be careful when declaring strings/numbers using var
// ORIGINAL
boolean ready = true;
char ch = '\ufffd';
long sum = 0L;
String label = "wombat";

// GOOD
var ready = true;
var ch    = '\ufffd';
var sum   = 0L;
var label = "wombat";

// ORIGINAL
byte flags = 0;
short mask = 0x7fff;
long base = 17;

// DANGEROUS: all infer as int
var flags = 0;
var mask = 0x7fff;
var base = 17;

// ORIGINAL
float f = 1.0f;
double d = 2.0;

// GOOD
var f = 1.0f;
var d = 2.0;

Summary

Var is a double-edged sword. On the one hand, it can simplify complicated codes. However, improper use will affect the readability of codes, which requires careful use.

doc