1

本文主要解读下java10的Local-Variable Type Inference

实例

    @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只是为了简化代码,注意var只能用于局部变量。它不能用于类成员变量,方法参数等。

Style Guidelines

引入var是一把双刃剑,一方面简化了代码,但是同时可能影响了可读性,特别是那些你不熟悉的类型。为此Stuart W. Marks给出了一份使用指南Style Guidelines for Local Variable Type Inference in Java。其主要观点如下:

主要原则

  • 阅读代码比编写代码更重要
  • 使用var应当让读者能够清楚推断出类型
  • 代码可读性不应该依赖于IDE
  • 显式类型是一种折衷,虽然有时候冗长,但是类型清晰

var使用指南

  • 变量名称要提供有用信息
// ORIGINAL
List<Customer> x = dbconn.executeQuery(query);
// GOOD
var custList = dbconn.executeQuery(query);
  • 尽量减少局部变量的范围
var items = new HashSet<Item>(...);

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

items.add(MUST_BE_PROCESSED_LAST);
for (var item : items) ...
var的声明与使用距离太远,不容易看清楚items的类型
  • 考虑var初始化时向读者提供足够的信息
// ORIGINAL
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();

// GOOD
var outputStream = new ByteArrayOutputStream();
比如右侧是显示类型,非常清晰
  • 用于使用var局部变量分解链接或嵌套的表达式
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);

可以用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);
  • 对于钻石语法及泛型要小心
// 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);
  • 使用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;

小结

var是一把双刃剑,一方面可以简化繁琐的代码,但是使用不恰当又会影响代码可读性,需要谨慎使用。

doc


codecraft
11.9k 声望2k 粉丝

当一个代码的工匠回首往事时,不因虚度年华而悔恨,也不因碌碌无为而羞愧,这样,当他老的时候,可以很自豪告诉世人,我曾经将代码注入生命去打造互联网的浪潮之巅,那是个很疯狂的时代,我在一波波的浪潮上留下...


引用和评论

0 条评论