java

关注公众号 jb51net

关闭
首页 > 软件编程 > java > lombok的val和var到JDK的var关键字

从lombok的val和var到JDK的var关键字方式

作者:初心绘流年

这篇文章主要介绍了从lombok的val和var到JDK的var关键字方式,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教

前言

近期因项目中的开源框架版本升级导致项目的整体jdk版本被迫从万年的java8升级到了java11

于是我们也从该开源框架中看到了对于我来说一个比较陌生的身影—val,于是便开始一小波的学习

在这里插入图片描述

一、Lombok中的val和var

1.1 lombok.val

首先看官方文档介绍:

You can use val as the type of a local variable declaration instead of actually writing the type. When you do this, the type will be inferred from the initializer expression. The local variable will also be made final. This feature works on local variables and on foreach loops only, not on fields. The initializer expression is required.

val is actually a ‘type’ of sorts, and exists as a real class in the lombok package. You must import it for val to work (or use lombok.val as the type). The existence of this type on a local variable declaration triggers both the adding of the final keyword as well as copying the type of the initializing expression which overwrites the ‘fake’ val type.

WARNING: This feature does not currently work in NetBeans.

大致的意思就是val是用于声明局部变量的,它会根据初始化的表达式来对变量类型进行推断。使用val的局部变量会被声明为final的,它可以使用与foreach的循环中,val不可以用于成员变量,并且它的初始化表达式是必须的(即必须在声明的时候给出初始化的表达式)。

使用示例:

public static void main(String[] args){
    val map = new HashMap<>();
    map.put("name","dachengcheng");
    map.put("gender","boy");
    map.put("obj", new Object());

    for (val entry:map.entrySet()){
        System.out.println(entry.getKey()+":"+entry.getValue());
    }
}

其实val就是一种类型推断的语法糖,在编译之后会进行desugar的,我们来看一下以上代码经过编译之后变成什么样子

// 以下是JDK11反编译出来的
public static void main(String[] args) {
    HashMap<Object, Object> map = new HashMap();
    map.put("name", "dachengcheng");
    map.put("gender", "boy");
    map.put("obj", new Object());
    Iterator var2 = map.entrySet().iterator();

    while(var2.hasNext()) {
        Entry<Object, Object> entry = (Entry)var2.next();
        PrintStream var10000 = System.out;
        ((PrintStream)entry.getKey()).println((String)entry.getValue());
    }

}

因为我们在泛型中并未指定具体的类型,所以类型推断出来的就是HashMap<Object,Object>的类型了,我们可以看到在循环中,也是很正确的推断出了Entry类型。

上面不是提到val代表的是final类型的局部变量吗?为啥反编译出来的没有看到final的字样?

这一点其实我也很疑惑,不知道是不是因为IDEA的工具反编译问题,或者是因为Java8以上的Effective final的特性导致的没有final修饰,但实际是final类型呢?

我们降低一下jdk版本(JDK6)试了一下,发现结果仍然一样,资料找了大半圈没找到一个人有合适的说法。于是我试了一下直接用final关键字修饰局部变量

在这里插入图片描述

在这里插入图片描述

才发现是局部变量的final并没有写入class文件中,至于为什么,大概是优化吧,具体的可能得去看看虚拟机相关的知识了,此处暂且不深入了。

局部变量以及参数中的final,同样不能提升我们的性能,它甚至不会被写进字节码中

1.2 lombok.var

还是来看官方文档的介绍

var works exactly like val, except the local variable is not marked as final.

The type is still entirely derived from the mandatory initializer expression, and any further assignments, while now legal (because the variable is no longer final), aren’t looked at to determine the appropriate type.For example, var x = "Hello"; x = Color.RED; does not work; the type of x will be inferred to be java.lang.String and thus, the x = Color.RED assignment will fail. If the type of x was inferred to be java.lang.Object this code would have compiled, but that’s not howvar works.

大致意思就是varval很像,只不过它标记的局部变量并不是final的。

二、Java中的var关键字

2.1 如何使用?

var关键字其实作用和上面提到的lombok.vallombok.var是一样的,也是用于局部变量的类型推断;var修饰的变量是非final的,如果需要final可以加上final修饰符。

还是一样我们来看一下源码和编译之后的class文件内容

源码

public class ValDemo {
    final String bbb ="111";

    public static void main(String[] args){
        var map = new HashMap<String,String>();
        map.put("aaa","dachengcheng");
        var obj = getObj();
    }
    private static List<List<Map<String, List<Map<String,String>>>>> getObj(){
        // 当然现实当中这种返回类型容易被打死,此处只是为了效果演示
        return null;
    }
}

反编译

public class ValDemo {
    final String bbb = "111";

    public ValDemo() {
    }
    public static void main(String[] args) {
        HashMap<String, String> map = new HashMap();
        map.put("aaa", "dachengcheng");
        List<List<Map<String, List<Map<String, String>>>>> obj = getObj();
    }
    private static List<List<Map<String, List<Map<String, String>>>>> getObj() {
        return null;
    }
}

2.2 为什么推出var关键字?

Java作为一个强类型的静态类型语言,为啥要推出这么一个关键字呢?

我们知道在JavaScript这种弱类型的语言中有var关键字,我想大概是因为“偷懒”吧。

我们知道Java代码存在很多模版化的声明,如HasMap<String,String> map = new HashMap<String,String>(), 这种写法虽然给我们带来了好处的同时,也带来了一些书写上的繁琐,有些人可能觉得花时间在书写这种模板代码上是在浪费时间,为啥Java就不能帮我们完成这个事呢? 比如上面提到的List<List<Map<String, List<Map<String,String>>>>>返回类型,真正要写的那可是一长串,有了var关键字之后就简单多了,直接var obj = getObj();,在这种情况下相对来说代码更简单,至于易读性是相对的,有的时候提高了可读性,有的时候反而不方便。

当然官方对于这个关键字的由来,大概是因为关于 2016 年有一个 JDK 增强提议(JEP)在 Java 社区引起了轰动:JEP 286,感兴趣的可以去了解一下。

三、该如何抉择呢?

其实该使用谁那完全看个人喜好了,但是呢我们必须得了解清楚他们之间的区别,比如lombok.val在书写的时候,IDE可能还无法识别它的final特性,可以针对该局部变量进行修改,只会在编译或运行的时候发现报错。

而且有的团队可能会比较抵制lombok,理由大概是一人用lombok,所有人都得用,在某种程度上属于强买强卖。

所以如果在JDK版本允许的情况下(JDK10及以上),我们可以考虑用jdk自带的var关键字。

如果是万年java8的话,又想使用这种语法特性,那么就可以使用lombok.val了。

Reference:

总结

以上为个人经验,希望能给大家一个参考,也希望大家多多支持脚本之家。

您可能感兴趣的文章:
阅读全文