java全局变量和局部变量
In this article, we would take a deep dive at the new feature of Local-Variable Type Inference introduced in Java 10. We will go through the scope and limitations of using the local variable type inference.
在本文中,我们将深入研究Java 10中引入的Local-Variable Type Inference的新功能。我们将探讨使用局部变量类型推断的范围和局限性。
This feature was proposed as part of JEP (JDK Enhancement Proposal): 286. The proposal was for enhancing the language to support the type inference to local variable declaration and initialization.
此功能是作为JEP(JDK增强建议):286的一部分提出的。 该提案是为了增强语言以支持对局部变量声明和初始化的类型推断。
1. Java 10:局部变量类型推断 (1. Java 10: Local Variable Type Inference)
With Java 10, you can use var
for local variables instead of a typed name (Manifest Type). This is done by a new feature which is called Local Variable Type Inference.
使用Java 10,您可以将var
用作局部变量,而不是使用类型名称(清单类型)。 这是通过一项称为局部变量类型推断的新功能完成的。
But first, What is Type Inference?
但是首先, 什么是类型推断?
Type inference is Java compiler’s ability to look at each method invocation and corresponding declaration to determine the type argument (or arguments) that make the invocation applicable. Type Inference is not to Java programming.
类型推断是Java编译器查看每个方法调用和相应声明以确定使调用适用的类型参数的能力。 类型推断不适用于Java编程。
For local variable declarations with initializer, we can now use a reserved type name “var” instead of a manifest type. Let’s look through a few examples.
对于带有初始化程序的局部变量声明,我们现在可以使用保留的类型名称“ var”而不是清单类型。 让我们看一些例子。
var list = new ArrayList<String>(); // infers ArrayList<String>
var stream = list.stream(); // infers Stream<String>
Manifest Type: Explicit identification of type for each variable being declared is called as Manifest Typing. For example, If a variable “actors” is going to store a List of Actors, then its type List<Actor> is the manifest type and its must be declared (as mentioned below) prior to Java 10:
清单类型 :对要声明的每个变量的类型的显式标识称为清单类型。 例如,如果变量“ actors”将存储一个Actor列表,则其类型List <Actor>是清单类型,并且必须在Java 10之前声明它(如下所述):
List<Actor> actors = List.of(new Actor()); // Pre Java 10
var actors = List.of(new Actor()); // Java 10 onwards
2.局部变量类型推断如何工作? (2. How does Local Variable Type Inference work?)
Parsing a var statement, the compiler looks at the right-hand side of the declaration, aka initializer, and it infers the type from the right-hand side (RHS) expression.
解析var语句后,编译器将查看声明的右侧(也称为初始化程序),并从右侧(RHS)表达式推断类型。
Ok fine enough, does this mean that now Java is a dynamically typed language? Not really, it’s still a statically typed language. Let’s take a code snippet for reading a file.
好的,这是否意味着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();
}
Now, let’s look at the decompiled code taken from IntelliJ IDEA decompiler.
现在,让我们看一下从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();
}
Here the compiler properly infers the type of the variable from the right-hand side expression and adds that to the bytecode.
在这里,编译器会从右侧表达式中正确推断出变量的类型,并将其添加到字节码中。
3. var是保留类型名称 (3. var is a reserved type name)
var is not a keyword, It’s a reserved type name. What does it mean?
var不是关键字,它是保留的类型名称。 这是什么意思?
- We can create a variable named “var”.
我们可以创建一个名为“ var”的变量。var var = 5; // syntactically correct // var is the name of the variable
- “var” as a method name is allowed.
允许使用“ var”作为方法名称。public static void var() { // syntactically correct }
- “var” as a package name is allowed.
允许使用“ var”作为程序包名称。package var; // syntactically correct
- “var” cannot be used as the name of a class or interface.
“ var”不能用作类或接口的名称。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
4.局部变量类型推断使用方案 (4. Local Variable Type Inference Usage Scenarios)
Local type inference can be used only in the following scenarios:
本地类型推断只能在以下情况下使用:
- Limited only to Local Variable with initializer 仅限于带有初始化程序的局部变量
- Indexes of enhanced for loop or indexes for循环或索引的增强索引
- Local declared in for loop 本地在for循环中声明
Let’s walk through the examples for these scenarios:
让我们看一下这些场景的示例:
var numbers = List.of(1, 2, 3, 4, 5); // inferred value ArrayList<String>
// 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));
}
5.局部变量类型推断限制 (5. Local Variable Type Inference Limitations)
There are certain limitations of using var, let’s take a look at some of them.
使用var有一定的局限性,让我们看一下其中的一些局限性。
- Cannot use ‘var’ on variables without initializer
If there’s no initializer then the compiler will not be able to infer the type.
没有初始化程序,不能在变量上使用“ var”var x; LocalTypeInference.java:37: error: cannot infer type for local variable x var x; ^ (cannot use 'var' on variable without initializer) 1 error
如果没有初始化程序,则编译器将无法推断类型。
- Cannot be used for multiple variable definition
不能用于多个变量定义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 cannot be used as an initializer for var
Null is not a type and hence the compiler cannot infer the type of the RHS expression.
Null不能用作var的初始化程序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
Null不是类型,因此编译器无法推断RHS表达式的类型。
- Cannot have extra array dimension brackets
不能有额外的数组尺寸括号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
- Poly expressions that have lambdas, method references, and array initializers, will trigger an error
For the type inference of Lambda expressions, Method inference and the Array initializers, compiler relies on the left hand side expression or the argument definition of the method where the expression is passed while var uses RHS, this would form a cyclic inference and hence the compiler generates a compile time error.
具有lambda,方法引用和数组初始化程序的多边形表达式将触发错误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
对于Lambda表达式的类型推断,方法推断和Array初始化程序,编译器依赖于左侧表达式或方法的参数定义(在var使用RHS的情况下传递表达式),这将形成循环推断,因此编译器生成编译时错误。
6.具有局部变量类型推断的泛型 (6. Generics with Local Variable Type Inference)
Java has type inference for Generics and to top of it, it also has to do Type Erasure for any generics statement. There are some edge cases which should be understood when using local type reference with Generics.
Java具有泛型的类型推断,最重要的是,它还必须对任何泛型语句进行类型擦除 。 将局部类型引用与泛型一起使用时,应了解一些边缘情况。
Type Erasure: To implement generics, the Java compiler applies type erasure to, replace all type parameters in generic types with their bounds or Object if the type parameters are unbounded.
类型擦除 :为了实现泛型,Java编译器将类型擦除应用于其中,如果类型参数是无界的,则用其边界或对象替换泛型类型中的所有类型参数。
Let’s go through some use case for var using generics:
我们来看一下使用泛型的var用例:
var map1 = new HashMap(); // Inferred as HashMap
var map2 = new HashMap<>(); // Inferred as HashMap<Object, Object>
map1
– Compiler infers the map as HashMap without any generic type.
map1
–编译器将地图推断为HashMap,没有任何泛型类型。
map2
– The diamond operator relies on the LHS for the type inference, where the compiler cannot infer the LHS and hence it infers map2 to have upper bound or supertype to which the HashMap can be denoted to. This leads to map2 being inferred as HashMap
map2
– map2
运算符依赖LHS进行类型推断,其中编译器无法推断LHS,因此,它推断map2具有可表示HashMap的上限或超类型。 这导致map2被推断为HashMap
7.匿名类类型 (7. Anonymous Class Types)
Anonymous class types cannot be named, but they’re easily understood – they’re just classes. Allowing variables to have anonymous class types introduces useful shorthand for declaring a singleton instance of a local class. Let’s look at an example:
匿名类类型不能命名,但它们很容易理解-它们只是类。 允许变量具有匿名类类型会引入有用的速记方式,用于声明本地类的单例实例。 让我们看一个例子:
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);
8.非限定类型 (8. Non Denotable Types)
An expression that cannot be inferred to a specific type is known as Non Denotable Type. Such type can occur for a capture variable type, intersection type, or anonymous class type. Let’s understand how a Non Denotable Type can be used for local variable type inference:
无法推断为特定类型的表达式称为非指定类型。 对于捕获变量类型,交集类型或匿名类类型,可能会出现这种类型。 让我们了解如何将不可指定类型用于局部变量类型推断:
var map3 = new HashMap<>() { // anonymous class
int someVar;
};
Here, when the diamond operator is used with anonymous class type, the compiler cannot infer the RHS expression to any specific type. This leads to a formation of non-denotable Type.
在这里,当将菱形运算符与匿名类类型一起使用时,编译器无法将RHS表达式推断为任何特定类型。 这导致形成不可表示的类型。
Firstly, the compiler will get denotable type by using the supertype for HashMap<>, which is HashMap<Object, Object>.
首先,编译器将通过使用HashMap <>的超类型(即HashMap <Object,Object>)来获得可表示类型。
Secondly, the anonymous class extension is applied. Finally, this becomes a Non-denotable type which gets assigned to map3.
其次,应用匿名类扩展。 最终,这成为分配给map3的不可表示类型。
A special case of Non-Denotable type which was not possible to create earlier in Java can now be created. Anonymously extending an Object class and adding attributes within it creates a POJO like a class which can be assigned to a variable to hold context. This can be very useful in using a dynamically created object which can have a structure within a temporary context. Let’s see an example:
现在可以创建无法在Java中更早创建的Non-Denotable类型的特殊情况。 匿名扩展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());
9.为局部变量类型推断选择var的一些有趣事实 (9. Some Fun Facts for choosing var for Local Variable Type Inference)
There was a survey for the list of keywords to choose from, for the local type inference. Below is the list of syntactic options provided to community users:
针对本地类型推断进行了一项针对可供选择的关键字列表的调查 。 以下是提供给社区用户的语法选项列表:
- var x = expr only (like C#) var x =仅expr(如C#)
- var, plus val for immutable locals (like Scala, Kotlin) var,加上不可变的当地人的val(例如Scala,Kotlin)
- var, plus let for immutable locals (like Swift) var,再加上不可变的本地变量(如Swift)
- auto x = expr (like C++) 自动x = expr(如C ++)
- const x = expr (already a reserved word) const x = expr(已保留字)
- final x = expr (already a reserved word) final x = expr(已保留字)
- let x = expr 令x = expr
- def x = expr (like Groovy) def x = expr(例如Groovy)
- x := expr (like Go) x:= expr(如Go)
Results of the Survey:
Response of choices by percentage:
调查结果:
选择对百分比的响应:
Rationale for using the 2nd best Choice (var)
使用第二最佳选择的理由(var)
- Even though var was the 2nd best choice, people were fine with it and almost no one hated it outright. Whereas this was not the case for the other options. 尽管var是第二好的选择,但人们对此表示满意,几乎没有人讨厌它。 而其他选项则并非如此。
- C# experience. C# community had found the keyword to be reasonable for a Java-like language. 有C#经验。 C#社区发现该关键字对于类似Java的语言来说是合理的。
- Some readers found that var/val were so similar that they could mostly ignore the difference and it would be annoying to use the different keywords for immutable and mutable variables. 一些读者发现var / val非常相似,以至于他们几乎可以忽略它们之间的差异,并且将不同的关键字用于不可变和可变的变量会很烦人。
- Majority of local variables are effectively final and punishing immutability with another ceremony is not what the intent of the JEP was. 多数局部变量实际上是最终的,并且用另一种仪式惩罚不变性不是JEP的意图。
10.局部变量类型推断的好处 (10. Benefits of Local Variable Type Inference)
- It improves the developer experience 它改善了开发人员的体验
- It reduces code ceremony 减少代码仪式
- It reduces boiler plate code 减少样板代码
- Increases code clarity 提高代码清晰度
11.结论 (11. Conclusion)
In this article we went through Local Type Inference and why var was chosen as a syntactic option. As usual you can check the complete code at github here.
在本文中,我们介绍了本地类型推断以及为什么选择var作为语法选项。 和往常一样,您可以在github 此处查看完整的代码。
翻译自: https://www.journaldev.com/19871/java-10-local-variable-type-inference
java全局变量和局部变量