var local variable 區域變數 implicitly typed Java 10 JDK 10 Java 11 JDK 11

Local Variable Type Inference -- Java 10後加入的var識別字

郭彥廷 2020/12/28 11:36:32
3484

Overview

JDK10後,我們可用var這個識別字宣告區域變數了!

在接下來的段落,我們將細述此語法更新並佐以範例code

Introduction

Java 9 以前,我們宣告區域變數必須一併指定他的型別,並確保型別與等號右側的initializer一致。

如:

String message = "Good bye, Java 9";

Java 10後,可以這樣宣告區域變數:

var message = "Hello, Java 10";

 

也就是說我們不提供變數的資料型別在等號左側,透過標記變數為var, 編譯器會就等號右側的initializer推論出變數的資料型別。

(如在上述的範例,編譯器會判斷message變數為String)

 

透過使用var,可以讓我們的程式更簡潔。

像是下面這段有點冗長難讀的code

URL url = new URL("http://www.oracle.com/"); 
URLConnection conn = url.openConnection(); 
Reader reader = new BufferedReader(
    new InputStreamReader(conn.getInputStream()));

我們可以用識別字var改寫成

var url = new URL("http://www.oracle.com/"); 
var conn = url.openConnection(); 
var reader = new BufferedReader(
    new InputStreamReader(conn.getInputStream()));

 

需注意的是,var這個識別字僅提供給區域變數使用。

var不能用在成員變數(member variables)和方法的參數與回傳型別。

 

還有Java仍然是靜態的語言。使用var時要讓編譯器有足夠的資訊判斷變數的型別。

 

此外,var並非java中的關鍵字(keyword ),這確保了他的向前相容性。

如果之前寫的程式中有用var當變數名稱、方法名稱,或是package的名稱用var命名,皆不會因為這次的語法新增而受影響。

但若曾用var作為類別名稱或介面名稱,則需重新命名。

Example

var 的用法有:

(1) 宣告區域變數,並且等號右側有非空的initializers

var list = new ArrayList<String>();    // infers ArrayList<String>
var stream = list.stream();            // infers Stream<String>
var path = Paths.get(fileName);        // infers Path
var bytes = Files.readAllBytes(path);  // infers bytes[]

 

(2)用於for each寫法

List<String> myList = Arrays.asList("a", "b", "c");
for (var element : myList) {...}  // infers String

 

(3) 傳統for迴圈

for (var counter = 0; counter < 10; counter++)  {...}   // infers int

 

(4) 嘗試關閉資源(try-with-resources)語法

try (var input = 
     new FileInputStream("validation.txt")) {...}   // infers FileInputStream

 

(5) lambda寫法的形式參數(formal parameter)宣告

(x, y) -> x.process(y) 

     可改寫成

(var x, var y) -> x.process(y)

     但不可寫成

(var x, y) -> x.process(y)      // Cannot mix var and inferred formal parameters
                                // in implicitly typed lambda expressions
(var x, int y) -> x.process(y)  // Cannot mix var and manifest types
                                // in explicitly typed lambda expressions

     即隱含型別Lambda表示式(implicitly typed lambda expression)裡的形式參數們必須都使用var或是都不使用,維持原寫法,

      不在參數前加任何型別。

      此項寫法是在JDK 11後才允許的,詳細描述於 JEP 323或可參考 Java 11 - Local-Variable Syntax for Lambda Parameters (JEP 323)

Illegal Use of var

下面是var的幾個錯誤用法

 

• 沒有initializer:

var n;

 

null初始化變數:

var emptyList = null;

 

不是區域變數

public var = "hello";

 

• Lambda 表示式的目標型態必須明確

var p = (String s) -> s.length() > 10; // error: lambda expression needs an explicit 

 

• 陣列 initializer的目標型態必須明確

var arr = { 1, 2, 3 }; // error: array initializer needs an explicit target-type

 

關於Lambda 表示式和陣列 initializer的等號左側不可用var,可參考Oracle Java 語言架構師Brian Goetz

 

Reader Mail Bag for Thursday (ThuMar 10 15:07:54 UTC 2016) 問題7中的說明。

Conclusion

JDK10後,var識別字的加入,讓我們撰寫code更便利,使用得當,可以令程式更易閱讀。

var的使用僅限於區域變數,須注意使用規則,尤其確保型別是可以被編譯器推斷出來的。

 

水能載舟、亦能覆舟,var使用不當,也可能造成程式難閱讀,進而不好除錯。

關於var宣告區域變數對周遭程式的影響及使用準則可參考 Style Guidelines for Local Variable Type Inference in Java

 

資料來源 :

Oracle : Local Variable Type Inference

Baeldung : Java 10 LocalVariable Type-Inference

郭彥廷