Java11新特性(四)——局部变量的类型推断

前言

在文章的前一章节中主要对 《 JShell使用教程&指南 》 进行了编写,本篇的主要内容为 “ Local-Variable Syntax for Lambda Parameters ”,通过本章节的阅读,将对 JDK 中的 局部变量的类型推断Lambda 表达式中类型推断的应用,var 关键字的正确用法,“ var ” 关键字,它不是真的关键字,博主在这儿把它定义为“ 伪关键字 ”,好吧,如果有不足的地方欢迎指正。

局部变量的类型推断

1、关于局部变量

在之前,博主觉得还是有必要对 “ 什么是成员变量 & 局部变量? ” 进行讲解哈,因为之前博主看到网上很多有关类似的文章将 Java 和 C 中的相关概念给搞混了,而且此类的内容之多,多到惊人,至于为什么会这么多,博主也不太清楚。所以这儿再唠叨哈,可能个别人还不太清除,然后一顿操作后,说博主你这是啥,咋感觉不对喃,呃。。。好吧!

Java中变量的分类:

 成员变量指的是类范围里定义的变量,也就是前面所说的属性;局部变量指的是一个方法内定义的变量。不管是成员变量、还是局部变量,都应该遵守相同的命名规则:从语法角度来看,只要一个合法的标识符即可,但我们应该知道,从程序可读性角度来看,应该是多个意义的单词连缀而成,其中第一个单词首字母小写,后面每个单词首字母大写。

成员变量被分为类属性和实例属性两种,定义一个属性时不使用static修饰的就是实例属性,使用static修饰的就是类属性。其中类属性从这个类的准备阶段起开始存在,直到系统完全销毁这个类,类属性的作用域与这个类的生存范围相同;而实例属性则从这个类的实例被创建开始起存在,直到系统完全销毁这个实例,实例属性的作用域与对应实例的生存范围相同。

 

成员变量 & 局部变量 的异同点:

成员变量:

在类中-在方法体外定义;存在于堆内存中;随着对象的创建而存在,随着对象的回收而消失;必须初始化值,否则在成员变量使用时,编译不通过,会报 “  ” 的错误,如下图:

 

局部变量:

在方法体内部定义;存在于栈中;随着方法的入栈(方法被调用时入站)而存在,随着方法的出栈(方法调用结束)而消失。

 

*特别提示: 在Java中,一个类的生存与消亡总共分为5个阶段,从类被加载到 JVM 虚拟机中开始,直到被回收为止,整个生命周期分为:加载 -> 验证 -> 准备 -> 解析 -> 初始化 -> 使用 -> 回收

 

推荐阅读:疯狂的Java第五版-第五章、第三节 中对 成员变量和局部变量 讲的非常详细,博主这儿只是皮毛而已。

下载地址:《 CSDN 疯狂的Java 第5版 下载 》(或者可以在网上购买,大概65到98RMB左右)

*注意:这儿需要注意一个问题,在 Java 中没有“ 全局变量 ”的这个概念,在 C 、C++中才有 “ 全局变量 ” 的这个概念,可惜博主在某度上随便一搜,80%以上的文章将 Java 中的“ 全局变量 ” 和 “ 成员变量 ”的概念混淆,也就是Java中的全局变量就是成员变量,而成员变量就是全局变量,这是错误的,如果您去面试时被面试官问道,Java 中的成员变量和局部变量时,你还特意的扩展的说了哈,成员变量即全局变量。。。面试官:好了,好了,您面试完了,回去等通知吧!!!

部分截图如下:

图一:

 

图二:

 

图三:

(以上截图来至网络,只为纠正问题,如有不到之处,欢迎指出,博主会立即予以回复和处理)

2、局部变量类型推断

1)、局部变量类型推断的目的:

通过减少与编写 Java 代码相关的形式来提高开发人员的体验,同时维持 Java 对静态类型安全的承诺,允许开发人员忽略局部变量类型的经常不必要的声明。 此功能将允许声明,例如:

            var list = new ArrayList<String>();  // 推断类型为: ArrayList<String>
            var stream = list.stream();          // 推断类型为: Stream<String>

2)、为社么要使用局部变量类型推断:

开发人员经常抱怨Java中所需的模板编码的程度。局部变量的清单类型声明通常被认为是不需要的,甚至是不必要的;如果给出了良好的变量命名,通常非常清楚地知道发生了什么。

为每个变量提供一个清单类型的需要也意外地鼓励开发人员使用过于复杂的表达式; 使用较低级的声明式语法,可以减少将复杂的链式或嵌套式表达式分解为更简单的表达式的不利因素。

几乎所有其他流行的静态类型“curly-brace”语言,无论是在JVM还是OFF,都支持某种形式的局部变量类型推断,包括:C++(auto),C#(var),Scala(var / val),Go(声明:=)。而Java几乎是唯一一种没有采用局部变量类型推断的流行的静态类型语言; 在这一点上,这应该不再是一个有争议的特性了。

在Java SE 8中,类型推断的范围得到了显著扩展,包括对嵌套和链式泛型方法调用的扩展推断,以及对 lambda 形式的推断。这使得构建用于调用链的 API 变得容易得多,并且这样的API(例如 Streams)已经非常流行,这表明开发人员已经习惯于使用推断出的中间类型。一个简单的调用连如下:

            int maxWeight = blocks.stream()
                    .filter(b -> b.getColor() == BLUE)
                    .mapToInt(Block::getWeight)
                    .max();

如上调用链,没有人会因为中间类型 Stream<Block> 和 IntStream,以及 lambda 表达式中 b 的类型没有显式地出现在源代码中而感到困惑(甚至根本没有人注意到)。

局部变量类型推断允许在结构不那么紧密的 API 中产生类似的效果; 局部变量的许多用法本质上都是链,并且从推断中同样受益,例如:

            var path = Paths.get(fileName);
            var bytes = Files.readAllBytes(path);

3)、局部变量类型推断的概述:

对于带有初始化器的局部变量声明、增强 For 循环索引和传统 For 循环中声明的索引变量,允许用保留类型名 var 代替清单类型,示例内容如下:

            var list = new ArrayList<String>();  // 推断类型为: ArrayList<String>
            var stream = list.stream();          // 推断类型为: Stream<String>

标识符 var 不是关键字,而是保留类型名称。 这意味着使用 var 用作变量、方法或包名称的代码不会受到影响; 使用 var 作为类或接口名称的代码则会受到影响(但这些名称在实践中很少见,因为它们违反了通常的命名约定)。

不允许缺少初始化器、声明多个变量、具有额外数组维括号或引用正在初始化的变量的局部变量声明形式。 未经初始化就拒绝局部变量会缩小特性的范围,避免“ 远距离操作 ”推断错误,并且在典型的程序中只排除一小部分局部变量。

 

4)、“ var ” 语法的选择

对语法有不同的看法。这里的两个主要自由度是使用什么关键字(var、 auto 等) ,以及是否为不可变的局部变量提供一个单独的新形式(val、 let)。 考虑了以下语法的选项:

  1. var x = expr only (like C#)
  2. var,加上 val 的不可变局部变量 (like Scala, Kotlin)
  3. var, plus let for immutable locals (like Swift)
  4. auto x = expr (like C++)
  5. const x = expr (already a reserved word)
  6. final x = expr (already a reserved word)
  7. let x = expr
  8. def x = expr (like Groovy)
  9. x := expr (like Go)
     

在收集了大量的意见后,var 显然比 Groovy、C++ 或 Go 方法更受欢迎。 对于针对不可变局部变量的第二种语法形式(val,let),存在着大量不同的意见; 这将是额外获取设计意图的额外形式的权衡。最后,选择只支持 “ var ”,到此关于 “ var ” 的局部变量类型推断便形成了。

 

5)、“ var ” 语法局部变量类型推断小结

一、“ var name; ” 这样的写法是错误的,因为没有推断的依据,所以无法推断,示例内容如下:

jshell> var name;
|  错误:
|  无法推断本地变量 name 的类型
|    (无法在不带初始化程序的变量上使用 'var')
|  var name;
|  ^-------^

jshell>

二、类的实例属性和类属性的数据类型不能使用 “ var ” 语法,示例内容如下:

jshell> class a{
   ...> public var name;
   ...> }
|  错误:
|  此处不允许使用 'var'
|  public var name;
|         ^-^

jshell> class b{
   ...> public static var name;
   ...> }
|  错误:
|  此处不允许使用 'var'
|  public static var name;
|                ^-^

jshell> class c{
   ...> public String name;
   ...> public static Integer age;
   ...> }
|  已创建 类 a

jshell>

“ var ”语法在有参数的lambda表达式使用:

jshell> Consumer<String> consumer = (var t) -> System.out.println(t.toLowerCase()+"-world");
consumer ==> $Lambda$15/0x00000008000b1c40@6093dd95
|  已创建 变量 consumer : Consumer<String>

jshell> consumer.accept("Hello");
hello-world

jshell> 

关于 var 语法的更多细节,请参看:《 Local Variable Type Inference 》


 好了,关于 Java11新特性(四)——局部变量的类型推断 就写到这儿了,如果还有什么疑问或遇到什么问题欢迎扫码提问,也可以给我留言哦,我会一一详细的解答的。 
歇后语:“ 共同学习,共同进步 ”,也希望大家多多关注CSND的IT社区。


作       者: 华    仔
联系作者: [email protected]
来        源: CSDN (Chinese Software Developer Network)
原        文: https://blog.csdn.net/Hello_World_QWP/article/details/88836423
版权声明: 本文为博主原创文章,请在转载时务必注明博文出处!
发布了318 篇原创文章 · 获赞 637 · 访问量 144万+

猜你喜欢

转载自blog.csdn.net/Hello_World_QWP/article/details/88836423