主頁 > 移動端開發 > 為什么java中的函式式API不處理已檢查的例外?

為什么java中的函式式API不處理已檢查的例外?

2022-10-11 06:13:26 移動端開發

我多次看到,當我們必須處理已檢查的例外時,在 java 中使用函式式 API 非常冗長且容易出錯。

例如:撰寫(更容易閱讀)代碼真的很方便

var obj = Objects.requireNonNullElseGet(something, Other::get);

實際上,它還避免了對 getter 的不當多次呼叫,例如當您這樣做時

var obj = something.get() != null ? something.get() : other.get();
//        ^^^^ first ^^^^          ^^^^ second ^^^^

但是當你必須處理檢查例外時,一切都變成了叢林,我有時會看到這種非常丑陋的代碼風格:

try {
  Objects.requireNonNullElseGet(obj, () -> {
    try {
      return invokeMethodWhichThrows();
    } catch (Exception e) {
      throw new RuntimeException(e);
    }
  });
} catch (RuntimeException r){
  Throwable cause = r.getCause();
  if(cause == null)
    throw r;
  else
    throw cause;
}

唯一的目的是處理檢查的例外,例如在沒有 lambda 的情況下撰寫代碼。現在,我知道這些情況可以用三元運算子和一個保存結果的變數更好地表達something.get(),但對于JDKObjects.requireNonNullElse(a, b)java.util包中的 JDK 也是如此。

對于將供應商作為引數并僅在需要時評估它們的日志記錄框架的方法也可以這樣說,但是如果您需要處理這些供應商中的已檢查例外,則需要呼叫它們并顯式檢查日志級別。

if(LOGGER.isDebugEnabled())
  LOGGER.debug("request from "   resolveIPOrThrow());

一些類似的理由也可以是 s 的女仆Future,但讓我繼續。

我的問題是:為什么 Java 中的功能 API 不處理檢查例外?

例如ThrowingSupplier,像下面這樣的介面,可以潛在地滿足處理檢查例外、保證型別一致性和更好的代碼可讀性的需求。

interface ThrowingSupplier<O, T extends Exception> {
  O get() throws T;
}

然后,我們需要復制使用 Suppliers 的方法以具有使用 ThrowingSuppliers 并引發例外的多載。但是我們作為 Java 開發人員已經習慣了這種重復(比如 with Stream, IntStream, LongStream, 或帶有多載的方法來處理int[], char[], long[], byte[], ...),所以這對我們來說并沒有什么奇怪的。

如果對 JDK 有深入了解的人爭論為什么檢查例外已從功能 API 中排除,我將非常感激,如果有辦法合并它們。

uj5u.com熱心網友回復:

這個問題可以解釋為“為什么做出這個決定的人會這樣決定”,它是在問:“請總結 5 年的嚴肅辯論——特別是 Brian Goetz 和他的想法”,這是不可能的,除非你名字是布賴恩·戈茨。據我所知,他沒有回答關于 SO 的問題。如果你愿意,你可以在 lambda-dev 郵件串列的 de 檔案中探索。

不過,人們可以做出明智的猜測。

范圍內與范圍外

lambdas沒有3 種透明度。

  1. 控制流。
  2. 檢查例外。
  3. 可變的區域變數。

控制流透明度

以這段代碼為例:

private Map<String, PhoneNumber> phonebook = ...;

public PhoneNumber findPhoneNumberOf(String personName) {
  phonebook.entrySet().stream().forEach(entry -> {
    if (entry.getKey().equals(personName)) return entry.getValue();
  });
  return null;
}

這段代碼很愚蠢(為什么不只做 a .get,或者如果我們必須通過這個東西,為什么不使用.filterand .findFirst,但如果你看過去,它甚至不起作用:你不能return從那個 lambda 中使用方法。那個return宣告回傳 lambda (因此是編譯器錯誤,您傳遞給的 lambdaforEach回傳void)。您也不能continuebreak從其內部的 lambda 之外的回圈。

與可以做得很好的 for 回圈相比:

for (var entry : phonebook.entrySet()) {
  if (entry.getKey().equals(personName)) return entry.getValue();
}
return null;

完全按照您的想法做,并且作業正常。

檢查例外透明度

這就是你抱怨的那個。這不編譯:

public void printFiles(Path... files) throws IOException {
  Arrays.stream(files).forEach(p -> System.out.println(Files.readString(p)));
}

背景關系允許您拋出 IOExceptions 的事實無濟于事:以上內容無法編譯,因為“可以拋出 IOExceptions”作為狀態不會“轉移”到 lambda 內部。

這里有一個主題:將其重寫為普通的 for 回圈,它可以按照您想要的方式精確編譯和作業。那么,究竟為什么我們不能讓 lambda 以同樣的方式作業呢?

可變區域變數

這不起作用:

int x = 0;
someList.stream().forEach(k -> x  );
System.out.println("Count: "   x);

您既不能修改在 lambda 之外宣告的區域變數,也不能讀取它們,除非它們是(有效地)final為什么不?

這些都是東西..取決于范圍分層

到目前為止,lambda 在這三個方面不透明似乎真的很愚蠢。但在稍微不同的背景下,它變成了一件好事。想象一下,而不是.stream().forEach有點不同的東西:

class DoubleNullException extends Exception {} // checked!

public class Example {
  private TreeSet<String> words;

  public Example() throws DoubleNullException {
    int comparisonCount = 0;
    this.words = new TreeSet<String>((a, b) -> {
      comparisonCount  ;
      if (a == null && b == null) throw new DoubleNullException();
    });
    System.out.println("Comparisons performed: "   comparisonCount);
  }
}

讓我們想象一下這 3 張透明膠片確實有效。上面的代碼使用了其中的兩個(嘗試 mutate comparisonCount,并嘗試DoubleNullException從內到外拋出)。

上面的代碼完全沒有意義編譯器錯誤是非常需要的該比較器可能要到下周才能在完全不同的執行緒中運行。每當您將第二個元素添加到集合中時,它就會運行,這是一個欄位,所以誰知道誰將執行此操作以及哪個執行緒將執行此操作。建構式早已停止運行 - 本地變數“在堆疊上”,因此本地變數已經消失沒關系,這里的列印總是會列印 'comparisons made: 0',陳述句 'comparisonCount :' 將試圖增加一個不再保存該變數的記憶體位置。

即使我們“修復”了這個問題(編譯器意識到在 lambda 中使用了 local 并將其提升到堆上,這是大多數其他語言所做的),代碼作為一個概念仍然沒有意義:那個 print 陳述句不會列印。此外,可以從多個執行緒呼叫該比較器,所以......我們現在允許volatile在我們的本地變數上嗎?相當的蠕蟲罐頭!在當前的 java 中,區域變數不可能遇到執行緒并發同步問題,因為不可能與另一個執行緒共享變數(您可以共享變數指向的物件,而不是變數本身)。

您被允許(有效地)與最終本地人混淆的原因是因為您可以制作一個副本,這就是編譯器為您所做的。副本很好 - 如果沒有人改變任何東西。

該例外同樣不起作用:呼叫的代碼thatSet.add(someElement)將獲得DoubleNullException. 有人寫道:

Example ex;
try {
  ex = new Example();
} catch (DoubleNullException e) {
  throw new WrappedEx(e);
}

ex.add(null);
ex.add(null); // BOOM

帶有注釋 ( BOOM) 的行將拋出 DoubleNullEx。它“破壞”了檢查的例外規則:該行將編譯(set.add不拋出 DNEx),但不在允許拋出 DNEx 的背景關系中。上述代碼段中的 catch 塊永遠無法運行。

看看這一切是如何分崩離析的,沒有任何意義?

關鍵線索是:lambda 會發生什么它是“運輸”的嗎?

在某些情況下,您將 lambda 直接交給一個方法,并且該方法具有“使用它并失去它”的心態:您將 lambda 交給該方法將運行它 0、1 或多次,但關鍵是:它會立即運行它,一旦您將 lambda 交給回傳的方法,該 lambda 就消失了您將 lambda 交給的東西沒有將其存盤在欄位中或將其交給將其存盤在欄位中的其他代碼,該方法也沒有將 lambda 傳輸到另一個執行緒。

在這種情況下(方法是使用它然后丟失它),透明膠片肯定會很方便并且不會“破壞”任何東西。

但是,當您將 lambda 傳遞給的方法確實將其傳輸到一個欄位時(例如,其建構式TreeSet將傳遞的比較器存盤在一個欄位中,以便將來的.add呼叫可以呼叫它),透明度會分解并且沒有任何意義。

Java 中的 Lambda 兩者都適用,因此缺乏透明度(在所有 3 個方面)實際上是有道理的當你有一個使用它然后失去它的情況時,這很煩人。

潛在的未來 JAVA 修復:我以前支持過它,但到目前為止,它大多被置若罔聞。下次我見到布賴恩時,我可能會再次提起它。想象一下,您可以將注釋或其他標記粘貼在方法的引數上,上面寫著:“我將使用它或丟失它”。然后編譯器將確保您不傳輸它(編譯器讓您使用該引數做的唯一事情就是呼叫.invoke()它。您不能呼叫其他任何東西,也不能將它分配或交給其他任何東西,除非您手它到一個方法,該方法也將該引數標記為@UseItOrLoseIt。然后編譯器可以通過對控制流和已檢查例外流進行一些戰術包裝來實作透明度,只需不抱怨(已檢查例外是javac的想象。運行時沒有檢查例外。這就是為什么 scala、kotlin 和其他運行在 JVM 上的語言可以做到這一點)。

實際上他們可以!

正如你的問題以 - 你實際上可以寫O get() throws T那么為什么各種功能介面,比如Supplier,不這樣做呢?

主要是因為它很痛苦。老實說,我不確定為什么例如串列的 forEach未定義為:

public <T extends Throwable> forEach(ThrowingConsumer<? super E, ? super T> consumer) throws T {
  for (E elem : this) consumer.consume(elem);
}

哪個可以正常作業并編譯(ThrowingConsumer具有明顯的 impl)。或者甚至Consumer像我們所擁有的那樣與<O, T extends Exception>零件一起宣告。

這有點麻煩。lambdas“作業”的方式是編譯器必須從背景關系中推斷出你正在實作的功能介面,特別是必須系結所有泛型。將例外系結添加到這種組合會使其變得更加困難。如果您正在“拋出 lambda” 中撰寫代碼并開始大量使用紅色下劃線,并且自動完成等沒有幫助,IDE 往往會有點困惑,因為 IDE 不能在它知道之前在這種情況下很有用。

Lambdas 作為一個系統也被設計為向后兼容地替換該概念的任何現有用法,例如 swing 的ActionListener. 這樣的偵聽器也不能拋出,因此使java.util.function包中的介面相似會更熟悉,并且可能更符合 java 習慣。

throws T解決方案會有所幫助,但不是靈丹妙藥。它在一定程度上解決了檢查例外透明度的缺乏,但對于解決可變區域變數透明度或控制流透明度沒有任何作用。也許結論很簡單:這樣做的好處比你想象的要有限,成本比你想象的要高。成本/收益分析說:壞主意,所以沒有完成。

轉載請註明出處,本文鏈接:https://www.uj5u.com/yidong/512973.html

標籤:爪哇例外功能接口

上一篇:python3.x-如何從類創建中回傳None

下一篇:確定例外原因

標籤雲
其他(157675) Python(38076) JavaScript(25376) Java(17977) C(15215) 區塊鏈(8255) C#(7972) AI(7469) 爪哇(7425) MySQL(7132) html(6777) 基礎類(6313) sql(6102) 熊猫(6058) PHP(5869) 数组(5741) R(5409) Linux(5327) 反应(5209) 腳本語言(PerlPython)(5129) 非技術區(4971) Android(4554) 数据框(4311) css(4259) 节点.js(4032) C語言(3288) json(3245) 列表(3129) 扑(3119) C++語言(3117) 安卓(2998) 打字稿(2995) VBA(2789) Java相關(2746) 疑難問題(2699) 细绳(2522) 單片機工控(2479) iOS(2429) ASP.NET(2402) MongoDB(2323) 麻木的(2285) 正则表达式(2254) 字典(2211) 循环(2198) 迅速(2185) 擅长(2169) 镖(2155) 功能(1967) .NET技术(1958) Web開發(1951) python-3.x(1918) HtmlCss(1915) 弹簧靴(1913) C++(1909) xml(1889) PostgreSQL(1872) .NETCore(1853) 谷歌表格(1846) Unity3D(1843) for循环(1842)

熱門瀏覽
  • 【從零開始擼一個App】Dagger2

    Dagger2是一個IOC框架,一般用于Android平臺,第一次接觸的朋友,一定會被搞得暈頭轉向。它延續了Java平臺Spring框架代碼碎片化,注解滿天飛的傳統。嘗試將各處代碼片段串聯起來,理清思緒,真不是件容易的事。更不用說還有各版本細微的差別。 與Spring不同的是,Spring是通過反射 ......

    uj5u.com 2020-09-10 06:57:59 more
  • Flutter Weekly Issue 66

    新聞 Flutter 季度調研結果分享 教程 Flutter+FaaS一體化任務編排的思考與設計 詳解Dart中如何通過注解生成代碼 GitHub 用對了嗎?Flutter 團隊分享如何管理大型開源專案 插件 flutter-bubble-tab-indicator A Flutter librar ......

    uj5u.com 2020-09-10 06:58:52 more
  • Proguard 常用規則

    介紹 Proguard 入口,如何查看輸出,如何使用 keep 設定入口以及使用實體,如何配置壓縮,混淆,校驗等規則。

    ......

    uj5u.com 2020-09-10 06:59:00 more
  • Android 開發技術周報 Issue#292

    新聞 Android即將獲得類AirDrop功能:可向附近設備快速分享檔案 谷歌為安卓檔案管理應用引入可安全隱藏資料的Safe Folder功能 Android TV新主界面將顯示電影、電視節目和應用推薦內容 泄露的Android檔案暗示了傳說中的谷歌Pixel 5a與折疊屏新機 谷歌發布Andro ......

    uj5u.com 2020-09-10 07:00:37 more
  • AutoFitTextureView Error inflating class

    報錯: Binary XML file line #0: Binary XML file line #0: Error inflating class xxx.AutoFitTextureView 解決: <com.example.testy2.AutoFitTextureView android: ......

    uj5u.com 2020-09-10 07:00:41 more
  • 根據Uri,Cursor沒有獲取到對應的屬性

    Android: 背景:呼叫攝像頭,拍攝視頻,指定保存的地址,但是回傳的Cursor檔案,只有名稱和大小的屬性,沒有其他諸如時長,連ID屬性都沒有 使用 cursor.getInt(cursor.getColumnIndexOrThrow(MediaStore.Video.Media.DURATIO ......

    uj5u.com 2020-09-10 07:00:44 more
  • Android連載29-持久化技術

    一、持久化技術 我們平時所使用的APP產生的資料,在記憶體中都是瞬時的,會隨著斷電、關機等丟失資料,因此android系統采用了持久化技術,用于存盤這些“瞬時”資料 持久化技術包括:檔案存盤、SharedPreference存盤以及資料庫存盤,還有更復雜的SD卡記憶體儲。 二、檔案存盤 最基本存盤方式, ......

    uj5u.com 2020-09-10 07:00:47 more
  • Android Camera2Video整合到自己專案里

    背景: Android專案里呼叫攝像頭拍攝視頻,原本使用的 MediaStore.ACTION_VIDEO_CAPTURE, 后來因專案需要,改成了camera2 1.Camera2Video 官方demo有點問題,下載后,不能直接整合到專案 問題1.多次拍攝視頻崩潰 問題2.雙擊record按鈕, ......

    uj5u.com 2020-09-10 07:00:50 more
  • Android 開發技術周報 Issue#293

    新聞 谷歌為Android TV開發者提供多種新功能 Android 11將自動填表功能整合到鍵盤輸入建議中 谷歌宣布Android Auto即將支持更多的導航和數字停車應用 谷歌Pixel 5只有XL版本 搭載驍龍765G且將比Pixel 4更便宜 [圖]Wear OS將迎來重磅更新:應用啟動時間 ......

    uj5u.com 2020-09-10 07:01:38 more
  • 海豚星空掃碼投屏 Android 接收端 SDK 集成 六步驟

    掃碼投屏,開放網路,獨占設備,不需要額外下載軟體,微信掃碼,發現設備。支持標準DLNA協議,支持倍速播放。視頻,音頻,圖片投屏。好點意思。還支持自定義基于 DLNA 擴展的操作動作。好像要收費,沒體驗。 這里簡單記錄一下集成程序。 一 跟目錄的build.gradle添加私有mevan倉庫 mave ......

    uj5u.com 2020-09-10 07:01:43 more
最新发布
  • 歡迎頁輪播影片

    如圖,引導開始,球從上落下,同時淡入文字,然后文字開始輪播,最后一頁時停止,點擊進入首頁。 在來看看效果圖。 重力球先不講,主要歡迎輪播簡單實作 首先新建一個類 TextTranslationXGuideView,用于影片展示 文本是類似的,最后會有個圖片箭頭影片,布局很簡單,就是一個 TextVi ......

    uj5u.com 2023-04-20 08:40:31 more
  • 【FAQ】關于華為推送服務因營銷訊息頻次管控導致服務通訊類訊息

    一. 問題描述 使用華為推送服務下發IM訊息時,下發訊息請求成功且code碼為80000000,但是手機總是收不到訊息; 在華為推送自助分析(Beta)平臺查看發現,訊息發送觸發了頻控。 二. 問題原因及背景 2023年1月05日起,華為推送服務對咨詢營銷類訊息做了單個設備每日推送數量上限管理,具體 ......

    uj5u.com 2023-04-20 08:40:11 more
  • 歡迎頁輪播影片

    如圖,引導開始,球從上落下,同時淡入文字,然后文字開始輪播,最后一頁時停止,點擊進入首頁。 在來看看效果圖。 重力球先不講,主要歡迎輪播簡單實作 首先新建一個類 TextTranslationXGuideView,用于影片展示 文本是類似的,最后會有個圖片箭頭影片,布局很簡單,就是一個 TextVi ......

    uj5u.com 2023-04-20 08:39:36 more
  • 【FAQ】關于華為推送服務因營銷訊息頻次管控導致服務通訊類訊息

    一. 問題描述 使用華為推送服務下發IM訊息時,下發訊息請求成功且code碼為80000000,但是手機總是收不到訊息; 在華為推送自助分析(Beta)平臺查看發現,訊息發送觸發了頻控。 二. 問題原因及背景 2023年1月05日起,華為推送服務對咨詢營銷類訊息做了單個設備每日推送數量上限管理,具體 ......

    uj5u.com 2023-04-20 08:39:13 more
  • iOS從UI記憶體地址到讀取成員變數(oc/swift)

    開發除錯時,我們發現bug時常首先是從UI顯示發現例外,下一步才會去定位UI相關連的資料的。XCode有給我們提供一系列debug工具,但是很多人可能還沒有形成一套穩定的除錯流程,因此本文嘗試解決這個問題,順便提出一個暴論:UI顯示例外問題只需要兩個步驟就能完成定位作業的80%: 定位例外 UI 組 ......

    uj5u.com 2023-04-19 09:16:23 more
  • FIDE重磅更新!性能飛躍!體驗有禮!

    FIDE 開發者工具重構升級啦!實作500%性能提升,誠邀體驗! 一直以來不少開發者朋友在社區反饋,在使用 FIDE 工具的程序中,時常會遇到諸如加載不及時、代碼預覽/渲染性能不如意的情況,十分影響開發體驗。 作為技術團隊,我們深知一件趁手的開發工具對開發者的重要性,因此,在2023年開年,FinC ......

    uj5u.com 2023-04-19 09:16:15 more
  • 游戲內嵌社區服務開放,助力開發者提升玩家互動與留存

    華為 HMS Core 游戲內嵌社區服務提供快速訪問華為游戲中心論壇能力,支持玩家直接在游戲內瀏覽帖子和交流互動,助力開發者擴展內容生產和觸達的場景。 一、為什么要游戲內嵌社區? 二、游戲內嵌社區的典型使用場景 1、游戲內打開論壇 您可以在游戲內繪制論壇入口,為玩家提供沉浸式發帖、瀏覽、點贊、回帖、 ......

    uj5u.com 2023-04-19 09:15:46 more
  • iOS從UI記憶體地址到讀取成員變數(oc/swift)

    開發除錯時,我們發現bug時常首先是從UI顯示發現例外,下一步才會去定位UI相關連的資料的。XCode有給我們提供一系列debug工具,但是很多人可能還沒有形成一套穩定的除錯流程,因此本文嘗試解決這個問題,順便提出一個暴論:UI顯示例外問題只需要兩個步驟就能完成定位作業的80%: 定位例外 UI 組 ......

    uj5u.com 2023-04-19 09:14:53 more
  • FIDE重磅更新!性能飛躍!體驗有禮!

    FIDE 開發者工具重構升級啦!實作500%性能提升,誠邀體驗! 一直以來不少開發者朋友在社區反饋,在使用 FIDE 工具的程序中,時常會遇到諸如加載不及時、代碼預覽/渲染性能不如意的情況,十分影響開發體驗。 作為技術團隊,我們深知一件趁手的開發工具對開發者的重要性,因此,在2023年開年,FinC ......

    uj5u.com 2023-04-19 09:14:08 more
  • 游戲內嵌社區服務開放,助力開發者提升玩家互動與留存

    華為 HMS Core 游戲內嵌社區服務提供快速訪問華為游戲中心論壇能力,支持玩家直接在游戲內瀏覽帖子和交流互動,助力開發者擴展內容生產和觸達的場景。 一、為什么要游戲內嵌社區? 二、游戲內嵌社區的典型使用場景 1、游戲內打開論壇 您可以在游戲內繪制論壇入口,為玩家提供沉浸式發帖、瀏覽、點贊、回帖、 ......

    uj5u.com 2023-04-19 09:08:34 more