在本文中,我们将深入研究Java 10中引入的局部变量类型推断的新特性。我们将讨论使用局部变量类型推断的范围和局限性。
此功能是作为JEP(JDK增强提案)的一部分提出的:286。该提案用于增强语言以支持对局部变量声明和初始化的类型推断。
有关Java 10发行版的完整概述,请参阅Java 10功能。
目录[ 隐藏 ]
使用Java 10,您可以使用var
局部变量而不是键入的名称(Manifest Type)。这是通过称为局部变量类型推断的新特征完成的。
但首先,什么是类型推断?
类型推断是Java编译器查看每个方法调用和相应声明的能力,以确定使调用适用的类型参数(或参数)。类型推断不是Java编程。
对于使用初始化程序的局部变量声明,我们现在可以使用保留类型名称“var”而不是清单类型。我们来看几个例子。
var list = new ArrayList(); // infers ArrayList
var stream = list.stream(); // infers Stream
清单类型:声明的每个变量的类型的显式标识称为清单类型。例如,如果变量“actors”要存储Actor的List,那么它的类型List
List actors = List.of(new Actor()); // Pre Java 10
var actors = List.of(new Actor()); // Java 10 onwards
解析var语句,编译器查看声明的右侧,即初始化程序,并从右侧(RHS)表达式推断出类型。
好吧,这是否意味着现在Java是一种动态类型的语言?不是真的,它仍然是一种静态类型的语言。我们来看一个代码片段来读取文件。
private static void readFile() throws IOException {
var fileName = "Sample.txt";
var line = "";
var fileReader = new FileReader(fileName);
var bufferedReader = new BufferedReader(fileReader);
while ((line = bufferedReader.readLine()) != null) {
System.out.println(line);
}
bufferedReader.close();
}
现在,让我们看一下从IntelliJ IDEA反编译器中获取的反编译代码。
private static void readFile() throws IOException {
String fileName = "Sample.txt";
String line = "";
FileReader fileReader = new FileReader(fileName);
BufferedReader bufferedReader = new BufferedReader(fileReader);
while ((line = bufferedReader.readLine()) != null) {
System.out.println(line);
}
bufferedReader.close();
}
这里编译器正确地从右侧表达式推断出变量的类型,并将其添加到字节码中。
var不是关键字,它是保留类型名称。这是什么意思?
var var = 5; // syntactically correct
// var is the name of the variable
public static void var() { // syntactically correct
}
package var; // syntactically correct
class var{ } // Compile Error
LocalTypeInference.java:45: error: 'var' not allowed here
class var{
^
as of release 10, 'var' is a restricted local variable type and cannot be used for type declarations
1 error
interface var{ } // Compile Error
本地类型推断只能在以下场景中使用:
让我们来看看这些场景的示例:
var numbers = List.of(1, 2, 3, 4, 5); // inferred value ArrayList
// Index of Enhanced For Loop
for (var number : numbers) {
System.out.println(number);
}
// Local variable declared in a loop
for (var i = 0; i < numbers.size(); i++) {
System.out.println(numbers.get(i));
}
使用var有一些限制,让我们来看看其中的一些。
如果没有initailizer,那么编译器将无法推断出类型。
var x;
LocalTypeInference.java:37: error: cannot infer type for local variable x
var x;
^
(cannot use 'var' on variable without initializer)
1 error
var x = 5, y = 10;
LocalTypeInference.java:41: error: 'var' is not allowed in a compound declaration
var x = 5, y = 10;
^
1 error
Null不是类型,因此编译器无法推断RHS表达式的类型。
var author = null; // Null cannot be inferred to a type
LocalTypeInference.java:47: error: cannot infer type for local variable author
var author = null;
^
(variable initializer is 'null')
1 error
var actorArr[] = new Actor[10];
LocalTypeInference.java:52: error: 'var' is not allowed as an element type of an array
var actorArr[] = new Actor[10];
^
1 error
对于Lambda表达式,方法推理和数组初始值设定项的类型推断,编译器依赖于左侧表达式或传递表达式的方法的参数定义,而var使用RHS,这将形成循环推断,因此编译器生成编译时错误。
var min = (a, b) -> a < b ? a : b;
LocalTypeInference.java:59: error: cannot infer type for local variable min
var min = (a, b) -> a < b ? a : b;
^
(lambda expression needs an explicit target-type)
1 error
var minimum = Math::min;
LocalTypeInference.java:65: error: cannot infer type for local variable minimum
var minimum = Math::min;
^
(method reference needs an explicit target-type)
1 error
var nums = {1,2,3,4,5};
LocalTypeInference.java:71: error: cannot infer type for local variable nums
var nums = {1,2,3,4,5};
^
(array initializer needs an explicit target-type)
1 error
Java具有泛型的类型推断,除此之外,它还必须为任何泛型语句执行类型擦除。在使用Generics的本地类型引用时,应该理解一些边缘情况。
类型擦除:为了实现泛型,Java编译器应用类型擦除,如果类型参数是无界的,则用泛化或对象替换泛型类型中的所有类型参数。
让我们通过泛型来讨论var的一些用例:
var map1 = new HashMap(); // Inferred as HashMap
var map2 = new HashMap<>(); // Inferred as HashMap
map1
- 编译器将地图推断为HashMap,不带任何泛型类型。
map2
- 菱形运算符依赖于LHS进行类型推断,这里编译器无法推断LHS,因此它推断map2具有可以表示HashMap的上限或超类型。这导致map2被推断为HashMap。
无法命名匿名类类型,但它们很容易理解 - 它们只是类。允许变量具有匿名类类型引入了用于声明本地类的单例实例的有用简写。我们来看一个例子:
var runnable = new Runnable() {
@Override
public void run() {
var numbers = List.of(5, 4, 3, 2, 1);
for (var number : numbers) {
System.out.println(number);
}
}
};
runThread(runnable);
无法推断到特定类型的表达式称为Non Nototable Type。对于捕获变量类型,交集类型或匿名类类型,可以发生这种类型。让我们理解一个非可表达类型如何用于局部变量类型推断:
var map3 = new HashMap<>() { // anonymous class
int someVar;
};
这里,当菱形运算符与匿名类类型一起使用时,编译器无法将RHS表达式推断为任何特定类型。这导致形成不可表示的类型。
首先,编译器将通过使用HashMap <>的超类型来获得可表示的类型,即HashMap
其次,应用匿名类扩展。最后,这将成为一个不可表示的类型,它被分配给map3。
现在可以创建一个特殊的Non Denotable类型,它不能在Java中创建。匿名扩展Object类并在其中添加属性会创建类似POJO的类,可以将其分配给变量以保存上下文。这在使用动态创建的对象时非常有用,该对象可以在临时上下文中具有结构。我们来看一个例子:
// Special Case Non Denotable Type
var person = new Object() {
class Name {
String firstName;
String lastName;
public Name(String firstName, String lastName) {
super();
this.firstName = firstName;
this.lastName = lastName;
}
public String getFirstName() {
return firstName;
}
public void setFirstName(String firstName) {
this.firstName = firstName;
}
}
Name name;
Actor actor;
public String displayName() {
return name.getFirstName() + " " + name.lastName;
}
};
person.name = person.new Name("Rakesh", "Kumar");
System.out.println(person.displayName());
有一个调查的关键字列表可供选择,为本地类型推断。以下是向社区用户提供的语法选项列表:
调查结果:
百分比选择的回应:
使用第二好的选择(var)的理由
在本文中,我们通过Local Type Inference以及为什么选择var作为语法选项。像往常一样,您可以在这里查看github上的完整代码。
翻译&转载:https://www.journaldev.com/19871/java-10-local-variable-type-inference