在本教程中,我们将学习Java 10的特性--局部变量类型推断。JDK Enhancement Proposal(JEP) 286是Java 10的一个新特性。
局部变量类型推理介绍
类型推断是编译器根据初始化值自动检测数据类型。随着Java 10的推出,它被引入到局部变量中。这个功能在其他语言中已经存在,比如Scala、javascript和c#。让我们看看这个功能在以前的Java版本中是如何工作的。通常情况下,局部变量的声明方式如下,在下面的例子中,在Java 5和6版本中创建了一个持有字符串的ArrayList。
List lists=new ArrayList();
这里有两个部分,等价运算符的左侧和右侧
左手边是变量声明,变量持有的数据类型 右手边是初始化,变量被初始化为可能持有的数据类型。
这篇文章,我们将讨论如何缩短局部变量的声明。
在Java 7中,引入了Diamond操作符,允许在没有类型的情况下使用空括号,变量的声明方式如下 在Java 7之前,字符串的ArrayList可以声明如下
List lists=new ArrayList();
编译器如何解释var类型?
每当遇到局部类型的变量时,它首先检查右侧的等价符号,即初始化器,找出初始化器的类型并将此类型分配给一个变量。
var的使用实例
在下面这一节中,我们将公布var的用法和例子。
基本类型推理例子
在下面的例子中,本地字符串被声明并以字符串常数字面值初始化。另一个局部变量被声明并用于存储字符串的小写结果。类型没有被声明。编译器从其值中自动推断出类型。
public class LocalVariableTypeInferenceHelloWorld {
public static void main(String[] args) {
var str = "THIS IS STRING"; // equals to String str= "THIS IS STRING";
var lowerCaseString = str.toLowerCase(); // equals to String lowerCaseString = str.toLowerCase();
System.out.println(lowerCaseString);
System.out.println(lowerCaseString.getClass().getTypeName());
}
}
输出是
this is string
java.lang.String
类型安全与var的例子
在下面的例子中,创建了本地字符串变量,并将同一本地变量分配给一个整数。这就产生了编译错误
var str = "test string"; // equals to String str= "test string";
str=10;
编译器仍在使用类型安全索引工作。
循环局部变量类型推理的例子
在for循环中声明了索引变量,如下所示。
for (var i = 1; i <= 10; i++) {
var temp= i* 2; // equals to Integer temp=i* 2;
System.out.println(temp);
}
对于每个局部变量类型推断的例子
本地变量可以用var保留词来推断,如下所示 在每个迭代过程中为迭代声明变量。
import java.util.ArrayList;
public class ForEachVarDemo {
public static void main(String[] args) {
var list = new ArrayList();
list.add("abc");
list.add("def");
list.add("ggg");
for (var str : list) {
var upperString = str.toUpperCase(); // equal to String upperString = str.toUpperCase();
System.out.println(upperString);
}
}
}
方法中的返回值
在一个方法中,声明一个局部变量并返回给调用者。同时,返回值也被存储在另一个局部变量中。
public class MethodLocalVarDemo {
public static void main(String[] args) {
var number1 = 30;
var number2 = 50;
var output = sum(number1, number2);
System.out.println(output);
}
private static Integer sum(Integer number1, Integer number2) {
var result = number1 + number2;
return result;
}
}
用于存储三元运算结果的局部变量
本地变量用于存储三元运算器的评估结果,在下面的例子中,结果被推断为字符串。
var result = true? "true": "false";
System.out.println(result);
声明一个用于流的局部变量
var不仅用于分配数据类型,也用于推断流。下面是var流的例子。
var list = new ArrayList();
list.add("abc");
list.add("def");
list.add("ggg");
var stream = list.stream();
stream.forEach((string) -> {
System.out.println(string);
});
编译错误
本地变量的使用也有很多限制和约束。下面的情况都会出现编译错误。
没有初始化器的局部变量
这里的局部变量被声明但没有初始化,它给出的编译错误是 "不能在没有初始化器的变量上使用'var'"。
没有用空值初始化的变量
如果var变量初始化为空值,编译器会产生错误--无法推断初始化为'null'的局部变量的类型。
没有多变量或复合变量声明
不允许声明多个局部变量
var m=5,n=2,p=3; // not allowed
int m=5,n=2,p=3; // allowed
如果我们声明了多个局部变量,它给出了var'在复合声明中是不允许的'。
不允许本地变量数组初始化
像下面这样声明的数组不允许用于局部变量。
上面这行代码给出的错误是数组初始化器需要一个明确的目标类型
不是类的实例变量
不允许用局部变量声明的实例变量或类变量。
public class ClassLocalDemo {
var memberVariable=10;
public static void main(String[] args) {
}
}
没有方法参数/参数
方法签名中没有声明局部变量。
public class ClassLocalDemo {
public static void main(String[] args) {
}
public static void method(var parame) {
}
}
方法返回类型中没有var
方法的返回类型不应该是var字样,并且会引发编译错误。
public static var method() {
}
var不允许出现在catch块中,如下图所示
try {
// code
} catch (var e) {
}
Lambda表达式需要一个明确的目标类型
下面的lambda表达式的var初始化引发了一个错误。像钻石运算符,右侧,需要目标数据类型才能工作。
var helloString = () -> "teststring";
不允许构造器参数
var不应该与构造函数参数一起使用,如下图所示
public class Demo {
public Demo(var a) {
}
}
优点
- 冗长的代码
- 可读性和更少的类型化
缺点
Java 7中的局部变量声明和钻石运算符相冲突,使开发人员感到困惑。
var保留字
var不是一个关键词,是保留字。我们不能用'var'这个词来给变量和方法命名,这些是无效的。