詳解Java 10 var關(guān)鍵字和示例教程
關(guān)鍵要點(diǎn)
- Java 10引入了一個(gè)閃亮的新功能:局部變量類型推斷。對(duì)于局部變量,現(xiàn)在可以使用特殊的保留類型名稱“var”代替實(shí)際類型。
- 提供這個(gè)特性是為了增強(qiáng)Java語(yǔ)言,并將類型推斷擴(kuò)展到局部變量的聲明上。這樣可以減少板代碼,同時(shí)仍然保留Java的編譯時(shí)類型檢查。
- 由于編譯器需要通過(guò)檢查賦值等式右側(cè)(RHS)來(lái)推斷var的實(shí)際類型,因此在某些情況下,這個(gè)特性具有局限性,例如在初始化Array和Stream的時(shí)候。
- 如何使用新的“var”來(lái)減少樣板代碼。
在本文中,我將通過(guò)示例介紹新的Java SE 10特性——“var”類型。你將學(xué)習(xí)如何在代碼中正確使用它,以及在什么情況下不能使用它。
介紹
Java 10引入了一個(gè)閃亮的新功能:局部變量類型推斷。對(duì)于局部變量,現(xiàn)在可以使用特殊的保留類型名稱“var”代替實(shí)際類型,如下所示:
var name = “Mohamed Taman”;
提供這個(gè)特性是為了增強(qiáng)Java語(yǔ)言,并將類型推斷擴(kuò)展到局部變量的聲明上。這樣可以減少板代碼,同時(shí)仍然保留Java的編譯時(shí)類型檢查。
由于編譯器需要通過(guò)檢查賦值等式右側(cè)(RHS)來(lái)推斷var的實(shí)際類型,因此在某些情況下,這個(gè)特性具有局限性。我會(huì)在稍后提到這個(gè)問(wèn)題?,F(xiàn)在,讓我們來(lái)看一些簡(jiǎn)單的例子吧。
在開始演示代碼之前,你需要一個(gè)IDE來(lái)體驗(yàn)這些新特性?,F(xiàn)在有很多可選擇的IDE,所以你可以在它們當(dāng)中選擇你喜歡的能夠支持Java SE 10的IDE,比如Apache NetBeans 9、IntelliJ IDEA 2018或最新版本的Eclipse。
就個(gè)人而言,我更喜歡使用交互式的編程工具,可以快速學(xué)習(xí)Java語(yǔ)言語(yǔ)法,了解新的Java API及其特性,甚至用來(lái)進(jìn)行復(fù)雜代碼的原型設(shè)計(jì)。這與枯燥的編輯、編譯和執(zhí)行代碼的繁瑣過(guò)程不太一樣:
- 寫一個(gè)完整的程序;
- 編譯并修復(fù)錯(cuò)誤;
- 運(yùn)行程序;
- 弄清楚它有什么問(wèn)題;
- 修改;
- 重復(fù)這個(gè)過(guò)程。
除了IDE之外,現(xiàn)在還可以使用從Java SE 9以就隨ava SE JDK一起發(fā)布的JShell。
什么是JShell
現(xiàn)在,Java有了自己的REPL(Read-Evaluate-Print-Loop)實(shí)現(xiàn)JShell(Java Shell),作為交互式的編程環(huán)境。那么,它有什么神奇的地方?JShell提供了一個(gè)快速友好的環(huán)境,讓你能夠快速探索、發(fā)現(xiàn)和試驗(yàn)Java語(yǔ)言特性及其豐富的庫(kù)。
在JShell中,你可以一次輸入一個(gè)程序元素,并可以立即看到結(jié)果,然后根據(jù)需要對(duì)代碼做出調(diào)整。因此,JShell用它的Read-Evaluate-Print循環(huán)取代了編輯、編譯和執(zhí)行的繁瑣過(guò)程。在JShell中,你不需要編寫完整的程序,只需要編寫JShell命令和Java代碼片段即可。
當(dāng)你輸入代碼段時(shí),JShell會(huì)立即讀取、執(zhí)行并打印結(jié)果,然后準(zhǔn)備好執(zhí)行下一個(gè)代碼片段。因此,JShell的即時(shí)反饋可以讓你保持注意力,提高你的效率,并加快學(xué)習(xí)和軟件開發(fā)過(guò)程。
對(duì)JShell的介紹就到此為止(InfoQ最近對(duì)這個(gè)工具進(jìn)行過(guò)全面介紹)。為了深入了解JShell的功能,我錄制了一套視頻教程“Hands-on Java 10 Programming with JShell”,可以幫助你掌握J(rèn)Shell,可以從Packt或Udemy訪問(wèn)這些教程。
現(xiàn)在,讓我們通過(guò)一些簡(jiǎn)單的示例(使用JShell)來(lái)了解這個(gè)新的var類型能做些什么。
必備軟件
為了能用上JShell,我假設(shè)你安裝了Java SE或JDK 10+,并且JDK的bin目錄已經(jīng)加入到系統(tǒng)路徑中。如果還沒(méi)有安裝,可以在這里下載JDK 10+ 最新版本。
啟動(dòng)JShell會(huì)話
- 在Windows上,打開命令提示符,輸入jshell并按回車鍵。
- 在Linux上,打開一個(gè)shell窗口,輸入jshell并按回車鍵。
- 在macOS(以前稱為OS X)上,打開終端窗口,輸入“jshell”并按回車鍵。
這個(gè)命令會(huì)啟動(dòng)一個(gè)新的JShell會(huì)話,并顯示這個(gè)消息:
| Welcome to JShell -- Version 10.0.1 | For an introduction type: /help intro jshell>
使用“var”類型
現(xiàn)在你已經(jīng)安裝了JDK 10,現(xiàn)在讓我們開始玩JShell。我們直接跳到終端,通過(guò)示例來(lái)了解var類型。只需在jshell提示符下輸入我接下來(lái)要介紹的每個(gè)代碼片段,我會(huì)把結(jié)果留給你作為練習(xí)。如果你稍微有瞄過(guò)一兩眼在代碼,你會(huì)注意到它們看起來(lái)好像是錯(cuò)的,因?yàn)楫?dāng)中沒(méi)有分號(hào)。你可以試試看,看看能不能運(yùn)行。
簡(jiǎn)單的類型推理
這是var類型的基本用法,在下面的示例中,編譯器可以將RHS推斷為String字面量:
var name = "Mohamed Taman"
var lastName = str.substring(8)
System.out.println("Value: "+lastName +" ,and type is: "+ lastName.getClass().getTypeName())
這里不需要分號(hào),因?yàn)镴Shell是一個(gè)交互式環(huán)境。只有當(dāng)同一行代碼有多個(gè)語(yǔ)句或一個(gè)類型聲明或方法聲明中有多個(gè)語(yǔ)句時(shí)才需要分號(hào),你將在后面的示例中看到。
var類型和繼承
在使用var時(shí),多態(tài)仍然有效。在繼承的世界中,var類型的子類型可以像平常一樣賦值給超類型的var類型,如下所示:
import javax.swing.*
var password = new JPasswordField("Password text")
String.valueOf(password.getPassword()) // // 將密碼的字符數(shù)組轉(zhuǎn)換成字符串
var textField = new JTextField("Hello text")
textField = password
textField.getText()
但不能將超類型var賦值給子類型var,如下所示:
password = textField
這是因?yàn)镴PasswordField是JTextField的子類。
var和編譯時(shí)安全性
如果出現(xiàn)錯(cuò)誤的賦值操作會(huì)怎樣?不兼容的變量類型不能相互賦值。一旦編譯器推斷出實(shí)際類型的var,就不能將錯(cuò)誤的值賦值給它,如下所示:
var number = 10 number = "InfoQ"
這里發(fā)生了什么?編譯器將“var number = 10”替換為“int number = 10”,所以仍然可以保證安全性。
var與集合和泛型
現(xiàn)在讓我們來(lái)看看var與集合和泛型一起使用時(shí)如何進(jìn)行類型推斷。我們先從集合開始。在下面的情況中,編譯器可以推斷出集合元素的類型是什么:
var list = List.of(10);
這里沒(méi)有必要進(jìn)行類型轉(zhuǎn)換,因?yàn)榫幾g器已經(jīng)推斷出正確的元素類型為int。
int i = list.get(0); //等效于: var i = list.get(0);
下面的情況就不一樣了,編譯器只會(huì)將其作為對(duì)象集合(而不是整數(shù)),因?yàn)樵谑褂昧庑芜\(yùn)算符時(shí),Java需要LHS(左側(cè))的類型來(lái)推斷RHS的類型:
var list2 = new ArrayList<>(); list2.add(10); list2 int i = list2.get(0) //編譯錯(cuò)誤 int i = (int) list2.get(0) //需要進(jìn)行轉(zhuǎn)換,獲得int
對(duì)于泛型,最好在RHS使用特定類型(而不是菱形運(yùn)算符),如下所示:
var list3 = new ArrayList<Integer>(); list3.add(10); System.out.println(list3) int i = list3.get(0)
for循環(huán)中的var類型
讓我們先來(lái)看看基于索引的For循環(huán):
for (var x = 1; x <= 5; x++) {
var m = x * 2; //等效于: int m = x * 2;
System.out.println(m);
}
下面是在For Each循環(huán)中:
var list = Arrays.asList(1,2,3,4,5,6,7,8,9,10)
for (var item : list) {
var m = item + 2;
System.out.println(m);
}
現(xiàn)在我有一個(gè)問(wèn)題,var是否適用于Java 8 Stream?讓我們看看下面的例子:
var list = List.of(1, 2, 3, 4, 5, 6, 7) var stream = list.stream() stream.filter(x -> x % 2 == 0).forEach(System.out::println)
var類型和三元運(yùn)算符
那么三元運(yùn)算符呢?
var x = 1 > 0 ? 10 : -10 int i = x
現(xiàn)在,如果在三元運(yùn)算符的RHS中使用不同類型的操作數(shù)會(huì)怎樣?讓我們來(lái)看看:
var x = 1 > 0 ? 10 : "Less than zero"; System.out.println(x.getClass()) //Integer var x = 1 < 0 ? 10 : "Less than zero"; System.out.println(x.getClass()) // String
這兩個(gè)例子是否可以說(shuō)明var的類型是在運(yùn)行時(shí)決定的?絕對(duì)不是!讓我們以舊方式實(shí)現(xiàn)同樣的邏輯:
Serializable x = 1 < 0 ? 10 : "Less than zero"; System.out.println(x.getClass())
Serializable是其中兩個(gè)操作數(shù)最具兼容性和最專的有類型(最不專有的類型是java.lang.Object)。
String和Integer都實(shí)現(xiàn)了Serializable。Integer從int自動(dòng)裝箱。換句話說(shuō),Serializable是兩個(gè)操作數(shù)的LUB(最小上限)。所以,這表明往前數(shù)第三個(gè)例子中的var類型也是Serializable。
讓我們轉(zhuǎn)到另一個(gè)主題:將var類型傳給方法。
var類型與方法
我們先聲明一個(gè)名為squareOf的方法,這個(gè)方法的參數(shù)為BigDecimal類型,并返回參數(shù)的平方,如下所示:
BigDecimal squareOf(BigDecimal number) {
var result= number.multiply(number);
return result;
}
var number = new BigDecimal("2.5")
number = squareOf(number)
現(xiàn)在讓我們看看它如何與泛型一起使用。我們聲明一個(gè)名為toIntgerList的方法,參數(shù)類型為L(zhǎng)ist<T>(泛型類型),并使用Streams API返回一個(gè)整數(shù)列表,如下所示:
<T extends Number> List<Integer> toIntgerList(List<T> numbers) {
var integers = numbers.stream()
.map(Number::intValue)
.collect(Collectors.toList());
return integers;
}
var numbers = List.of(1.1, 2.2, 3.3, 4.4, 5.5)
var integers = toIntgerList(numbers)
var類型與匿名類
最后,讓我們看一下var和匿名類。我們通過(guò)實(shí)現(xiàn)Runnable接口來(lái)使用線程,如下所示:
<T extends Number> List<Integer> toIntgerList(List<T> numbers) {
var integers = numbers.stream()
.map(Number::intValue)
.collect(Collectors.toList());
return integers;
}
var numbers = List.of(1.1, 2.2, 3.3, 4.4, 5.5)
var integers = toIntgerList(numbers)
到目前為止,我已經(jīng)介紹了Java 10的新特性——“var”類型,它減少了樣板編碼,同時(shí)保持了Java的編譯時(shí)類型檢查。我還通過(guò)實(shí)例說(shuō)明了可以用它做些什么。接下來(lái),你將了解var類型的局限性以及不能將它用在哪些地方。
var message = "running..." //effectively final
var runner = new Runnable(){
@Override
public void run() {
System.out.println(message);
}}
runner.run()
“var”的局限性
接下來(lái),你將看一些示例,以便了解var類型功能無(wú)法做到的事情。
jshell提示符將會(huì)告訴你代碼出了什么問(wèn)題,你可以利用這些交互式的即時(shí)反饋。
應(yīng)該要進(jìn)行初始化
第一個(gè)也是最簡(jiǎn)單的原則就是不允許沒(méi)有初始值的變量。
var name;
你將得到一個(gè)編譯錯(cuò)誤,因?yàn)榫幾g器無(wú)法推斷這個(gè)局部變量x的類型。
不允許復(fù)合聲明
嘗試運(yùn)行這行代碼:
var x = 1, y = 3, z = 4
你將得到一個(gè)錯(cuò)誤消息:復(fù)合聲明中不允許使用'var'。
不支持確定性賦值(Definite Assignment)
嘗試創(chuàng)建一個(gè)名為testVar的方法,如下所示,將下面的代碼復(fù)制并粘貼到JShell中:
void testVar(boolean b)
{
var x; if (b)
{
x = 1;
}
else
{
x = 2;
}
System.out.println(x);
}
方法不會(huì)被創(chuàng)建,而是會(huì)拋出編譯錯(cuò)誤。因?yàn)闆](méi)有設(shè)置初始值,所以不能使用'var'。
null賦值
不允許進(jìn)行null賦值,如下所示:
var name = null;
這將拋出異?!皏ariable initializer is 'null'”。因?yàn)閚ull不是一個(gè)類型。
與Lambda一起使用
另一個(gè)例子,沒(méi)有Lambda初始化器。這與菱形操作符那個(gè)示例一樣,RHS需要依賴LHS的類型推斷。
var runnable = () ->
{
}
將拋出異常:“l(fā)ambda expression needs an explicit target-type”。
var和方法引用
沒(méi)有方法引用初始值,類似于Lambda和菱形運(yùn)算符示例:
var abs = BigDecimal::abs
將拋出異常:“method reference needs an explicit target-type”。
var和數(shù)組初始化
并非所有數(shù)組初始化都有效,讓我們看看什么時(shí)候var與[]不起作用:
var numbers[] = new int[]
{
2, 4, 6
}
以下也不起作用:
var numbers =
{
2, 4, 6
}
拋出的錯(cuò)誤是: “array initializer needs an explicit target-type”。
就像上一個(gè)例子一樣,var和[]不能同時(shí)用在LHS一邊:
var numbers[] =
{
2, 4, 6
}
錯(cuò)誤: 'var' is not allowed as an element type of an array。
只有以下數(shù)組初始化是有效的:
var numbers = new int[]
{
2, 4, 6
}
var number = numbers[1]number = number + 3
不允許使用var字段
class Clazz
{
private var name;
}
不允許使用var方法參數(shù)
void doAwesomeStuffHere(var salary)
{
}
不能將var作為方法返回類型
var getAwesomeStuff()
{
return salary;
}
catch子句中不能使用var
try
{
Files.readAllBytes(Paths.get("c:\temp\temp.txt"));
}
catch (var e)
{
}
在編譯時(shí)var類型究竟發(fā)生了什么?
“var”實(shí)際上只是一個(gè)語(yǔ)法糖,并且它不會(huì)在編譯的字節(jié)碼中引入任何新的結(jié)構(gòu),在運(yùn)行期間,JVM也沒(méi)有為它們提供任何特殊的指令。
結(jié)論
在這篇文章中,我介紹了“var”類型是什么以及它如何減少樣板編碼,同時(shí)保持Java的編譯時(shí)類型檢查。
然后,你了解了新的JShell工具,即Java的REPL實(shí)現(xiàn),它可以幫助你快速學(xué)習(xí)Java語(yǔ)言,并探索新的Java API及其功能。你還可以使用JShell對(duì)復(fù)雜代碼進(jìn)行原型設(shè)計(jì),而不是重復(fù)編輯、編譯和執(zhí)行的傳統(tǒng)繁瑣流程。
最后,你了解了所有var類型的功能和限制,例如什么時(shí)候可以和不可以使用var。寫這篇文章很有意思,所以我希望你喜歡它并能給你帶來(lái)幫助。
其他資源
- JDK 10 Documentation
- Hands-on Java 10 Programming with JShell .
- Getting Started with Clean Code Java SE 9 .
- Overview of JDK 10 and JRE 10 Installation .
- JEP 286: Local-Variable Type Inference .
- Definite Assignment
總結(jié)
以上所述是小編給大家介紹的詳解Java 10 var關(guān)鍵字和示例教程,希望對(duì)大家有所幫助,如果大家有任何疑問(wèn)請(qǐng)給我留言,小編會(huì)及時(shí)回復(fù)大家的。在此也非常感謝大家對(duì)腳本之家網(wǎng)站的支持!
相關(guān)文章
從源碼角度看spring mvc的請(qǐng)求處理過(guò)程
這篇文章主要介紹了從源碼角度看spring mvc的請(qǐng)求處理過(guò)程,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,,需要的朋友可以參考下2019-06-06
JAVA中使用FTPClient實(shí)現(xiàn)文件上傳下載實(shí)例代碼
本文給大家介紹如何利用jakarta commons中的FTPClient(在commons-net包中)實(shí)現(xiàn)上傳下載文件。非常不錯(cuò)具有參考借鑒價(jià)值,感興趣的朋友一起學(xué)習(xí)吧2016-06-06
Spring Boot集成Druid出現(xiàn)異常報(bào)錯(cuò)的原因及解決
Druid 可以很好的監(jiān)控 DB 池連接和 SQL 的執(zhí)行情況,天生就是針對(duì)監(jiān)控而生的 DB 連接池。本文講述了Spring Boot集成Druid項(xiàng)目中discard long time none received connection異常的解決方法,出現(xiàn)此問(wèn)題的同學(xué)可以參考下2021-05-05
Java的Struts框架中配置國(guó)際化的資源存儲(chǔ)的要點(diǎn)解析
這篇文章主要介紹了Java的Struts框架中配置國(guó)際化的資源存儲(chǔ)的要點(diǎn)解析,針對(duì)用戶所使用的語(yǔ)言來(lái)配置資源文件,需要的朋友可以參考下2016-04-04
SpringBoot中多個(gè)實(shí)現(xiàn)的接口正確注入的六種方式
在SpringBoot中,正確注入多個(gè)接口實(shí)現(xiàn)包括使用@Autowired和@Qualifier、@Resource注解、構(gòu)造方法注入、@Primary注解、Java配置類以及將所有實(shí)現(xiàn)注入到List或Map中,感興趣的可以了解一下2024-10-10
org.springframework.beans.BeanInstantiationException異常解決
本文主要介紹了org.springframework.beans.BeanInstantiationException異常解決,大多數(shù)情況下,這個(gè)異常是由于簡(jiǎn)單的配置錯(cuò)誤或者代碼問(wèn)題導(dǎo)致的,下面就來(lái)具體解決一下2024-03-03

