mike-neckのブログ

Java or Groovy or Swift or Golang

Enumeration を使いやすくする(Iterable にする/Iterator にする)

TL で Enumeration が使いづらいというツイートが流れてたら、鮮やかに解決するツイートも流れてきた。


Enumeration<String> enumeration = ...;
Iterable<String> iterable = enumeration::asIterator;

f:id:mike_neck:20191128174829p:plain

asIterator というメソッドが Java9 から生えていたらしい。


残念なことに 拡張 for 文のソースの部分では推論がうまくできないため、コンパイルエラーになる

for (String s: enumeration::asIterator) { System.out.println(s); }

f:id:mike_neck:20191128174603p:plain

JJUG CCC 2019 Fall で発表した #jjug_ccc

表記の通り、 JJUG CCC 2019 で発表してきた。

f:id:mike_neck:20181019112748p:plain

jjug-cfp.cfapps.io

もともと発表資料を Key Note で作っていたものの、前日に予行演習した際に 90 分かかってしまい(セッションの時間は45分)、資料中に無駄な要素と有用な要素が分かちがたく結びついているなどで資料を削るのも難しいと判断、前日の 22:30 頃に 0 から資料を作り直すことにしました。極力短時間で書きたいため、レイアウトなどを考えなくてよいマークダウンで記述した結果、 GitHub のレポジトリーに発表資料ができる次第になりました。

github.com

発表中のツイートをまとめたのがこちらです

togetter.com

いつも参加された勉強会の発表をイラストでまとめてくださる 中山さん さんにまとめていただけました。光栄です!!!!


セッションについて

セッションでは時間が足らずにいくつか説明できなかったものがあるので、手短に雑に解説します

3-4. 概念に適切な例外を用いる

ざっくりいうと、

  • モデルとかレポジトリーインターフェースに実装依存の例外クラスを使わない(垂直方向に適切な例外を使う)
  • 業務内容的におかしな例外は投げない(ユーザーの認証モジュールが在庫モジュールの例外を投げてたらおかしい)(水平方向に適切な例外を使う)

3-6. 本当に必要なところだけに例外を使う

内容的にはバリデーションで引っかかったものについては例外を使わないですが、遠巻きに例外を使うなという過激なことを言っています

業務において期待した動作が不可能なのは

  • 入力が業務の入力値的に許容されない(バリデーション)
  • 業務的に操作が許可されない(パーミッション等DBへのアクセスが必要なものなど)

の 2 パターンだと思われますが、これらの違反状態は機能仕様として想定の範囲内だと思われます。したがって、これらは決して想定できない例外状態ではないので、例外で表現しないほうがよいだろうという主張をする予定でした。

発表の後に、技術的な例外と業務的な例外をうまく区別して扱う方法がないかという質問をうけて、 3-4. の回答をしましたが、こちらの業務には例外使わなくてもよいのではないかという回答でも良かったかと思っています。


最後に参考文献について

もともとは自分の経験を元に例外について喋っていこうと考えていました(CFPの時点では)。ところが、僕は語彙が圧倒的に足りないのか「例外とは何か?」という根本的な質問に対する適切な回答ができませんでした。 『Effective Java 第3版』 を読み返してみると、「契約による設計」というキーワードがそこかしこに見られ、また以前参加した java-ja「LOG.debug("nice catch!")」 での t_wada さんの資料と西尾さんの記録(はてな)を読み返すと、「契約」「事前条件」「事後条件」といったキーワードもあり、バートランド・メイヤーの 『オブジェクト指向入門 第2版 原則・コンセプト』 を読むことにしました。

オブジェクト指向入門』には例外の定義と例外処理の原則が書かれており、 JavaGolang などの実装から想定される例外に慣れていると考えもしないようなことを反省できます。その最たる例は「回復」というキーワードだと思っています。僕らの大好き『Effective Java』の項目70(第2版は項目58)に 「回復可能な状態にはチェックされる例外」という見出しが設定されており、これをもとにそこかしこで検査例外と非検査例外の使い分け条件として「回復」可能性を根拠としています。ところが、この回復についてちゃんと定義している人は『Effective Java』を含めてほとんどいないのです。『オブジェクト指向入門』では「失敗するケース」に「回復」について書いてあります。

ルーチンの実行中に例外が起き、ルーチンがその例外から回復しない場合に限り、そのルーチンコールは失敗となる。

オブジェクト指向入門 第2版 原則・コンセプト』p.530

これは逆に言うと、ルーチンから回復するとルーチンは成功になるということを意味します。ここから制御された例外処理の原則(『オブジェクト指向入門』p.534)をあわせて考えると、「回復」というのが「阻害されてしまった事後条件の達成を、再び獲得しようとする試み」と定義できる。また、「12.4.2 ハードウェアあるいはオペレーティングシステムの例外から回復する」(p.542)では浮動小数点数の 0 付近の値の除算命令の実行に対して、不可能な場合にはデフォルト値 0 を返すという回復の具体例があげられる。これはデフォルト値を返すことによって事後条件を達成するという「回復」を示す例である。

ここからは、芋づる式に例外処理ですべきことが明確かつ原理に即して(経験とか適当な推量ではなく)説明できるようになります。広義の事後条件の中にはクラスの不変条件が含まれており(『オブジェクト指向入門』p.472)、結果が成功であれ失敗であれルーチンの終了には不変条件の再構築が求められる(同p.549)(。『Effective Java』では、このことを項目76(旧64)「エラーアトミック性に務める」と言い直しています)。こうして、発表資料の 1-4.例外処理でやるべきこと3-5. 検査例外の使い分け の記述を書いていきました。


今回の発表資料は自分の無知からはじまって、『オブジェクト指向入門』を頼りに構成していきました。勢いと勘で書けばそれほど時間かからずに資料は作れたと思います。しかし今回はわからないことへの不安が強かったため、レンガを一つ一つ積み上げていくようなやり方で資料を書き上げました。そのため非常に時間がかかってしまい、所属する会社の皆様に協力をしていただくなどしました。大変感謝しております。