java var类型
重要要点
- Java 10引入了一个闪亮的新功能:局部变量的类型推断。 对于局部变量,您现在可以使用特殊的保留类型名称“ var”,而不是实际类型。
- 提供此功能是为了增强Java语言并将类型推断扩展到使用初始化程序声明的局部变量。 这减少了所需的样板代码,同时仍保持Java的编译时类型检查。
- 由于编译器需要通过查看右侧(RHS)来推断var实际类型,因此此功能在某些情况下具有局限性,例如在初始化数组和流时。
- 在本动手教程中进行实验,以了解如何使用新的“ var”类型减少样板代码。
在本文中,我将通过示例介绍新的Java SE 10功能“ var”类型。 您将学习如何在代码中正确使用它,以及何时不能使用它。
介绍
Java 10引入了一个闪亮的新功能:局部变量的类型推断。 对于局部变量,您现在可以使用特殊的保留类型名称“ var”,而不是实际类型,如下所示:
var name = “Mohamed Taman”;
提供此功能是为了增强Java语言并将类型推断扩展到使用初始化程序声明的局部变量。 这减少了所需的样板代码,同时仍保持Java的编译时类型检查。
由于编译器需要通过观察将R ight- H和S IDE(RHS)推断出VAR实际类型,此功能在某些情况下限制。 过一会儿我要提一下,继续阅读。 现在让我们来看一些简单的例子。
嘿,等等,等等 ! 在跳入代码之前,您将需要使用IDE像往常一样尝试新功能。 好消息是市场上有很多,所以您可以在许多IDE(例如Apache NetBeans 9,IntelliJ IDEA 2018或新的Eclipse)中选择自己喜欢的,支持Java SE 10的IDE。
就我个人而言,我始终喜欢使用交互式编程环境工具来快速学习Java语言语法,探索新的Java API及其功能,甚至用于复杂代码的原型设计。 这不是繁琐的编辑,编译和执行代码的过程,该过程通常涉及以下过程:
- 编写一个完整的程序。
- 编译并修复所有错误。
- 运行程序。
- 找出问题所在。
- 编辑它。
- 重复该过程。
再次好消息是,您将使用自Java SE 9以来Java SE JDK内置和随附的JShell工具,该工具是该版本的旗舰功能。
什么是JShell
现在,Java使用JShell工具实现了丰富的REPL ( R e- E valuate - Pint- L oop)实现,称为J ava S hell,它是一种交互式编程环境 。 那么,什么是魔术呢? 这很简单。 JShell提供了一个快速友好的环境,使您可以快速探索,发现和试验Java语言功能及其广泛的库。
使用JShell,您可以一次输入一个程序元素,立即查看结果,并根据需要进行调整。 因此,JShell用其read-evaluate-print循环代替了繁琐的编辑,编译和执行循环。 在JShell中,您不是编写完整的程序,而是编写JShell命令和Java代码段。
输入代码段时,JShell会立即读取,评估并打印其结果。 然后循环执行以针对下一个片段再次执行此过程。 因此,JShell及其即时反馈可以吸引您的注意力,提高您的性能,并加快学习和软件开发过程。
这对于JShell来说已经足够了,InfoQ最近已经发布了对该工具的详尽介绍。 为了深入学习并了解有关JShell的所有功能的更多信息,我已经录制了有关此主题的完整视频培训,标题为“ 使用JShell进行Java 10编程实践[视频] ”,它可以帮助您精通该主题,并且可以通过以下途径获得Packt或Udemy 。
现在,让我们来看一些简单的示例,以了解使用JShell可以使用此新的var类型功能来完成的工作 。
必备软件
为了可能与JShell一起使用,我假设您已经安装了Java SE或JDK 10+,并且JDK bin文件夹中的工具被配置为可以从系统中的任何位置访问,如果没有,这里是安装JDK 10+ 最新版本的链接。 。
启动JShell会话
要在以下位置启动JShell会话:
- Microsoft Windows打开命令提示符,然后键入jshell并按Enter。
- 在Linux上,打开一个shell窗口,然后键入jshell并按Enter。
- 在macOS(以前称为OS X)上时,打开一个Terminal窗口,然后键入以下命令“ jshell ”并按Enter。
塔拉阿! 此命令执行一个新的JShell会话,并在jshell>提示符下显示此消息:
| Welcome to JShell -- Version 10.0.1
| For an introduction type: /help intro
jshell>
使用“ var”类型。
现在,您已经安装了JDK 10,让我们开始使用JShell,让我们直接跳到终端以通过示例开始破解var类型功能。 只需在jshell提示符下输入接下来要介绍的每个片段,我将把结果留给您练习。 如果您先偷偷摸摸地看一下代码,您会发现它看起来错了,因为没有分号。 尝试一下,看看它是否有效。
简单类型推断案例
这是var类型的基本用法,在下面的示例中,编译器可以将RHS推断为String文字:
var name = "Mohamed Taman"
var lastName = str.substring(8)
System.out.println("Value: "+lastName +" ,and type is: "+ lastName.getClass().getTypeName())
不需要分号,因为JShell是一个交互式环境。 仅当同一行上有多个语句,或者声明的类型或方法中包含多个语句时才需要分号,并且您将在以下示例中看到分号。
var类型和继承
同样,多态仍然有效。 在继承的世界中,可以将var type的子类型分配给var type的超类型,这是通常情况,如下所示:
import javax.swing.*
var password = new JPasswordField("Password text")
String.valueOf(password.getPassword()) // To convert password char array to string to see the value
var textField = new JTextField("Hello text")
textField = password
textField.getText()
但是不能将超类型var分配给子类型var,如下所示:
password = textField
这是因为JPasswordField是JTextField类的子类。
var和编译时安全
那么现在,错误的分配又如何呢? 不兼容的变量类型不能互相分配。 一旦编译器推断出var的实际类型,就不能为以下内容分配错误的值:
var number = 10
number = "InfoQ"
那么,这里发生了什么? 此处的编译器只是将“ var number = 10 ”替换为“ int number = 10 ”以进行进一步检查,因此仍保持安全性。
带有集合和泛型的var
好吧,让我们看看var如何与Collection元素类型推断和泛型一起工作。 让我们从集合开始。 在以下情况下,编译器可以推断集合元素的类型:
var list = List.of(10);
此处无需进行强制转换,因为编译器已推断出正确的元素类型int
int i = list.get(0); //equivalent to: var i = list.get(0);
在以下情况下,情况有所不同,编译器将其视为对象(不是整数)的集合,这是因为当您使用菱形运算符时,Java已经需要LHS (左手侧)上的类型来推断RHS上的类型。 ,让我们看看如何;
var list2 = new ArrayList<>(); list2.add(10); list2
int i = list2.get(0) //Compilation error
int i = (int) list2.get(0) //need to cast to get int back
对于泛型,最好在RHS上使用特定类型(而不是菱形运算符),如下所示:
var list3 = new ArrayList<Integer>(); list3.add(10); System.out.println(list3)
int i = list3.get(0)
让我们跳下去看看var类型如何在不同类型的循环中工作:
var类型用于循环
我们首先检查基于索引的普通For Loop
for (var x = 1; x <= 5; x++) {
var m = x * 2; //equivalent to: int m = x * 2;
System.out.println(m);
}
这是它与For Each循环一起使用的方式
var list = Arrays.asList(1,2,3,4,5,6,7,8,9,10)
for (var item : list) {
var m = item + 2;
System.out.println(m);
}
所以现在我在这里有一个问题,var是否可以与Java 8 Stream一起使用? 让我们看下面的例子;
var list = List.of(1, 2, 3, 4, 5, 6, 7)
var stream = list.stream()
stream.filter(x -> x % 2 == 0).forEach(System.out::println)
三元运算符的var类型
三元运算符呢?
var x = 1 > 0 ? 10 : -10
int i = x
现在,如果您在三元运算符的RHS上使用不同类型的操作数,该怎么办? 让我们来看看:
var x = 1 > 0 ? 10 : "Less than zero"; System.out.println(x.getClass()) //Integer
var x = 1 < 0 ? 10 : "Less than zero"; System.out.println(x.getClass()) // String
这两个例子是否表明var的类型是在运行时确定的? 让我们以旧的方式做同样的事情:
Serializable x = 1 < 0 ? 10 : "Less than zero"; System.out.println(x.getClass())
Serializable ,它是两个不同操作数的通用兼容且最专业的类型(最不专业的类型为java.lang.Object
)。
String和Integer都实现Serializable。 整数从int自动装箱。 换句话说,序列化是两个操作数的LUB(L东ûPPER 乙 ound)。 因此,这表明在我们的第三个示例中,var类型也是Serializable 。
让我们进入另一个主题:将var类型传递给方法。
带有方法的var类型
我们首先声明一个名为squareOf的方法,该方法的一个参数为BigDecimal类型,该方法将按如下所示返回此参数的平方:
BigDecimal squareOf(BigDecimal number) {
var result= number.multiply(number);
return result;
}
var number = new BigDecimal("2.5")
number = squareOf(number)
现在让我们看看它如何与泛型一起工作。 再次让我们声明一个名为toIntgerList的方法,该方法的一个参数类型为T类型的List (泛型类型),它使用Streams API如下返回该参数的基于整数的列表:
<T extends Number> List<Integer> toIntgerList(List<T> numbers) {
var integers = numbers.stream()
.map(Number::intValue)
.collect(Collectors.toList());
return integers;
}
var numbers = List.of(1.1, 2.2, 3.3, 4.4, 5.5)
var integers = toIntgerList(numbers)
带有匿名类的var
最后,让我们看看将var与匿名类一起使用。 让我们通过实现Runnable接口来利用线程,如下所示:
var message = "running..." //effectively final
var runner = new Runnable(){
@Override
public void run() {
System.out.println(message);
}}
runner.run()
到目前为止,我已经介绍了闪亮的新Java 10功能“ var”类型,它减少了样板代码,同时保持了Java的编译时类型检查。 您通过示例展示了可以完成的工作。 现在,您将了解var类型的局限性以及不允许使用的类型。
“无限制”限制
现在,您将看一些简单的示例,以了解使用var类型功能无法完成的工作。 因此,让我们跳到终端,通过一些示例来破解限制。
jshell提示的结果将说明代码有什么问题,因此您可以利用交互式即时反馈。
您应该使用一个值进行初始化
首先,也是最简单的事情是,此处不允许使用没有初始化程序的变量。
var name;
您将得到一个编译错误; 因为编译器无法推断此局部变量x的类型。
不允许复合声明
尝试运行此行;
var x = 1, y = 3, z = 4
您将收到此错误消息,复合声明中不允许使用“ var”。
没有明确的分配
尝试如下创建一个称为testVar的方法,只需将其复制并粘贴到JShell中:
void testVar(boolean b) {
var x;
if (b) {
x = 1;
} else {
x = 2;
}
System.out.println(x);
}
它不会创建方法,而是会引发编译错误。 如果没有初始化程序,则不能在变量上使用“ var”。 即使是类似以下的赋值(称为“ 确定性赋值” ),也不适用于var。
空分配
不允许空分配,如下所示;
var name = null;
这将引发异常“变量初始值设定项为'null'”。 因为null不是类型。
使用Lambdas
另一个示例,没有Lambda初始化程序。 就像钻石算子的情况一样,RHS已经需要来自LHS的类型推断。
var runnable = () -> {}
将抛出此异常,“ lambda表达式需要显式的目标类型”。
var和方法参考
没有方法引用初始化程序,类似于lambda和diamond运算符:
var abs = BigDecimal::abs
将抛出此异常:“方法引用需要显式的目标类型”
var和数组初始化
并非所有的数组初始值设定项都起作用,让我们看看带有[]的var如何不起作用:
var numbers[] = new int[]{2, 4, 6}
错误将是:不允许将'var'作为数组的元素类型。
以下内容也不起作用:
var numbers = {2, 4, 6}
错误是:“数组初始化器需要显式的目标类型”
就像上一个示例一样,var和[]在LHS上不能一起使用:
var numbers[] = {2, 4, 6}
错误:不允许将“ var”作为数组的元素类型
仅以下数组初始化有效:
var numbers = new int[]{2, 4, 6}
var number = numbers[1]
number = number + 3
不允许使用var字段
class Clazz {
private var name;
}
不允许使用var方法参数
void doAwesomeStuffHere(var salary){}
没有var作为方法返回类型
var getAwesomeStuff(){ return salary; }
catch子句中没有var
try {
Files.readAllBytes(Paths.get("c:\temp\temp.txt"));
} catch (var e) {}
编译时var类型在后台发生了什么?
“ var”实际上只是一个语法糖,它不会在编译后的代码中引入任何新的字节码构造,并且在运行时JVM没有针对它们的特殊指令。
结论。
在本文的最后,您介绍了“ var”类型是什么,以及此功能如何减少样板代码,同时保持Java的编译时类型检查。
然后,您了解了新的JShell工具(Java的REPL实现),它可以帮助您快速学习Java语言,并探索新的Java API及其功能。 您还可以使用JShell代替复杂的代码原型,而不用传统的单调乏味的代码编辑,编译和执行周期。
最后,您了解了所有var类型的功能和限制,例如可以使用和不能使用的地方。 为您写这篇文章很有趣,所以我希望您喜欢它并发现它有用。 如果是这样,那么请传播这个词。
资源资源
java var类型