java全局变量和局部变量_Java 10:局部变量类型推断

本文深入探讨了Java 10中引入的局部变量类型推断(Local Variable Type Inference),该特性允许使用`var`代替显式类型声明。文章解释了类型推断的工作原理,`var`的使用场景、限制,以及泛型和匿名类类型等复杂情况的处理。此外,还讨论了选择`var`的原因和该特性带来的好处,包括提升开发者体验和代码清晰度。
摘要由CSDN通过智能技术生成

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 = 5; // syntactically correct
    // var is the name of the variable

    我们可以创建一个名为“ var”的变量。
  • “var” as a method name is allowed.
    public static void var() { // syntactically correct 
    }

    允许使用“ var”作为方法名称。
  • “var” as a package name is allowed.
    package var; // syntactically correct

    允许使用“ var”作为程序包名称。
  • “var” cannot be used as the name of a class or interface.
    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”不能用作类或接口的名称。

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有一定的局限性,让我们看一下其中的一些局限性。

  1. Cannot use ‘var’ on variables without initializer

    If there’s no initializer then the compiler will not be able to infer the type.

    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”

    如果没有初始化程序,则编译器将无法推断类型。

  2. 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

    不能用于多个变量定义
  3. 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.

    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不能用作var的初始化程序

    Null不是类型,因此编译器无法推断RHS表达式的类型。

  4. 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

    不能有额外的数组尺寸括号
  5. 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.

    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,方法引用和数组初始化程序的多边形表达式将触发错误

    对于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.

map2map2运算符依赖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:

java local variable type inference

Response of choices by percentage:

调查结果:

java局部变量类型推断

选择对百分比的响应:

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全局变量和局部变量

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值