京东6.18大促主会场领京享红包更优惠

 找回密码
 立即注册

QQ登录

只需一步,快速开始

你应该更新的 Java 知识:var

2026-2-8 20:27| 发布者: 上位机李工| 查看: 91| 评论: 0

摘要: 这一篇,我们从编程中最简单的概念说起:变量声明。unsetunsetJava 变量声明的演化unsetunset如何在 Java 里声明一个键和值都是 String 类型的 map?如果是 Java 5之前,你可能会这么做:Map map = new HashMap();这

这一篇,我们从编程中最简单的概念说起:变量声明。

unsetunsetJava 变量声明的演化unsetunset

如何在 Java 里声明一个键和值都是 String 类型的 map?如果是 Java 5之前,你可能会这么做:

Map map = new HashMap();

这里我们声明了一个 map,虽然我明确地说明了键和值的类型,但在这个声明里,我们根本看不到这方面的信息。在这种声明下,map 里存放的键和值都是 Object 类型,也就是任何类型。要保证键和值都是 String,我们只能自己去做相应的转换,尤其是在取出的时候:

map.put("Hello", "World");String value = (String)map.get("Hello");

Java 5 引入了泛型,代码变成了这样:

Map<String, String> map = new HashMap<String, String>();

这里出现了键和值的类型,我们从 map 中取出数据的时候,就不再需要转换了:

map.put("Hello", "World");String value = map.get("Hello");

Java 5 虽然解决了类型信息的问题,但是,这个声明变得很长,尤其是还存在重复信息,比如,前面 Map 类型里已经有了键和值的类型,后面在初始化 HashMap 时,还要把类型再写一遍,这就是一种重复。于是,Java 7 引入了钻石操作符(<>)简化变量的声明:

Map<String, String> map = new HashMap<>();

钻石操作符之所以可行,因为键和值的类型信息已经在前面声明过了,编译器可以根据前面的 Map 声明“推断”出后面 HashMap 键和值的类型。

其实,无论是泛型还是钻石表达式,它们对 Java 的运行时都没有什么影响,主要的工作都是在编译过程中完成。编译器知道很多上下文信息,利用这些信息,它可以“推断”出很多事情,就像前面说的钻石操作符一样。那还能不能再进一步呢?答案是肯定的,Java 10 推出了 var。

unsetunset局部变量类型推断:varunsetunset

我们先来看一个例子:

var value = 1;

在这个例子里,把 1 赋值给 value。在这里,我们没有声明 value 的类型,但是,我们知道,在 Java 中,字面量 1 是一个 int。我们把一个 int 赋值给 value,那 value 应该是什么类型呢?它当然应该也是一个 int。所以,虽然我们没有声明 value 的类型,但是,编译器已经“推断”出了它的类型。

或许你没觉得这里 var 比 int 节省了什么,那我们回到前面的 map 声明:

var map = new HashMap<String, String>();

按照前面讲的逻辑,编译器很容易“推断”出 map 的类型是 HashMap<String, String>,这里的重点是,我不再需要手工声明这个 map 的类型,取而代之的是 var。

或许,这个简单的例子你觉得也没什么,但在真实的项目中,很多代码会出现层层嵌套的情况,比如,下面就是一个 Map 套 Map 的情况:

Map<String, Map<String, String>> map = new HashMap<String, HashMap<String, String>>();

用 var 就可以简化一些:

var map = new HashMap<String, HashMap<String, String>>();

下面这段代码使用 var,优势就更明显了:

for (var entry : map.entrySet()) {    var key = entry.getKey();    var value = entry.getValue();    // ...}

这是一段典型的 for-each 遍历集合元素的代码,在这里,entry 的类型是 Map.Entry<String, String>,使用 var 就会简化很多。

你现在已经了解了 var 的基本逻辑,就是根据上下文来推断类型信息。这样你就能知道,有些地方 var 是不能用的,比如,它不能用作方法参数和返回值,也不能当作成员变量,因为在这些情况下,没有供编译器推断的信息。所以,var 这个特性在 JEP(JDK Enhancement Proposal)中叫局部变量类型推断

unsetunset实践 varunsetunset

即便是局部变量,也有些情况 var 是不能用的,比如,未初始化的变量,或初始化为 null 的变量,总而言之,你看不出类型的地方,编译器也看不出。在这种情况下,var 就不能用。

问题来了,var 能不能和钻石操作符一起用呢?就像下面这样:

var map = new HashMap<>();

它们两个都是等着别人告诉自己类型信息,按道理来说,应该是不行的。但是,这段代码是可以通过编译的,只不过,编译器会按照最宽泛的类型进行推断,所以,它的类型本质上等价于 HashMap<Object, Object>,这个声明就回退到最原始的状态,完全颠覆掉了 Java 辛辛苦苦建立起的类型系统。所以,在实际的工作中,不要写这种代码。

有一点需要记住,用 var 声明的变量也是有类型的,我们可以把理解为编译器替我们声明了类型。一旦声明好了,它的类型就不能轻易改动了,所以,你会明白下面的代码错在哪了:

var value = 0;// 错误value = "Hello";

明白了这一点,你就可以理解一些不易察觉的错误:

var obj = new Object() {};// 错误obj = new Object();

请注意:obj 的类型是并不是 Object,而是这里声明的一个匿名类,所以,当我们用 Object 对象给它赋值时,就会出现问题。

var 的优点在于简化代码编写,所以,如果能让代码变得清晰,使用 var 就是好的,比如:

var list = new ArrayList<String>(); var stream = list.stream();         

但如果需要别人猜测类型信息,var 就不是一个好的选择,比如:

var data = someObject.getData(); // data 是什么类型?

所以,变量名就变得非常重要,它最好能够承载类型信息:

// 糟糕var x = getUserById(123);// 优秀var user = getUserById(123); // 即使看不到类型,也知道这是一个 User 对象var users = userRepository.findAll(); // 这是一个 User 列表

unsetunset小结unsetunset

类型推断本来是动态语言(比如 Python 和 Ruby 等)的强项,因为它们的类型往往是在运行时决定的。但更多人看到是类型推断可以简化代码的编写,随着编译技术的发展,越来越多的静态语言也开始把类型推断纳入到语言特性之中。当然,细微之处也是有差别的,比如,前面提到的用 var 声明的变量类型是不能改动的,而在动态语言里,这是可以的。

关于 var,我们讨论了这么多,都在说 var 在简化代码编写上起到的作用。但 var 本身并不是 Java 的关键字。这是向后兼容的考量,肯定有不少程序里已经有了作为变量名或函数名的 var。所以,如果你身处一个老项目,准备升级到 Java 10 以后的版本,先清理一下代码里的 var,以便更好地迎接真正 var 的到来。


查看详情:https://www.toutiao.com/article/7604470041899237888
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!

QQ|手机版|小黑屋|梦想之都-俊月星空 ( 粤ICP备18056059号 )|网站地图

GMT+8, 2026-2-11 07:00 , Processed in 0.041849 second(s), 17 queries .

Powered by Mxzdjyxk! X3.5

© 2001-2025 Discuz! Team.

返回顶部