木曜スペシャル!アパッチの秘宝!Commonsの奥地で伝説の「例外が投げられるStream」を見た!
Javaお前な。そういうとこやぞ。
いやぁ。JavaのStreamって便利ですね!
forループのあるあるバグ要素を排して、メソッドチェーンでどんどん処理がつなげられる。
書いててとても気持ちのいい!*1rubyとかJavascriptにもあるアレです。
しかしながらこのStream野郎。例外との相性が悪いと言う軽い致命傷があります。
Streamで使ってる関数インターフェース(ConsumerとかFunction)にthrowsが付いてないので、その中身で例外を処理することが要求されるんですね。
そのため、Stream処理の途中で例外を吐いて以降の処理を止めたりできないとか、Streamの外で例外処理を行うのが面倒だったりとか。
結局例外を吐くループ処理は従来の拡張forを使うみたいな残念な状況になってます。
そんなある日。
たまたまApache財団のJava便利ライブラリCommonsを探索していたらFailableStreamというクラスを発見。
「失敗可能なStream」ってもしかしてこれアレじゃないの?と思ってJavadocを見るとlambda内で例外をキャッチしなくてもいいStreamらしい。
ってことは当然キャッチしなかった例外を外に伝搬できるってことだよね???もっと調べてみよう!
と思ったけどそれ以上の説明はなし。
ググってみるけど気持ち悪いくらい誰もこのクラスについて言及をしていない。
と言うわけで今回は、私もぐのが体を張ってこのクラスをご紹介していこうと思います。
とりあえず試してみよう!
素のStreamと例外処理のおさらい
まずは比較対象として素のJavaで今回書きたいコードを書いてみます。
Streamのmap()のlambdaで例外をthrowして、map()の外側でそれをキャッチしたい感じです。
しかし、mapに渡すFunctionインターフェースのapply()メソッドにはthrowsが付いてないので、lambda内で例外のキャッチを求められます。
package com.example.demox; import java.io.IOException; import static java.util.stream.Collectors.toList; import java.util.stream.Stream; public class DemoxApplication { public static void main(String[] args) { try { Stream.of(0).map(a -> { throw new IOException("test"); ↑ ここでコンパイルエラー }).collect(toList()); } catch (Exception e) { System.out.println(e.getMessage()); } } }
Streamの外で例外の発生有無を確認するには、Stream外のListに例外オブジェクトをメモるとか何かしらの工夫が必要です。
package com.example.demox; import java.io.IOException; import static java.util.stream.Collectors.toList; import java.util.stream.Stream; public class DemoxApplication { public static void main(String[] args) { List<Exception> exceptions = new ArrayList<>(); Stream.of(0).map(a -> { try { throw new IOException("test"); } catch (Exception e) { System.out.println(e.getMessage()); exceptions.add(e); } }).collect(toList()); // ここでexceptionsを評価 } }
FailableStreamと例外処理
それでは今回のメインイベント。FailableStream。
このmap()はFailableFunctionって言うthrows付きのメソッドを持ってるインターフェースを引数に取るのでコンパイルエラーは発生しませんね。
期待できます。
package com.example.demox; import java.io.IOException; import static java.util.stream.Collectors.toList; import java.util.stream.Stream; import org.apache.commons.lang3.function.Failable; public class DemoxApplication { public static void main(String[] args) { try { Failable.stream(Stream.of(0)).map(a -> { throw new IOException("これはテスト用の例外です!もぐののダジャレは冷害と界隈からよく言われていますが。そんなことないと思いますよプンプン。"); }).collect(toList()); } catch (Exception e) { System.out.println(e.getMessage()); } } }
それでは実行。
ちゃんとlamda内でthrowしたIOExceptionがキャッチできて、僕の「例外」と「冷害」を掛けた渾身のメッセージがコンソー
null
も「虚無・・・やと?」
雲行きが怪しくなってくる
言われてみればこのFailableStream。map()にthrows付いてないんすよね。
素直に例外を受け取ってる訳では無い様です。
何が起こってんのかデバッガで見てみます。
なんかリフレクション関連のけったいな例外UndeclaredThrowableExceptionが出てますね。
一般的にネストした例外を突っ込むフィールドであるcauseもnull。
独自のundeclaredThrowableってフィールドに僕が放った渾身のExceptionが格納されてます。
ドキュメントを適当に読んでみます。
UndeclaredThrowableException (Java Platform SE 6)
呼び出しハンドラの invoke メソッドが、プロキシインスタンスで呼び出され呼び出しハンドラにディスパッチされたメソッドの throws 節で宣言されたどの例外タイプにも割り当てできない確認済み例外 (RuntimeException または Error に割り当てできない Throwable) をスローした場合、プロキシインスタンスのメソッド呼び出しによってスローされます。
UndeclaredThrowableException インスタンスは、呼び出しハンドラによってスローされた、宣言されていない確認済み例外を格納しており、getUndeclaredThrowable() メソッドで取り出すことができます。UndeclaredThrowableException は RuntimeException を拡張するため、確認済み例外をラップする未確認例外となります。
も「なるほど120%理解した(目にハイライトなし)」
まず「未確認例外」って言うのは、いわゆる非検査例外のことと解釈。
(検索したけどこのクラスの説明以外にヒットせず)
確か非検査例外ならば普通のStreamの外でも例外が受け取れるって言うハックがあるはず。それを利用してるのかしら。
次、リフレクション関係の例外ってことはFunction::apply()をリフレクション呼び出ししてんのかなぁ?とFailableFunctionのソースを追うもリフレクションでapply()を呼び出してる箇所は発見できず。
まぁ、JVMが関数インターフェースの唯一のメソッドを特定する処理とかでリフレクションしてそうだからそんなもんなんでしょう。
大体(空気間は)わかったけど、UndeclaredThrowableExceptionをキャッチしないと真の例外が見れないのは可読性下がりそうでやだなぁ。
と思ってたら、
1.4 リリースでは、この例外は汎用的な例外チェーン機構に適合するように改良されています。「呼び出しハンドラによりスローされた宣言されていない確認済み例外」 (構築時にスローされ、getUndeclaredThrowable() メソッドを介してアクセス可能) は、cause メソッドと呼ばれるようになり、前述の「レガシーメソッド」に加えて Throwable.getCause() メソッドを介してアクセス可能です。
あ、getCause()で取れるのね。ギリ許せるか。
これが決定打か?
最終的にたどり着いたのがこのコード。
import static java.util.stream.Collectors.toList; import java.io.IOException; import java.util.stream.Stream; import org.apache.commons.lang3.function.Failable; public class DemoxApplication { public static void main(String[] args) { try { Failable.stream(Stream.of(0)).map(a -> { throw new IOException("これはテスト用の例外です!もぐののダジャレは冷害と界隈からよく言われていますが。そんなことないと思いますよプンプン。"); }).collect(toList()); } catch (Exception e) { if (e.getCause() instanceof IOException) { System.out.println(e.getCause().getMessage()); } } } }
catchの中が例外もみ消し事故を誘発しそうでちょっと使いにくいなぁ。。。
引き続き、もっといい銀の弾丸を探そうと思います。
【このブログがお気に召しましたら、ぜひ以下のリツイートをお願いします!】
はてなブログに投稿しました #はてなブログ
— もぐの@ブログ「もぐてっく」 (@moguno) 2021年2月11日
木曜スペシャル!アパッチの秘宝!Commonsの奥地で伝説の「例外が投げられるStream」を見た! - もぐてっくhttps://t.co/GewG8OaB7a
*1:後から見ると可読性が死んでたりしますが
はてなブログのブログタイトルのところにCSSでトレードマークを表示する
今YY-BBSが熱い!!!!(えっなんで??????)
理由はさっぱりわかりませんが、私の周りでYY-BBSが空前の大ブームになっています。
発端は例によってておくれインフルエンサーの@Akkiesoftさん。
要はあれですね。
個人用HP*1によくあった掲示板CGIってやつですね。
それを@Akkiesoftさんが戯れにインストールしたところ、インターネットに幼少期とか青春とかを捧げたおっさん達にぶっ刺さったということみたいです。
あっという間にスレにはギコ猫やしぃが溢れ、※未承諾広告が妖艶な光を放ち、アクセスカウンターのキリ番申請が飛び交うと言う、当時の野暮ったくも熱量のあるインターネットが再現されました。なんだこれ?
まぁいいや。そんな中で始まった遊びの一つが「相互リンク」でした。
「相互リンク」と言うのは、小さい「バナー」と呼ばれる画像に自分のHPのリンクを付けたものをお互いのHPに置いて、友情と言うか絆と言うか目に見えない何かを確かめ合うと言うあれです。
僕も昔は「Friday Blue」「NECO SYSTEM」「ウサギ畑で囲まれて」など数々のHPを立ち上げバナーをこしらえてきました。
僕もぜひ「もぐてっく」のバナーでこの流れに乗りたい!
そう思ってExcel*2を立ち上げてハタと気づきました。
あれ?「もぐてっく」ってどんなバナーにすればいいんだ?
昔のHPって大抵「タイトル画像」と言うものがあったので、バナーはその縮小版を基本にすればよかったのですが「もぐてっく」のタイトル部には「もぐてっく」と言うテキストしかありません。
一刻も早く「もぐてっく」のタイトル部を装飾してバナー素材を作らないと時代に乗り遅れる・・・!
と言うわけで首記の技を編み出すに至ったわけです。
まずはいい感じのロゴ探し ⇒ 最強感のある素材サイトが!
まずはロゴマークにする素材を探します。
「フリー アイコン」
で検索したところ、最上位にめっちゃクールな素材集サイトを発見。
モノトーンのかっこいいアイコンがこれでもかと登録されています。
使用許諾もゆるゆるなので安心して使えますね。
さて問題は何のアイコンにするかですが、
このおっさんがウサギを自称していることから、ウサギにすることにしました。
選んだのは左下のこの子。
下半分はアゴなのか体なのか。
捉えどころがないところが、いかにも「もぐてっく」と言ったところ。
この子を「ネルソン君」と名付けて、勝手に「もぐてっく」の公式キャラクターにすることにしました。
なおこのサイト。他の素材集サイトとWeb同盟*4を結んでいるらしく、使いやすそうな各種素材が一生困らないレベルの分量で落ちてる感じになってます。
こんなんとか。
こんなんとか。
はい、先生がエントリを書き始めて本題に入るまで1370文字かかりました。
と言うわけで、はてなブログのダッシュボードにある「デザイン」から「デザインCSS」を流し込んでいきます。
まずはネルソン君をはてなフォトライフにアップロードして、あらかじめ画像のURLをコピーしておきます。
次に以下のCSSを適用します。
#title a { background-image: url(https://cdn-ak.f.st-hatena.com/images/fotolife/m/moguno/20210127/20210127030131.png); background-repeat: no-repeat; background-size: contain; padding-left: 1.2em; }
背景画像としてネルソン君を繰り返しなしで表示。
本文を1.2em右にずらしてタイトルとネルソン君がかぶらないようにする。
その結果がこちら!
うひゃあ!可愛い。大成功です。
調子にのってエントリータイトルにもネルソン君を配備します。
.entry-title-link { display: table-cell; background-image: url(https://cdn-ak.f.st-hatena.com/images/fotolife/m/moguno/20210127/20210127030131.png); background-repeat: no-repeat; background-position: 0px; background-size: 2em; padding-left: 2.5em; vertical-align: middle; height: 2em; }
こっちも大成功です。
と、言うわけで本サイトはリンクフリーです。
「ネルソン君」と言う強力なマスコットを得た「もぐてっく」。
次はいよいよバナー交換です。夜なべして作ったのがこちら!
よろしければ相互リンクをお願いいたします!
【このブログがお気に召しましたら、ぜひ以下のリツイートをお願いします!】
はてなブログに投稿しました #はてなブログ
— もぐの@駆けだしフルスタック (@moguno) 2021年1月28日
CSSではてなブログのブログタイトルのところにトレードマークを表示する - もぐてっくhttps://t.co/ySF3QQxuKE
リモートワーク通算3日のもぐのがイキる!買ってよかったものダメだったもの!
コロナ怖いっすね
うちの県も緊急事態になったということで、弊社もリモートワークが再び解禁になりました。
職場の雰囲気としては前春の様な感じはなくて、みんな普通に出社してるような温度感なんですが、使われない制度ほど無駄なものはない精神で率先して早起きと通勤ラッシュを放棄することにした次第です。
なお、申請は週1日にしました。
同調圧力にも適度に屈するのがマイ生き様。
今回は過去2回 + 今日のリモートワークで大体わかった、良かったガジェット悪かったガジェットをご紹介したいなと思いました。
適度に広いぜ!27インチQWXGAモニター
自宅では、会社から借りてきたノートPCを引っ越し直後に買った27インチのモニターにつないで作業をしています。
2048×1152ドット(QWXGA)の100%表示。
これが会社で使ってる23インチのフルHD x 2枚よりもなんだか使い勝手がいい。
例えば、A4のドキュメントのPDFとデザインレビューシートのExcelを横並びにしたときに、十分な大きさで表示できる解像感。
フルHDx2枚だと両者をモニタに1枚ずつ表示してるのでコメントを書くたびにかぶりを振らなきゃならんかったんですが、それがいらなくなりました。
めっちゃ楽。
また、愛用してるVisual Studio Code(Vimプラグインカスタム)はMDI(ではないがウインドウ一枚の四隅にサブウインドウがスナップする)なので、枚数よりも1枚の解像度の高さが重要になります。ソースコードの一覧性は正義っすね。
シンプルに机の上も広くなるし、会社でもごねて買ってもらおうかしらん。
ちなみにこのHPモニタ。期間限定クーポンとかでなんか異様な割引があって2万円ちょいで買えました。
スピーカーがついてないのでミニスーパーファミコンとかに使えないところを除いて満足してます。
腰が楽だぜ!ダイソーの腰を支えてくれる奴
引っ越し時にKOKUYOのオフィスチェアを中古で買ったんですがなんかフィーリングが合わず。
17:00頃には腰が死んでいました。
今日試しにダイソーで買った自動車のシート用の腰サポーター(\300)を付けたところ、快適性が劇的に向上しました。
ておくれ界のトレンドリーダー@AkkieSoftさんに感謝です。
相性が悪いぜ!Majestouch MINILA Air(茶軸)
逆に使ってダメだったものもご紹介。
デスク周りをお洒落にしたくて買った日本語60%キーボード。
Majestouch MINILA Airです。
こいつが致命的だったのはただ一点。
「ESCと半角/全角が排他仕様(片方はFn押し必要)」
なところ。
皆様ご存じの通り私Vimmerでして、勤務時間の半分はVimキーバインドで暮らしてます。
Vimを代表するキーと言えばESC。作業中数百回はタイプすることになります(ほんまか?)。
一方、WindowsキーバインドではESCの出番は案外なく、圧倒的に半角/全角の方が使いでがあります。
今はWindowsに合わせて半角/全角をワンキー、ESCをFnキー付きにしてますが、そうするとVimが致命的に使いにくくなります。
コマンドモードに入ったと思ってコマンドを打ったら全角文字が入って、まずいとESCを叩くもまた半角に戻るだけ。みたいなのを数百回繰り返すと人間気が狂ってきます。
Ctrl + [がESCの代わりになるってことでそっちの練習を続ける傍ら、試しにサブキーボードにしたら普通にトップスピードが出て苦労するのがアホらしくなりました。
このThinkPadキーボード。
安っぽい打鍵感の割には特にストレスも感じない。なんか不思議な子ですね。
地味にお気に入りです。Bluetooth版を買ってメインにするのもいいかも。
と、言うわけで私の特殊性癖とピンポイントで相性の悪いキーボードのお話でした。*1
最後に
リモートワーク。感染拡大防止に絶大な効果を感じました。
(一日中他人と接触しないのでリスクフリー。かつ混み混みの電車で咳してるバカに対するイライラを完全回避。)
さらに睡眠時間の増大&通勤時間の削減とか、休み時間に家族との語らいとか家事ができるとか。
副次的効果も多いのでなんとかレギュラー制度として定着してほしいなと思いました。
*1:初代Majeの茶軸と比べて打鍵感が大味。とか別の気になるところもあったりしたのですがまぁまぁ。