设为首页 加入收藏

TOP

Java 10 var关键字详解和示例教程(一)
2018-10-14 22:08:16 】 浏览:276
Tags:Java var 关键字 详解 示例 教程

在本文中,我将通过示例介绍新的Java SE 10特性——“var”类型。你将学习如何在代码中正确使用它,以及在什么情况下不能使用它。


Java 10引入了一个闪亮的新功能:局部变量类型推断。对于局部变量,现在可以使用特殊的保留类型名称“var”代替实际类型,如下所示:


提供这个特性是为了增强Java语言,并将类型推断扩展到局部变量的声明上。这样可以减少板代码,同时仍然保留Java的编译时类型检查。 


由于编译器需要通过检查赋值等式右侧(RHS)来推断var的实际类型,因此在某些情况下,这个特性具有局限性。我会在稍后提到这个问题。现在,让我们来看一些简单的例子吧。


在开始演示代码之前,你需要一个IDE来体验这些新特性。现在有很多可选择的IDE,所以你可以在它们当中选择你喜欢的能够支持Java SE 10的IDE,比如Apache NetBeans 9、IntelliJ IDEA 2018或最新版本的Eclipse。


就个人而言,我更喜欢使用交互式的编程工具,可以快速学习Java语言语法,了解新的Java API及其特性,甚至用来进行复杂代码的原型设计。这与枯燥的编辑、编译和执行代码的繁琐过程不太一样:


除了IDE之外,现在还可以使用从Java SE 9以就随ava SE JDK一起发布的JShell。


现在,Java有了自己的REPL(Read-eva luate-Print-Loop)实现JShell(Java Shell),作为交互式的编程环境。那么,它有什么神奇的地方?JShell提供了一个快速友好的环境,让你能够快速探索、发现和试验Java语言特性及其丰富的库。


在JShell中,你可以一次输入一个程序元素,并可以立即看到结果,然后根据需要对代码做出调整。因此,JShell用它的Read-eva luate-Print循环取代了编辑、编译和执行的繁琐过程。在JShell中,你不需要编写完整的程序,只需要编写JShell命令和Java代码片段即可。


当你输入代码段时,JShell会立即读取、执行并打印结果,然后准备好执行下一个代码片段。因此,JShell的即时反馈可以让你保持注意力,提高你的效率,并加快学习和软件开发过程。


对JShell的介绍就到此为止(InfoQ最近对这个工具进行过全面介绍)。为了深入了解JShell的功能,我录制了一套视频教程“Hands-on Java 10 Programming with JShell”,可以帮助你掌握JShell,可以从PacktUdemy访问这些教程。


现在,让我们通过一些简单的示例(使用JShell)来了解这个新的var类型能做些什么。


为了能用上JShell,我假设你安装了Java SE或JDK 10+,并且JDK的bin目录已经加入到系统路径中。如果还没有安装,可以在这里下载JDK 10+最新版本


这个命令会启动一个新的JShell会话,并显示这个消息:


现在你已经安装了JDK 10,现在让我们开始玩JShell。我们直接跳到终端,通过示例来了解var类型。只需在jshell提示符下输入我接下来要介绍的每个代码片段,我会把结果留给你作为练习。如果你稍微有瞄过一两眼在代码,你会注意到它们看起来好像是错的,因为当中没有分号。你可以试试看,看看能不能运行。


这是var类型的基本用法,在下面的示例中,编译器可以将RHS推断为String字面量:


这里不需要分号,因为JShell是一个交互式环境。只有当同一行代码有多个语句或一个类型声明或方法声明中有多个语句时才需要分号,你将在后面的示例中看到。


在使用var时,多态仍然有效。在继承的世界中,var类型的子类型可以像平常一样赋值给超类型的var类型,如下所示:


但不能将超类型var赋值给子类型var,如下所示:


这是因为JPasswordField是JTextField的子类。


如果出现错误的赋值操作会怎样?不兼容的变量类型不能相互赋值。一旦编译器推断出实际类型的var,就不能将错误的值赋值给它,如下所示:


这里发生了什么?编译器将“var number = 10”替换为“int number = 10”,所以仍然可以保证安全性。


现在让我们来看看var与集合和泛型一起使用时如何进行类型推断。我们先从集合开始。在下面的情况中,编译器可以推断出集合元素的类型是什么:


这里没有必要进行类型转换,因为编译器已经推断出正确的元素类型为int。


下面的情况就不一样了,编译器只会将其作为对象集合(而不是整数),因为在使用菱形运算符时,Java需要LHS(左侧)的类型来推断RHS的类型:


对于泛型,最好在RHS使用特定类型(而不是菱形运算符),如下所示:


让我们先来看看基于索引的For循环:


下面是在For Each循环中:


现在我有一个问题,var是否适用于Java 8 Stream?让我们看看下面的例子:


那么三元运算符呢?


现在,如果在三元运算符的RHS中使用不同类型的操作数会怎样?让我们来看看:


这两个例子是否可以说明var的类型是在运行时决定的?绝对不是!让我们以旧方式实现同样的逻辑:


Serializable是其中两个操作数最具兼容性和最专的有类型(最不专有的类型是java.lang.Object)。


String和Integer都实现了Serializable。Integer从int自动装箱。换句话说,Serializable是两个操作数的LUB(最小上限)。所以,这表明往前数第三个例子中的var类型也是Serializable。


让我们转到另一个主题:将var类型传给方法。


我们先声明一个名为squareOf的方法,这个方法的参数为BigDecimal类型,并返回参数的平方,如下所示:


现在让我们看看它如何与泛型一起使用。我们声明一个名为toIntgerList的方法,参数类型为List<T>(泛型类型),并使用Streams API返回一个整数列表,如下所示:


最后,让我们看一下var和匿名类。我们通过实现Runnable接口来使用线程,如下所示:


到目前为止,我已经介绍了Java 10的新特性——“var”类型,它减少了样板编码,同时保持了Java的编译时类型检查。我还通过实例说明了可以用它做些什么。接下来,你将了解var类型的局限性以及不能将它用在哪些地方。


接下来,你将看一些示例,以便了解var类型???能无法做到的事情。


jshell提示符将会告诉你代码出了什么问题,你可以利用这些交互式的即时反馈。


第一个也是最简单的原则就是不允许没有初始值的变量。


你将得到一个编译错误,因为编译器无法推断这个局部变量x的类型。


尝试运行这行代码:


你将得到一个错误消息:复合声明中不允许使用’var'。


尝试创建一个名为testVar的方法,如下所示,将下面的代码复制并粘贴到JShell中:


方法不会被创建,而是会抛出编译错误。因为没有设置初始值,所以不能使用’var'。


不允许进行null赋值,如下所示:


这将抛出异常“variable initializer is 'null'”。因为null不是一个类型。


另一个例子,没有Lambda初始化器。这与菱形操作符那个示例一样,RHS需要依赖LHS的类型推断。


将抛出异常:“lambda expression needs an explicit target-type”。


没有方法引用初始值,类似于Lamb

首页 上一页 1 2 下一页 尾页 1/2/2
】【打印繁体】【投稿】【收藏】 【推荐】【举报】【评论】 【关闭】 【返回顶部
上一篇TypeScript 3.1增加可映射元组和.. 下一篇Kotlin 1.3带来稳定的协程、合约..

最新文章

热门文章

Hot 文章

Python

C 语言

C++基础

大数据基础

linux编程基础

C/C++面试题目