當(dāng)前位置:首頁 > IT技術(shù) > 編程語言 > 正文

Java新特性:數(shù)據(jù)類型可以扔掉了?
2022-02-14 14:20:32

Java新特性 var 局部類型推導(dǎo)


在很久很久以前,我們寫代碼時要慎重的考慮變量的數(shù)據(jù)類型,比如下面這些:

Java新特性:數(shù)據(jù)類型可以扔掉了?_數(shù)據(jù)類型


枚舉:盡管在 JDK 5 中增加了枚舉類型,但是 Class 文件常量池的 CONSTANT_Class_info 類型常量并沒有發(fā)生任何語義變化,仍然是代表一個類或接口的符號引用,沒有加入枚舉,也沒有增加過“CONSTANT_Enum_info”之類的“枚舉符號引用”常量。所以使用 enum 關(guān)鍵字定義常量,盡管從 Java 語法上看起來與使用 class 關(guān)鍵字定義類、使用 interface 關(guān)鍵字定義接口是同一層次的,但實(shí)際上這是由 Javac 編譯器做出來的假象,從字節(jié)碼的角度來看,枚舉僅僅是一個繼承于 java.lang.Enum、自動生成了 values() 和 valueOf() 方法的普通 Java 類而已,因此枚舉也歸為引用類型了。


然而到了 JDK 10 時,我們就有了新的選擇,JDK 10 中新增了 ??var?? 局部變量推斷的功能,使用它我們可以很 happy 的忘記數(shù)據(jù)類型這件事了,那它是如何使用的呢?接下來我們一起來看。

1、使用對比

接下來我們就使用對比的方式,來體會一下 ??var???的作用。

場景一:定義字符串

舊寫法:

String str = "Hello, Java.";

新寫法:

var s = "Hello, Java.";


PS:這里的舊寫法指的是 JDK 10 之前的版本,而新寫法指的是 JDK 10 以后(包含 JDK 10)的版本。


場景二:數(shù)值相加

舊寫法:

int num1 = 111;
double num2 = 555.666d;
double num3 = num1 + num2;
System.out.println(num3);


PS:當(dāng)遇到不同類型相加時(??int???+ ??double???)會發(fā)生數(shù)據(jù)類型向上轉(zhuǎn)型,因此 ??num3????就會升級為 ??double???類型。


新寫法:

var n1 = 111L;
var n2 = 555.666;
var n3 = n1 + n2;
System.out.println(n3);

場景三:集合

舊寫法:

List<Object> list = new ArrayList<>();
list.add("Hello");
list.add("Java");

新寫法:

var list = new ArrayList<>();
list.add("Hello");
list.add("Java");

場景四:循環(huán)

舊寫法:

for (Object item : list) {
System.out.println("item:" + item);
}
for (int i = 0; i < 10; i++) {
// do something...
}

新寫法:

for (var item : list) {
System.out.println("item:" + item);
}
for (var i = 0; i < 10; i++) {
// do something...
}

場景五:配合 Lambda 使用

舊寫法:

List<Object> flist = list.stream().filter(v ->
v.equals("Java")).collect(Collectors.toList());
System.out.println(flist);

新寫法:

var flist = list.stream().filter(v ->
v.equals("Java")).collect(Collectors.toList());
System.out.println(flist);

2、優(yōu)點(diǎn)分析

通過上面的示例我們可以看出, ??var?? 具備兩個明顯的優(yōu)點(diǎn):提高了代碼的可讀性和命名對齊。

① 提高了可讀性

我們在沒有使用 ??var???之前,如果類型的名稱很長就會出現(xiàn)下面的這種情況:

InternationalCustomerOrderProcessor<AnonymousCustomer, SimpleOrder<Book>> orderProcessor = 
createInternationalOrderProcessor(customer, order);

當(dāng)限定每行不能超過 150 個字符的話,變量名就會被推到下一行顯示,這樣整個代碼的可讀性就變得很低。但當(dāng)我們使用了 ??var???之后,代碼就變成了這樣:

var orderProcessor = createInternationalOrderProcessor(customer, order);

從上述的代碼可以看出,當(dāng)類型越長時,??var??(可讀性)的價值就越大。

② 命名對齊

在不使用 ??var???時,當(dāng)遇到下面這種情況,代碼就是這樣的:

// 顯式類型
No no = new No();
AmountIncrease<BigDecimal> more = new BigDecimalAmountIncrease();
HorizontalConnection<LinePosition, LinePosition> jumping =
new HorizontalLinePositionConnection();
Variable variable = new Constant(6);
List<String> names = List.of("Java", "中文社群");

在使用了 ??var???之后,代碼是這樣的:

var no = new No();
var more = new BigDecimalAmountIncrease();
var jumping = new HorizontalLinePositionConnection();
var variable = new Constant(6);
var names = List.of("Java", "中文社群");

從上述代碼可以看出使用了 ??var???之后,命名對齊了,整個代碼也變得更優(yōu)雅了。

3、使用規(guī)則 & 反例

??var????的實(shí)現(xiàn)來自于 JEP 286 (改善提議 286),詳情地址 :??http://openjdk.java.net/jeps/286??

從 JEP 286 的標(biāo)題“局部變量類型推斷”可以看出,??var????只能用于局部變量聲明,也就是說 ??var???必須滿足以下條件:


  • 它只能用于局部變量上;
  • 聲明時必須初始化;
  • 不能用作方法參數(shù)和全局變量(類變量)。


PS:因?yàn)???var???的實(shí)現(xiàn)必須根據(jù)等會右邊的代碼進(jìn)行類型推斷,因此它不能被賦值 null 或不被初始化。


反例一:未初始化和賦值 null

Java新特性:數(shù)據(jù)類型可以扔掉了?_數(shù)據(jù)類型_02

Java新特性:數(shù)據(jù)類型可以扔掉了?_java_03

反例二:中途類型更改

Java新特性:數(shù)據(jù)類型可以扔掉了?_java_04

反例三:全局變量

Java新特性:數(shù)據(jù)類型可以扔掉了?_java_05

反例四:作為返回值

Java新特性:數(shù)據(jù)類型可以扔掉了?_java_06

4、原理分析

經(jīng)過前面的使用我們對 ??var????已經(jīng)有了初步的認(rèn)識,但 ??var???的實(shí)現(xiàn)原理是什么呢?

為了搞清楚它的原理,我們對下面的代碼進(jìn)行了編譯(使用命令 ??javac MainTest.java??):

Java新特性:數(shù)據(jù)類型可以扔掉了?_反例_07

然后我們再用反編譯工具打開被編譯的類發(fā)現(xiàn):??var???竟然被替換成一個個確定的數(shù)據(jù)類型了,如下圖所示:

Java新特性:數(shù)據(jù)類型可以扔掉了?_java_08

由此我們可以得出結(jié)論:??var???關(guān)鍵字的實(shí)現(xiàn)和它的名字密切相關(guān), ??var?? 只是局部類型推斷,它只會在 Java 編碼期和編譯期有效,當(dāng)類被編譯為 class 文件時,??var???就會變成一個個確定的數(shù)據(jù)類型(通過推斷得出)。 所以我們可以把 ??var?? 通俗的理解為 Java 的語法糖,使用它可以讓我們快速優(yōu)雅的實(shí)現(xiàn)業(yè)務(wù)代碼,但 ??var???在字節(jié)碼層面是不存在的。

總結(jié)

本文我們介紹了 ??var???(局部類型推斷)的使用,它可以用在局部變量、 ??for???、 ??Lambda????的變量聲明中,但不能用在全局變量的聲明中,也不能用它作為方法的返回值,并且在聲明時一定要進(jìn)行初始化(也不能賦值為 null)。使用 ??var????可以有效的提高代碼的可讀性和命名對齊,它的實(shí)現(xiàn)原理,是在編譯期通過等號右側(cè)的代碼進(jìn)行類型推斷,然后再將 ??var???替換成確定的數(shù)據(jù)類型。


關(guān)注下面二維碼,訂閱更多精彩內(nèi)容。

Java新特性:數(shù)據(jù)類型可以扔掉了?_反例_09

作者: 王磊的博客


本文摘自 :https://blog.51cto.com/u

開通會員,享受整站包年服務(wù)立即開通 >