java中已定義類型car_Java 8 習慣用語(8):Java 知道您的類型

215749586_1_20210219095137537.jpeg

Java?8

是第一個支持類型推斷的 Java 版本,而且它僅對 lambda 表達式支持此功能。在 lambda

表達式中使用類型推斷具有強大的作用,它將幫助您做好準備以應對未來的 Java

版本,在今后的版本中還會將類型推斷用于變量等更多可能。這里的訣竅在于恰當地命名參數,相信 Java 編譯器會推斷出剩余的信息。

大多數時候,編譯器完全能夠推斷類型。在它無法推斷出來的時候,就會報錯。

了解 lambda 表達式中的類型推斷的工作原理,至少查看一個無法推斷類型的示例。即使如此,也有解決辦法。

顯式類型和冗余

假設您詢問某個人“您叫什么名字?”,他會回答“我名叫約翰”。這種情況經常發生,但簡單地說“約翰”會更高效。您需要的只是一個名稱,所以該句子的剩余部分都是多余的。

不幸的是,我們總是在代碼中做這類多余的事情。Java 開發人員可以使用 forEach 迭代并輸出某個范圍內的每個值的雙倍值,如下所示:IntStream.rangeClosed(1,?5)

.forEach((int?number)?->?System.out.println(number?*?2));

rangeClosed 方法生成一個從 1 到 5 的 int 值流。lambda 表達式的唯一職責就是接收一個名為 number 的 int 參數,使用PrintStream 的 println 方法輸出該值的雙倍值。從語法上講,該 lambda 表達式沒有錯,但類型細節有些冗余。

Java 8 中的類型推斷

當您從某個數字范圍中提取一個值時,編譯器知道該值的類型為 int。不需要在代碼中顯式聲明該值,盡管這是目前為止的約定。

在 Java 8 中,我們可以丟棄 lambda 表達式中的類型,如下所示:IntStream.rangeClosed(1,?5)

.forEach((number)?->?System.out.println(number?*?2));

由于

Java 是靜態類型語言,它需要在編譯時知道所有對象和變量的類型。在 lambda 表達式的參數列表中省略類型并不會讓 Java

更接近動態類型語言。但是,添加適當的類型推斷功能會讓 Java 更接近其他靜態類型語言,比如 Scala 或 Haskell。

信任編譯器

如果您在 lambda 表達式的一個參數中省略類型,Java 需要通過上下文細節來推斷該類型。

返回到上一個示例,當我們在 IntStream 上調用 forEach 時,編譯器會查找該方法來確定它采用的參數。IntStream 的 forEach 方法期望使用函數接口 IntConsumer,該接口的抽象方法 accept 采用了一個 int 類型的參數并返回 void。

如果在參數列表中指定了該類型,編譯器將會確認該類型符合預期。

如果省略該類型,編譯器會推斷出預期的類型 —在本例中為 int。

無論是您提供類型還是編譯器推斷出該類型,Java 都會在編譯時知道 lambda 表達式參數的類型。要測試這種情況,可以在 lambda 表達式中引入一個錯誤,同時省略參數的類型:IntStream.rangeClosed(1,?5)

.forEach((number)?->?System.out.println(number.length()?*?2));

編譯此代碼時,Java 編譯器會返回以下錯誤:Sample.java:7:?error:?int?cannot?be?dereferenced

.forEach((number)?->?System.out.println(number.length()?*?2));

^1?error

編譯器知道名為 number 的參數的類型。它報錯是因為它無法使用點運算符解除對某個 int 類型的變量的引用。可以對對象執行此操作,但不能對 int 變量這么做。

類型推斷的好處

在 lambda 表達式中省略類型有兩個主要好處:鍵入的內容更少。無需輸入類型信息,因為編譯器自己能輕松確定該類型。

代碼雜質更少 —(number) 比 (int number) 簡單得多。

此外,一般來講,如果我們僅有一個參數,省略類型意味著也可以省略 (),如下所示:IntStream.rangeClosed(1,?5)

.forEach(number?->?System.out.println(number?*?2));

請注意,您將需要為采用多個參數的 lambda 表達式添加括號。

類型推斷和可讀性

lambda 表達式中的類型推斷違背了 Java 中的常規做法,在常規做法中,會指定每個變量和參數的類型。盡管一些開發人員辯稱 Java 指定類型的約定讓代碼變得更可讀、更容易理解,但我認為這種偏好反映出一種習慣而不是必要性。

以一個包含一系列轉換的函數管道為例:List?result?=

cars.stream()

.map((Car?c)?->?c.getRegistration())

.map((String?s)?->?DMVRecords.getOwner(s))

.map((Person?o)?->?o.getName())

.map((String?s)?->?s.toUpperCase())

.collect(toList());

在這里,我們首先提供了一組 Car 實例和相關的注冊信息。我們獲取每輛車的車主和車主姓名,并將該姓名轉換為大寫。最后,將結果放入一個列表中。

這段代碼中的每個 lambda 表達式都為其參數指定了一個類型,但我們為參數使用了單字母變量名。這在 Java 中很常見。但這種做法不合適,因為它丟棄了特定于域的上下文。

我們可以做得比這更好。讓我們看看使用更強大的參數名重寫代碼后發生的情況:List?result?=

cars.stream()

.map((Car?car)?->?car.getRegistration())

.map((String?registration)?->?DMVRecords.getOwner(registration))

.map((Person?owner)?->?owner.getName())

.map((String?name)?->?name.toUpperCase())

.collect(toList());

這些參數名包含了特定于域的信息。我們沒有使用 s 來表示 String,而是指定了特定于域的細節,比如 registration 和name。類似地,我們沒有使用 p 或 o,而是使用 owner 表明 Person 不只是一個人,還是這輛車的車主。

這個示例中的每個 lambda 表達式都比它所取代的表達式更好。在讀取 lambda 表達式(例如 (Person owner) -> owner.getName())時,我們知道我們獲得了車主的姓名,而不只是隨便某個人的姓名。

命名參數

Scala 和 TypeScript 等一些語言更加重視參數名而不是類型。在 Scala 中,我們在定義類型之前定義參數,例如通過編寫:def?getOwner(registration:?String)

而不是:def?getOwner(String?registration)

類型和參數名都很有用,但在 Scala 中,參數名更重要一些。我們用 Java 編寫 lambda 表達式時,也可以考慮這一想法。請注意我們在 Java 中的車輛注冊示例中丟棄類型細節和括號時發生的情況:List?result?=

cars.stream()

.map(car?->?car.getRegistration())

.map(registration?->?DMVRecords.getOwner(registration))

.map(owner?->?owner.getName())

.map(name?->?name.toUpperCase())

.collect(toList());

因為我們添加了描述性的參數名,所以我們沒有丟失太多上下文,而且顯式類型(現在是冗余內容)已悄然消失。結果是我們獲得了更干凈、更樸實的代碼。

類型推斷的局限性

盡管使用類型推斷可以提高效率和可讀性,但這種技術并不適用于所有場合。在某些情況下,完全無法使用類型推斷。幸運的是,您可以依靠 Java 編譯器來獲知何時出現這種情況。

我們首先看一個測試編譯器并獲得成功的示例,然后看一個測試失敗的示例。最重要的是,在兩種情況下,都能夠相信編譯器會按期望方式工作。

擴展類型推斷

在我們的第一個示例中,假設我們想創建一個 Comparator 來比較 Car 實例。我們首先需要一個 Car 類:class?Car?{

public?String?getRegistration()?{?return?null;?}}

接下來,我們將創建一個 Comparator,以便基于 Car 實例的注冊信息對它們進行比較:public?static?Comparator?createComparator()?{

return?comparing((Car?car)?->?car.getRegistration());}

用作 comparing 方法的參數的 lambda 表達式在其參數列表中包含了類型信息。我們知道 Java 編譯器非常擅長類型推斷,那么讓我們看看在省略參數類型的情況下會發生什么,如下所示:public?static?Comparator?createComparator()?{

return?comparing(car?->?car.getRegistration());}

comparing 方法采用了 1 個參數。它期望使用 Function super T, ? extends U> 并返回 Comparator。因為 comparing是 Comparator 上的一個靜態方法,所以編譯器目前沒有關于 T 或 U 可能是什么的線索。

為了解決此問題,編譯器稍微擴展了推斷范圍,將范圍擴大到傳遞給 comparing 方法的參數之外。它觀察我們是如何處理調用comparing 的結果的。根據此信息,編譯器確定我們僅返回該結果。接下來,它看到由 comparing 返回的 Comparator 又作為 Comparator 由 createComparator 返回 。

注意了!編譯器現在已明白我們的意圖:它推斷應該將 T 綁定到 Car。根據此信息,它知道 lambda 表達式中的 car 參數的類型應該為 Car。

在這個例子中,編譯器必須執行一些額外的工作來推斷類型,但它成功了。接下來,讓我們看看在提高挑戰難度,讓編譯器達到其能力極限時,會發生什么。

推斷的局限性

首先,我們在前一個 comparing 調用后面添加了一個新調用。在本例中,我們還為 lambda 表達式的參數重新引入顯式類型:public?static?Comparator?createComparator()?{

return?comparing((Car?car)?->?car.getRegistration()).reversed();}

借助顯式類型,此代碼沒有編譯問題,但現在讓我們丟棄類型信息,看看會發生什么:public?static?Comparator?createComparator()?{

return?comparing(car?->?car.getRegistration()).reversed();}

如您下面所見,進展并不順利。Java 編譯器拋出了錯誤:Sample.java:21:?error:?cannot?find?symbol

return?comparing(car?->?car.getRegistration()).reversed();

^

symbol:???method?getRegistration()

location:?variable?car?of?type?ObjectSample.java:21:?error:?incompatible?types:?Comparator?cannot?be?converted?to?Comparator

return?comparing(car?->?car.getRegistration()).reversed();

^2?errors

像上一個場景一樣,在包含 .reversed() 之前,編譯器會詢問我們將如何處理調用 comparing(car -> car.getRegistration()) 的結果。在上一個示例中,我們以 Comparable 形式返回結果,所以編譯器能推斷出 T 的類型為 Car。

但在修改過后的版本中,我們將傳遞 comparable 的結果作為調用 reversed() 的目標。comparable 返回Comparable,reversed() 沒有展示任何有關 T 的可能含義的額外信息。根據此信息,編譯器推斷 T 的類型肯定是 Object。遺憾的是,此信息對于該代碼而言并不足夠,因為 Object 缺少我們在 lambda 表達式中調用的 getRegistration() 方法。

類型推斷在這一刻失敗了。在這種情況下,編譯器實際上需要一些信息。類型推斷會分析參數、返回元素或賦值元素來確定類型,但在上下文提供的細節不足時,編譯器就會達到其能力極限。

能否采用方法引用作為補救措施?

在我們放棄這種特殊情況之前,讓我們嘗試另一種方法:不使用 lambda 表達式,而是嘗試使用方法引用:public?static?Comparator?createComparator()?{

return?comparing(Car::getRegistration).reversed();}

編譯器對此解決方案非常滿意。它在方法引用中使用 Car:: 來推斷類型。

結束語

Java 8 為 lambda 表達式的參數引入了有限的類型推斷能力,在未來的 Java 版本中,會將類型推斷擴展到局部變量。現在應該學會省略類型細節并信任編譯器,這有助于您輕松步入未來的 Java 環境。

依靠類型推斷和適當命名的參數,編寫簡明、更富于表達且更少雜質的代碼。只要您相信編譯器能自行推斷出類型,就可以使用類型推斷。僅在您確定編譯器確實需要您的幫助的情況下提供類型細節。原作者:Venkat Subramaniam

原文鏈接:Java 8 習慣用語

原出處: IBM Developer

215749586_2_20210219095137787.jpeg

來源:https://www.icode9.com/content-1-863301.html

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/530293.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/530293.shtml
英文地址,請注明出處:http://en.pswp.cn/news/530293.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

ATM柜員機JAVA課程設計_ATM柜員機學年論文設計(Java課程設計)

內容簡介:ATM柜員機學年論文設計(Java課程設計),共23頁,4599字,附源程序。一. 程序介紹3二. 開發環境搭建31. MyEclipse 5.5.1 GA安裝32. MyEclipse Designer 圖形設計插件安裝33. MySQL數據庫安裝4三&…

mysql 結果集什么意思_結果集中的mysql“和”邏輯

假設我有一個類似以下的數據集:table fooid | employeeType | employeeID-------------------------1 | Developer | 12 | Developer | 23 | Developer | 34 | Manager | 15 | Manager | 46 | Manager | 57 | CEO | 18 | CEO | 6我想運行一個查詢,該查詢將返回所有e…

opencv java 去干擾_java - OpenCV Java修補圖像格式要求 - 堆棧內存溢出

一直試圖讓修復工作在Android上進行,int height (int) viewMat.size().height;int width (int) viewMat.size().width;Mat maskMat new Mat();maskMat.create(viewMat.size(), CvType.CV_8U);maskMat.setTo(bColor);Point r1 new Point(width/2-width/10, heigh…

java中 set集合_第8篇 Java中的集合(Set)

Java 集合的 Set 接口Set類型與List類型的區別Set: 無序、不可重復List: 有序、可重復1、HashSetHashSet的存儲結構:HashMap特點:HashSet通過比較存放的哈希碼(hashCode)來確定對象存放的位置當兩個對象的哈希值相等時&#xff0c…

android mysql實現登錄注冊_android簡單登陸和注冊功能實現+SQLite數據庫學習

android簡單登陸和注冊功能實現SQLite數據庫學習發布時間&#xff1a;2018-07-04 17:23,瀏覽次數&#xff1a;1027, 標簽&#xff1a;androidSQLite這里我只是建立了一個用簡單的存儲用戶名和密碼的表單MyDBHelper.java<>public class MyDBHelper extends SQLiteOpenHelp…

java web 來源頁_Java:Java Web--分頁效果

先來看一看分頁的實現原理萬能公式.jpg項目目錄.PNG首先,新建Java Web項目一. 梳理業務邏輯重定向到URL(跳轉到StudentViewAction頁面)//index.jsp頁面1.從頁面接收可變的值2.接收值有問題時,初始化為13.如果沒有問題,把String類型接收值強轉成Integer4.實例DAO方法,調用findSt…

java 瀏覽器 安全_安全策略-IE瀏覽器防黑十大秘籍

1.管理好Cookie在IE6.0中&#xff0c;打開“工具”→“Internet選項”→“隱私”對話框&#xff0c;這里設定了“阻止所有Cookie”、“高”、“中高”、“中”、“低”、“接受所有Cookie”六個級別&#xff0c;你只要拖動滑塊就可以方便地進行設定&#xff0c;而點擊下方的“編…

什么是java中的枚舉法_enum枚舉javajava,enum枚舉使用詳解+,總結

enum 的全稱為 enumeration&#xff0c; 是 JDK 1.5 中引入的新特性&#xff0c;存放在 java.lang 包中。下面是我在使用 enum 過程中的一些經驗和總結。原始的接口定義常量語法(定義)創建枚舉類型要使用 enum 關鍵字&#xff0c;隱含了所創建的類型都是 java.lang.Enum 類的子…

java 審計 漏洞函數_Java Web代碼審計流程與漏洞函數

常見框架與組合常見框架Struts2SpringMVCSpring Boot框架執行流程View層&#xff1a;視圖層Controller層&#xff1a;表現層Service層&#xff1a;業務層Dom層&#xff1a;持久層常見組合SpringStruts2HibernateSpringSpringMVCMybatisSpring BootMybatis代碼審計方法根據業務功…

java前期_【JAVA】前期環境配置

一、java的環境配置及在eclipse中如何安裝JRE或JDK環境eclipse下載地址&#xff1a;JDK下載地址&#xff1a;1)安裝JDK或JRE注&#xff1a;JDK使用與開發者運用&#xff0c;其中包含了開發環境和運行環境。而JRE只包含了java的運行環境。2)配置設置執行路徑UNiX&#xff1a;在C…

php截取指定字符串之后,php截取字符串(截取指定字符串之間的字符串)

一、PHP截取兩個指定字符后邊的字符$a "123abc#456";$b (strpos($a,""));$c (strpos($a,"#"));echo substr($a,$b1,$c-1);二、常用截取字符串技巧。//構造字符串$str "ABCDEFGHIJKLMNOPQRSTUVWXYZ";echo "原字符串&#xff1a;…

php 日志按天截取,Laravel 日志管理:按日期切割日志

日志存儲Laravel 默認的錯誤文件記錄在一個文件里&#xff0c;隨著時間的推移&#xff0c;此文件將會變得巨大&#xff0c;不方便查閱。我們可以通過修改 config/app.php 配置文件中的 log 選項來配置 Laravel 使用的存儲機制。如果你希望每天產生日志都存放在不同的文件中&…

php xcache 方法,php xcache 解密

NO.2 /index.php?actionmd5webcrack 很強大,需要登陸論壇才能解密,點擊右上角的“register”進行注冊,都是一些簡單的單詞,應該能看懂吧?...() A.PHP B.JSP D.Ajax 4.配置 ...假設$aarray(‘x’,’y’);,則$aarray_pad...xcache 15 四個模塊 Admin Common Member Article 用戶…

ecshop category.php?id=4,categoryall.php

//by 瑯琊源碼 QQ:27392236define(IN_ECS, true);require dirname(__FILE__) . /includes/init.php;if ((DEBUG_MODE & 2) ! 2) {$smarty->caching true;}require ROOT_PATH . /includes/lib_area.php;$area_info get_area_info($province_id);$area_id $area_info[r…

php 獲取key的位置,PHP獲取當前所在目錄位置的方法

本文實例講述了PHP獲取當前所在目錄位置的方法。分享給大家供大家參考。具體分析如下&#xff1a;如果要獲取腳本文件的目錄&#xff0c;要應用函數getcwd()來實現。函數聲明如下&#xff1a;string getcwd ( void ) ;成功執行后返回當前目錄字符串&#xff0c;失敗返回FALSE。…

java8收集器,Java 8中的收集器collectionAndThen()方法

collectingAndThen()Java Collectors類中的方法使Collector適應于執行其他完成轉換。它返回執行下游收集器動作的收集器&#xff0c;然后執行附加的結束步驟。語法如下。static Collector collectingAndThen(Collector downstream, Functionfinisher)在這里&#xff0c;參數T-…

php 精度運算,PHP BC 庫(任意精度數字運算) | 網游世界

留意&#xff1a;備選參數$scale以設置運算精度(保留小數位)。bcscale(設置運算精度)bool bcscale ( int $scale )說明&#xff1a;設置運算精度(保留小數位)&#xff0c;成功返回TRUE否則為FALSE。bcadd(加法運算)string bcadd ( string $left_operand , string $right_operan…

php 不允許外部訪問,[日常] 解決mysql不允許外部訪問

1.在端口已經開放的情況下,ubuntu mysql 3306允許遠程訪問vim /etc/mysql/mysql.conf.d/mysqld.cnf注釋#bind-address 127.0.0.12.給用戶授權允許遠程訪問:grant all privileges on *.* to root"%" identified by "pwd" with grant option;flush privileg…

elementary安裝Java,elementary os怎么樣安裝java

elementary os的安裝方法elementary os 的安裝鏡像文件準備好以后&#xff0c;這里以虛擬機上安裝為例&#xff0c;配置好以后啟動虛擬機進入安裝界面。在初始化安裝界面中先選擇“中文簡體”&#xff0c;再點擊“安裝elementary os”按鈕隨后系統會顯示硬件安裝需求界面&#…

java jni框架,Java JNI 簡明教程(一)——傳智播客JNI筆記(王澤佑)

package cn.itcast;public calss TestNative {public native void sayHello();public static void main(String[] arg){}}2. 用javah.exe生成包含native方法的C/C頭文件javah -jni(默認)javah cn.itcast.TestNative //由類名執行生成C/C頭文件生成的頭文件內容&#xff1a;JNIE…