日々常々

ふつうのプログラマがあたりまえにしたいこと。

2021年のJavaとnullの話

ブログの下書きを眺めてたら長文がみつかったので供養しておきます。 TODOがDOされるかはわかりません。心の目で見てください。


Javaには null があって、この子がいない世界がいいんですけど、いなくなったりはしてくれないので、仕方がありません。いい感じに付き合っていきましょうねーって話を書きます。関連エントリとかスライドはこの辺。

以降は長文ですが、この手のは実務でいくつかの条件を満たした上での話になります。 なるべく書いたつもりですが、無意識に前提としているものはスコーンと抜けているでしょうし、まるっきり同じ条件でも「こちらの方がいい」という選択肢もあっていいと思います。その辺の話は実務でやりたいなーって思ってます。

本稿は「Javaの話」として書いてます。「他の言語にすればいい」とかは異なる話です。

私の基本的な考え

NullPointerException(以降NPE)は起こるべきところで起こって良い。ダサかろうと予期しない null が混入した際に発生する適切な例外はNPEであり、他の何かで取り繕う必要はない。

予期しない nullnull チェックで誤魔化さない。null チェックして null を返すようなものは延命措置にしかならないし、そんな延命してる間にどんどん傷は深くなる。フェイルファスト。

そのような null は混入しうる境界で検疫し、丁寧に取り除く。実装は冗長で良い。下手に格好良く null を回避できると、一律それを行うような思考停止を招きかねない。 null は必要なところでだけで、泥臭くダサく目立つ形で取り扱う。

もう少し詳しい話

(なんかどっかで書いたと思うんだけどなぁ、と思いながら)

null が混入するタイミングはそれなりに限定的であり、予測可能です。

  • 自身で null を明示的に使用した場合。変数へ代入したり、メソッドの引数や戻り値にすることで伝染する。比較での使用は問題ない。
  • 参照型フィールドのデフォルト値。
  • フレームワークに設定される引数。
  • ライブラリのメソッドの戻り値。

このうち前者2つは自身の実装の話です。フィールドのデフォルト値は完全コンストラクタに拘れば、自身が混入させるのは null を明示的に記述した場合に限定されます。コンストラクタ引数に null が勝手に突っ込まれるのは、後のフレームワークやライブラリの話。

脱線: ちなみに JIGバイトコードから null を見つけて警告しています。自身のコードによる混入チェッカー。作者の null 嫌いがわかりますね(ただの自己紹介)。

自身で制御しづらいのは後者の2つですが、フレームワークやライブラリの実装都合との接点で null は片付けてしまうように設計します。 最近のフレームワークやライブラリは null の代わりに Optional を選べるものも増えてきましたが、これは別に解ではありません。てか Optional であっても考慮しなきゃいけないことには代わりないので、可能であれば null もemptyな Optional も取り除き、予期しない値であればここで素直に例外を発生させます。予期できる null であれば、適切な取り扱いをこの境界で決定し、コアである処理には持ち込まないようにします。

<<<TODO 例示>>>

null は言語都合や実装都合で混入するものなので、コアなところに null は入ってきて欲しくないわけです。Javaという言語上どこにでも入りうる null をそのままにしておくと、本当にやりたいことが null の考慮に圧迫されます。考えなければならないことを減らすが設計の基本だと思っているので、安全な領域を定めて境界で弾く。弾き損ねたものは素直にNPEのフィードバックを受け取る。そんな感じです。

nullセーフな演算子

たとえばGroovyには ?. があります。 null だった場合も気にせずにメソッドが呼び出すかのように記述でき、 nullの場合は呼び出されずにnullが返る便利さんですね。他の言語でも似たような機能は結構見ると思います。局所的にはシンプルなコードが記述できます。

これは少なくとも私がJavaで業務処理を書く時には存在していて欲しくないものです。 道具が悪いのではなく使い方の問題ではあるんですが、「hoge.method() と書けるものであっても hoge?.method() と書いておけば安全」のような思考停止を招くからです。一律やるのは避けたとしても、「NPEが発生したら .?. に書き換える」ようなことが起こります。私は意志が弱いので、その引力に負けない自信はありません。そしてきっと後悔する。

これは「強力な機能が設計の歪みを覆い隠す」パターンだと思っています。 null が混入しなくなった場合に除去するフォースも働きません。そしてそのコードがある限り、 null は混入しうるのか?と疑心暗鬼になります。1箇所あるんだから、他でも入ってきたりするんじゃ……と。そんなノイズを入れながらコードを読み書きしたくないんだ。

でももし言語機能として持ってたら、特定の文脈(ライブラリとかフレームワーク作ってる時とか)では喜んで使うと思う。便利は便利だからねぇ。

JEP 358: Helpful NullPointerExceptions

2021年風味を混ぜておきます。 Java14で入ったJEP 358は、今年9月リリース予定であり、しばらくのスタンダードになると思われるJava17で使用できます。 これは何かというと、メソッドをチェーンしている場合などの途中でNPEが発生した場合、どれが null かを教えてくれる機能です。 以下は 16.0.0-librca での実行例です。

jshell> class A { Object obj;}
|  次を作成しました: クラス A

jshell> var a = new A()
a ==> A@5f184fc6

jshell> a.obj.toString()
|  例外java.lang.NullPointerException: Cannot invoke "Object.toString()" because "REPL.$JShell$12.a.obj" is null
|        at (#3:1)

jshell>

Java13以前で a.obj.toString() のようなコードでNPEが発生した場合、スタックトレースには行番号しか表示されず、anull なのか objnull なのかわかりませんでした。そのためNPEの解析では一時変数で受けてみたり、aa.obj それぞれに対して null チェックを行ってみるなどの足掻きが必要でした。デバッガなどを使用しない場合、コードを変えないと解析が困難なこともあり(少なくともスタックトレースから瞬時に判断はつかない)、NPEが嫌煙される一因だったかと思います。

Java14以降ではObjects#requireNonNull(Object) メソッドや自身で null チェックしての例外送出より、そのままNPEを起こした方が解析しやすいまであります。予期しないNPEには備えるんじゃなく、予期しないnullを教えてくれるNPEを素直に受け止めるのがいいと思います。

nullを許容する場合

ライブラリが要求するAPIになっている場合と、閉じたスコープで使用する特殊値です。

クラスに閉じたフィールドで、最も軽量に扱える特殊値はおそらく null であり、それ以外の値を使用するのはコストが上回ります。 ただその null が外部に流出しないように設計/制御する必要はありますが、制御可能な範囲において null 以外を頑張って使用するよりかは扱いやすいです。これは単に null が言語に組み込まれた特殊値であるが故で、使えるから使うと言うスタンスです。決して「 null が最適解」ではないので、誤解されぬよう。。。

まとめ

NPEがダサいって気持ちはわからなくはないですが、NPE以外の予期しない実行時例外もどうせダサいです。NPEじゃなくす努力なんて要らないんじゃないかなって。

あー null 無くならないかなー。

おまけ(2024-05-12T0:03)

投稿したらサムネに出た画像をおまけでつけておきます。

たぶん <<<TODO 例示>>> でこうやって棲み分けるんだーみたいなことを書こうとしてたんだと思います。 ファイル名が 20210421013620.png とかから察するに、あれがアレで。もう覚えてない。 Miroで書いてるっぽいからどこかのボードに残骸はあるんだろけど……

わからないことの調べかたを考えてみる(2018年版)

ブログの下書きを眺めてたら長文がみつかったので供養しておきます。


開発では、何かを調べることに多くの時間が費やされます。私の調べ物のやりかたや、他の人のを見て思っていることを書いてみます。

前提条件

  • 調べ物が苦手
  • 何か効率が悪い気がする
  • 調べ物に時間がかかる
  • 間違った情報に振り回されている
  • 情報が間違っているのかやり方が間違っているのかわからない

と言った方には役に立つかもしれません。 自分で調べ物ができるかた、そのやり方に疑問のない方には何の足しにもなりません。 あと私のやり方と、私から見える人の私からの見え方でしかないので、他のコンテキストは知りません。

また、ここでの「調べる」は初めて使用するツールの使い方など、未知領域の課題解決を行うためにインターネット等を検索することを指します。 実際は検索する前に意識/無意識にかかわらず課題の切り分けを行い、問題領域によって手段を変えているはずです。

調べ物のレベル

レベル感をふわっと書いてみます。

  1. 何を調べたらいいかわからない
    • 何がわからないかわからない、どう言うキーワードで調べればいいかわからない。思ったものが全く出てこず、解決しない。稀に何とかなったりするけれど、ほんと稀。
  2. 検索上位にでてきたものをそのまま試す
    • 短時間でたどり着くこともあるが、打率は低い。動かないときは「調べた通りにやったけれど、なぜか動かなかった。」でゲームセット。
  3. 検索で出てきたものを手当たり次第に試す
    • うまくいかないときは永久にたどり着かないが、当人は「いつかたどり着く」と思っていたりする。そのため「時間が足りない」と言うが、どれくらい時間があればたどり着くかの予想もできない。
  4. 検索結果をさらに自分の条件に照らし合わせてフィルタリングして試す
    • それなりの精度と質で解決できたりする。しかしなぜそれでいいかの説明ができることは稀。「出典はWikipedia」程度の信頼性。
  5. 公式ドキュメントの手順に従う
    • ドキュメントのGettingStartedなど、手順的なドキュメントがなければお手上げだったりする。
  6. 公式ドキュメントから設定を理解した上で調整する
    • だいたいの問題はクリアできる。逃れられない問題として、ドキュメントの質に依存する。記載されている箇所が見つけにくかったり、そもそもドキュメントが使い物にならないプロダクトも稀によくある。
  7. ソースコードを当たって解決する
    • (コードが使える場合)解決できる問題なら解決できないほうがおかしくなる。しかしドキュメントされていないトリッキーな解決をしてしまう可能性もある。機能を損傷したり、片手落ちな設定をしてしまったりすることもある。
  8. コンセプトを踏まえて解決する
    • 「こういうコンセプトで作られているから」から導かれる解決。「そう言うことに使うものではない」と捨てる判断も早い。ドキュメントはもちろん、経験や歴史を把握していると可能になる。かもしれない。
  9. サポートに問い合わせる
    • 金の弾丸。サポートの人に教えることになることになったり、言う通りにしたら環境が壊れたとかも稀によくあるので、万能ではない。

だいたい前段階はクリアしているので、その手段でなくても前段で解決できたりします。分水嶺は5、公式ドキュメントを読めるか。これが冒頭のツイートのラインです。4までくればそれなりの精度と質で解決できてしまうので、その辺りで満足してしまっているのも多そうな感じがしています。

Qiitaが一部の人に嫌われる理由

特定サービスを出して申し訳ない。別にQiitaに限ったことではありませんが、検索時に

調べ物をしている人を見ていると、高確率でQiitaを参照しています。検索上位にくるのはサービスの努力も大きいでしょうが、同様の問題で困った方が書いていたりするので、近い語彙なのでヒットしやすいと言うのもあるのかなと思います。

Qiitaの記事が玉石混交であると言う事実はありますが、それは他のQAサービスやブログ、情報サイトでも変わりません。 なので問題点はそこではなく レイアウトが統一されていることによる区別のつきづらさ に由来すると思います。 これは「ソースはWikipedia」と言うのが揶揄の対象となるのと近いのですが、Qiitaも同様に「Qiitaに載ってた」と言うような場合に問題になります。 つまり、記事の質は個人に由来するサービスの特性に対して、情報を判定する精度が個人に由来していないのです。

私も調べ物でQiitaを参照することは多いので、非常に助けられています。なので「Qiitaを見てはいけない」とまで言うつもりはないです。 ちなみに、私がQiitaを参照するときは「誰が書いているか」を気にしていることが多いです。

Qiitaのコンセプトを確認してみる

qiita.com

公式ドキュメントを見るべき理由

解決したときの根拠の説明ができるかが重要です。もし解決しても、なぜそれでいいのかを説明できないのであれば、それは「たまたま」でしかありません。対面していた問題は「たまたま」で解決しちゃっていいものでしょうか。再発した場合に何か対処法はあるでしょうか。他のところに影響は与えないでしょうか。などなどあります。公式ドキュメントに記載のある方法ならば、注意すべきことがあるならば記載されていることも多いです。

また、解決できない場合もドキュメントに記載がないのであれば、ある程度「仕方ない」と思えます。そう言うものを使っていると言う事実を受け止めましょう。「ドキュメントが使い物にならないもの」とわかって使うならば、アプローチの仕方も変わってきます。その判断のためにもドキュメントを参照するべきなのです。

もし、ドキュメントに記載されている通りにやってうまくいかないのであれば、バグの可能性もあります。課題管理システムにアクセスできるなら、課題登録されていないかを検索してみましょう。次バージョンのリリースノートに載っているかもしれません。ともかく、記載の通りにやっているのに動かないのであればサポート問い合わせを検討するのがいいでしょう。OSSなら「Use the Soruce, Luke」もありです。

もう一つの理由は、互換性です。ドキュメントに記載されているものは互換性が保たれる可能性が高いです。ソフトウェアはバージョンアップするものなので、互換性は重要です。ドキュメントされているものは、バージョンアップの方法もドキュメントされることが期待できます。ソースコードから得たトリッキーな解、たとえば内部APIを触ったものなどに互換性は期待できません。更新コストを抑えるためにも、提供元が予想できる使い方をすることは重要なのです。

常に公式ドキュメントを読むか

そうとも限りません。ブログやQiita、StackOverflowなどが検索上位に現れることも多くあります。これを除外するフィルタリングを常に検索エンジンで行うのは骨でしょうし、私はやったことはありません。

公式ドキュメントはピンポイントで私の問題に答えてくれるものではないため、(その瞬間に限って言えば)多くのノイズを含みます。常に公式ドキュメントだけを参照するのは時間がかかります。無限に時間があるならそれでもいいかもしれませんが、私の時間は無限ではないのです。そのため解決したい問題の性質によっては、公式ドキュメントを当たらないこともあります。

前段で「たまたま」と言いましたが、たまたまも悪いものではありません。公式ドキュメントではないにせよ同等の信頼性があると判断できる場合だとか、明確な検証方法がある場合だとか、まあ色々言えますが、ともかく。たまたまで解決できていい問題は、たまたまで解決してしまっても良いのです。ドツボにはまりそうになったら、落ち着いて公式ドキュメントを読めばよいだけです。

私の調べ方

  1. アクセスしやすい公式ドキュメントを参照する
    • Javadocコメントなどに記載されているものであれば、高精度な情報に最速でアクセスできる。検索する必要もない。
  2. 検索結果に一通り目を通す
    • タイトルだけでも。だいたいGoogle検索結果の1ページだけで、たまに2ページ目もみる。
  3. 信頼性の高い情報を試す
    • (後述)
  4. 信頼性の高い情報で得たキーワードで公式ドキュメントを検索する
    • 最初から全部舐めるのは厳しいので。

記事の信頼性判断

検索結果は玉石混交。とは言え砂漠の中で砂金


ここで終わってました。 何書こうとしてたんですかね、2018年の私……

あ、今だと「AIに投げかけてみる」ってのも出てきますね。AIの使い方の話になってきて、これはこれで一大トピックだし、それを書き始めるといつまでもこの下書きが成仏できないのでここまでにしときます。

2023年のふりかえり

ことし10本目のエントリ。

毎回このブログの1単位を「記事」と呼ぶか「エントリ」と呼ぶかとかで無駄に悩んだりします。「本記事」とか「本投稿」とかそういう、書いている文章内のスコープを指すの。 で意識的には「エントリ」を使ってるんだけど、これははてなブログのどこか(たとえばこの記入欄のURLなどにnew entryとか書かれていたりする)で使われていたからなんだけど、UIを見ると「記事を書く」とか「最近書いた記事」とかだな。えーでも「ホットエントリー」だよなぁ、でもホットエントリーははてブの文脈か。「記事」に統一したそうだから「記事」でいくか。

と、いきなり脱線したけど、別に本線もない。 本線とかいうと電車を思い浮かべるんだけど、Factorioで最近ようやく電車使った拡大を初めてて。どんどんデカくなるけど、世界はもっとでかそうだねぇ。敵(原住生物)が邪魔だけど、邪魔程度で戦い方の工夫とか要らないのがもう少しどうにかならないかなぁって。過密工場地帯みたいなの作ってないから汚染拡大もしないんだよね。ふむー。

今ブログ書こうと思ったのは、かずひらさんがなんか書いてたから。

kazuhira-r.hatenablog.com

そいやふりかえり記事とか書いてたね。

今年のふりかえり

オフラインイベント

JJUG CCC行きました。SpringとFall両方。 関ジャバやりました。

オフラインはいいね。

JJUG CCC Spring

「Webアプリケーションを作りましょう」というタイトルで、ライブコーディング。

あまりそうは見えないって言われるけど、緊張するタイプでして。通常の登壇時も心拍数爆上がりするし喉はカラカラになるし。 そんな奴が一発勝負のライブコーディングなんてやったらどうなるよってね。

笑えるほど腕が震えてほんと笑った。端末環境もあるけど、家でやったことの半分もできないでやんの。

あ、資料公開はしてないです。ライブコーディングありきだし。

オフラインイベントでの面白さはある程度あったかなぁって思いました。 これはJavaコミュ@福岡でも呼んでいただいて再演しました。

javaq.connpass.com

素敵な建物でした。おさけ飲みながらダベるのも楽しかったです。

ライブコーディングはいいんだけど、タイムアタックはオフラインイベントとしてはイマイチな感じがする。 タイムアタックなら動画撮影してYouTubeにでも置いておけばいいしねー。双方向な感じでやりたい。

JJUG CCC Fall

「Gatlingによる負荷テスト入門」というタイトルで、ビギナーセッション。これは資料公開してる。

speakerdeck.com

内容は「色々勉強するのもいいけど、まずできるところからやって眺めるってはじめ方もいいよ」というものです。 資料にもある通りなんだけど、この入門段階では実務に耐えません。全然足んないです。でもやらないよりマシではあると思います。

これは別途独立した記事を書きたいんだよねぇ。

関ジャバでもこの資料や準備を使い回して、Side:BでGatlingに比重おいてやろうとしたんだ。時間制約とかで色々はしょりました。色々。

関ジャバ

別に休止してたつもりもないので再開とか言わないって言い張ってるんだけど、再開以外のなにものでもない。

kanjava.connpass.com

オフラインイベント(要するにJJUG CCC)のたびに「やらないの?」「やりたいね」みたいな話をして、ようやくという感じです。 やってみたらやっぱいいなぁという感じ。でもオンライン勉強会の選択肢が出た以上、オフラインならではを強めたい思いを新たにしています。

「会」ですからね。

次は1月にやる予定だよー。

ゲーム

SteamとNintendo Switchでやってます。 スマホゲーは今年やってない気がするね。外出ないしなぁ。

Surviving Mars

火星でコロニーを作る。 住民は食糧や酸素や水の供給が途絶えてもがき苦しんでます。ドローンかわいい。

延々と同じマップでやる系ではなく、条件と目標を達成するチャレンジモードをこなしていく感じ。 延々と同じマップでやってもいいけど、そんな広くないし。

実績は35/80、プレイ時間は内緒。

Tropico 6

島国の独裁者になって色々やる。 独裁者で弾圧とかもできなくはないけど、やってもいいことない。

延々と同じマップでやる系ではなく、条件と目標を達成するミッションをこなしていく感じ。 延々と同じマップでやってもいいけど、島ごとに方向性決めて発展させるから、別の島でやる方が楽しい。

実績は22/40、プレイ時間は内緒。

This War of Mine

戦争に取り残された民間人を操作して生き延びる。 プレイ中は何やっても悪い方向にしかいかないし、エンディングも救われない。

オートセーブが基本で取り返しがつかない。 セーブできたらヌルゲーでつまんなくなるからやるべきじゃない。

何が楽しいかって、楽しいゲームではないね。 クリアしても達成感や解放感ってより単に気が抜けるだけだし。 でもいいゲームだとは思う。一度はやってみてほしい。一度でいいと思うと言うか、一度でお腹いっぱいになると思う。

実績は20/55、プレイ時間は内緒。

Factorio

惑星に一人で不時着、宇宙船はぶっ壊れてる。そうだロケットを飛ばそう! ……ロケットを飛ばすのが目標なんだけど、別にロケットに自分が乗るわけではない。飛ばすだけ。なんで?

なんとなく壊れた宇宙船の隣でロケットを飛ばすのが好きで、毎回こんな感じで飛ばしてる。

これマジで仕事の知識使えるし、仕事に転用できる要素も多い。 そう言う見方をすれば、だけど。

延々と同じマップでやる系。 違うマップはそれなりに違いがあって面白みもあるんだけど、大規模な鉄道運用とかやるのを違うマップで1からやるとか、ちょっとだるい。

実績は27/38、プレイ時間はマジで内緒。 「雑談する暇はない」はいけてた。「スプーンなんてない」は挑む気すらしない。

Stellaris

銀河を股にかけて、開拓したり戦争したり同盟したりする。 目標は一定期間で一位になることっぽい?

銀河間を飛び回れる文明たちでやり合うけど、そう言うのを認識していない未開の文明を観察したり気づかれたりとかそう言う要素もある。パラメタが大量にあって把握するだけでも結構なもの。ぶっちゃけ情報過多ってか、チュートリアルやっても「これなんだっけ??」ってなる。

あと不具合が多くてめちゃめちゃクラッシュする。やりはじめた時のバージョンが非常にバギーで、色々覚えていこうってタイミングでこう言う中断で水をさされるとやる気無くすよね。てことであまり遊べてない。

実績は21/174。

Plague Inc: Evolved

伝染病を進化させて人類を絶滅させる。 なおコロナ前からあるゲームである。

日本語だと「伝染病株式会社」でスマホアプリとかもあるらしい。

今日始めた。年末に何やってるの?

大学でバクテリアの遺伝子いじるのやってたから、ちらほら「あ、知って・・・・る?気がするけど、もう忘れた・・・」ってなってる。

リングフィットアドベンチャー

12月に買った。

らくしょーって思ってたら運動負荷10だった。

毎日やるのを今年の目標にしたのに、25日間で連続は途絶えた。

void tRrLM();

トリコちゃんかわいい。

まとめ

かずひらさんの記事、および元になったやんくさんの記事を読んで書き始めたのに、なんかゲーム記事になった。

yyyank.blogspot.com

まぁいいや。

アウトプットしようぜー、って言いながら自分がアウトプットあまりできてないなって。

色々事情あって仕方ないんだけど。来年は出ると思います。思うだけ。