日々常々

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

Java本格入門を読みました

ゴールデンウィークの間に圧倒的成長をしたい若手プログラマさんに丁度いいんじゃないでしょうか。

Java本格入門 ~モダンスタイルによる基礎からオブジェクト指向・実用ライブラリまで

Java本格入門 ~モダンスタイルによる基礎からオブジェクト指向・実用ライブラリまで

Java本格入門をせろさんからいただきました。

よかったところ

この本で特に読むべきところを挙げるならば、「筆者は」で始まる部分です。 Javaはエコシステムも含めて選択肢の多い世界のため、「どれを選べば良いかわからない」こともしばしばあります。 そのような問題に対して、経験をベースにどういう考えでどんな選択をしているかが書かれています。 こういうのを前面に押し出した書籍は希少かなと。 ここだけ抜き出してもすごく価値があります。目立つようにしててくれたらいいのに。

言語仕様等、事実の解説はどうしても不足しますが、それは仕方ないところ。 プログラミング言語Javaはこの本よりも厚いですしね。

プログラミング言語 Java 第4版

プログラミング言語 Java 第4版

膨大な範囲を扱っていますが、焦点を現場で使うものの現場の使い方に絞っているので、深さも十分なものになっています。 タイトルの「本格」にぴったり合っているんじゃないでしょうか。

微妙なところ

スベってなかった。

「ファイル操作を極める」のところが少々冗長と感じました。 ファイル操作のバージョンアップにおける変遷は業務では特に重要なので、Java6以前との対比は必要なんでしょうが・・・毎回繰り返す必要はないんじゃないかなーと。なんか他と記述レベルが違う感じ。

また、序盤に集中している*1のですが、誤りと思われる部分もそこそこありました。 ですが、ほとんどは現場では実害を与えるものではないと思います。 仮に現場で誰かが言っててもスルーする*2程度ですね。

気づいたところをつらつら書こうかなと思ったのですが、そもそも対象読者が気にするものではないと思ったので、今回は書かないことにします。 チラシの裏(gist)に書いてってるので、そのうちリンクしようかな・・・

こんな人にオススメ

メインは業務でなんとかコーディングしている3-5年生くらいのプログラマさんかな。 がんばって読み砕けば、きっと以前の自分のコードを見て「うわぁ・・・」とか思えるはず。 自分のコードにコレジャナイ感を持っているなら是非どうぞ。

また、10年以上経験のある方もアップデートに良いと思います。 最新をある程度キャッチできてるつもりの私も「あ、こんなのあったんだ」みたいなのはありました。 読んで損するものではないでしょう。

後半にかけて急激にレベルが上がるので、序盤をなんとか読める程度の方にはなかなかハードな気はします。 入門とはいえ決して軽い門ではありませんが、まあ「門にも色々あっていいよね」とか言ってる人もいるんでいいんじゃないかな。

*1:事実を説明している部分なので、理解の水準や説明のわかりやすさなどで難しい部分です。

*2:さすがに面と向かって言われたら訂正しますが。

DIコンテナのインジェクション方法の使い分けについて

DIコンテナを使う時にどのインジェクションを使うかって話です。 たぶん誰かがどこかで同じようなことを書いているだろうけれど、気にせず書くよ。 「他の誰かが書いている」なんてのを書かない理由にしてると何も書けなくなるし。

コンテナ
DIコンテナのこと。
コンテナ管理
インスタンスのライフサイクルをコンテナが管理していること。雑に言えば、使う側で new しないってこと。
インジェクション
Dependency Injectionのこと。

Short Answer

コンストラクタインジェクションを使いましょう。使い分けなくていいです。

3種類のインジェクション

インジェクションには3種類ありますね。他あっても知らない。

  • フィールドインジェクション
  • セッターインジェクション
  • コンストラクタインジェクション

フィールドインジェクション

一番よく見るかな。

class Hoge {

    @Inject
    Piyo piyo;
}

この状態でHogeをコンテナにインスタンス生成してもらうと、piyoフィールドにコンテナ管理されているPiyoインスタンスがインジェクションされる。 記述量が少なくて済むのは嬉しいですね。

このインスタンスをコンテナ外で生成して使おうとすると、piyoフィールドに何かしらの方法でインスタンスを設定してあげなきゃいけない。 何かしらの方法ってのは十中八九リフレクションですね。フィールドをpublicにする手もあるけれど、あまりしないかな。 コンテナでライフサイクルを管理したいオブジェクトは、プロダクトコードではコンテナ外で使いません(やっちゃいけない)が、テストコードではありえます。

問題点は、(リフレクションを使わない場合)テストでコンテナを使わないといけなくなります。 コンテナを使うと重くなります。 コンテナの重さはキャッシュやコンテナの高速化などで対応できなくはないです。 とはいえ、テストの重さがコンテナの起動時間に支配されている開発現場において、適切な対応がされることは稀です。 対応せずに放置すると、単純に時間がかかるのでテストの実行機会が減ります。とても残念ですね。

もう一つ問題があって、このオブジェクトがコンテナに依存することです。 せっかくDIコンテナを使うことで依存関係を逆転させたのに、DIコンテナを使わないと適切にインスタンス生成できない物体になっています。 なんというか、本末転倒感があります。 個人的にはこちらの理由で使いたくない。

セッターインジェクション

黎明期によく見たけど、最近はあまり見ないような。

class Hoge {

    Piyo piyo;

    @Inject
    void setPiyo(Piyo piyo) {
        this.piyo = piyo;
    }
}

この状態でHogeをコンテナにインスタンス生成してもらうと、setPiyo(Piyo)メソッドがコンテナ管理されているPiyoインスタンスを引数として呼び出される。 前述のリフレクション云々よりは挙動がわかりやすいですね。

コンテナ外で使う時も、自分でインスタンス生成してからセッターを呼び出せばいいだけです。

問題点は、セッターなこと。 って言うと趣味が入ってるように捉えられるかもですが、セッターを持つってことは、状態遷移があるってことなんです。 状態遷移があるオブジェクトは不安定です。組み合わせるともっと不安定になります。 疑心暗鬼になりながら使いたくないです。

なんだかんだで、コンテナ管理するインスタンスってコンテナ内ではシングルトンなことが多いです。 スコープを省略した場合のデフォルトがシングルトンなことからもわかるかと思います。 これはこれでどうなって思うのですが、まあ現状はそうです。 そんなインスタンスの状態が不安定とか言われると、私は落ち着いて開発できない。

私の心理的な安全のためにセッターインジェクションは使わないでほしいです。 フィールドインジェクションは「リフレクションを使う時点で危険なのをわかってやってる」と自分に言い聞かせることができるので、この点は許容できます。

コンストラクタインジェクション

なぜかあまり見ない。

class Hoge {

  final Piyo piyo;

  @Inject
  public Hoge(Piyo piyo) {
    this.piyo = piyo;
  }
}

この状態でHogeをコンテナにインスタンス生成してもらうと、コンテナ管理されているPiyoインスタンスがコンストラクタの引数として呼び出される。 インジェクションタイミングがわかりやすいのがいいですね。

フィールドインジェクションと比較して「インジェクションする数が増えると積み重なってすごく冗長になる」と言うのを聞きます。 でも、それだけ多くのものに依存している状態がそもそもおかしいと断言できます。

インスタンス生成時点で状態が安定するのが実に良い。 フィールドにfinalつけれるのが嬉しいです。 フィールドもローカル変数もデフォルトfinalにならないかなぁと常々思うけど、これは別の話。

<脱線> フィールドインジェクションはデフォルトコンストラクタなどでインスタンスを生成してから、フィールドにリフレクションで設定します。セッターインジェクションも同じです。 そのため、インスタンス生成直後に値が入ってなかったりします。 その間隙をついてNullPointerExceptionが起こったりすることも無いとは言えません。 実際起こることはあまりないのですが、疑いたくなる気持ちをわかってください。 </脱線>

また、インスタンス生成で必要なものがコンストラクタで明示されています。 テストなどのコンテナの外でインスタンス生成する際も特に制約はありません。

と言うことで、問題点は特に挙げられません。

なお、Springでもコンストラクタインジェクションは@Autowiredとか書かなくてもインジェクションしてくれるようになってたりします。 この辺もコンストラクタインジェクションの追い風。

なぜ複数のインジェクション方法があるのか

コンテナを作る側の都合かなーと。知らんけど。 使う側の事情でこの3種類のインジェクションを使い分けたことは今までなくて、多分今後もない。

インスタンスを生成するためにはコンストラクタを呼ぶ必要があります。 「コンテナ管理するにはデフォルトコンストラクタが必要」なんてのを見聞きしたことはあるでしょう。 これはインスタンス生成時にClass#newInstance()を使ってるから…なんて理由ではないと思いますが、ともかく単にインスタンス生成するだけならデフォルトコンストラクタが楽です。 コンテナに限らず、インスタンスを生成するライブラリがデフォルトコンストラクタを要求するのは珍しい話ではありません。 そう言えばClass#newInstance()がJDK9でDeprecatedになりますね。関係ないですけど。

話を戻して。コンテナを作る側の都合ではフィールドインジェクションが楽です。 コンテナ管理対象のインスタンスを一通り生成してから必要なインスタンスをインジェクションしていけばいいだけだからです。

コンストラクタインジェクションでは、コンストラクタが要求するインスタンスを先に生成する必要があります。 インジェクション対象のインスタンスがなければコンストラクタを呼び出せないですから。 こうなってくると、インスタンスの生成順を考慮しないといけなくなるわけで、コンテナが多段構成になっていたり、定義の上書きがあったりするととても面倒です。 依存関係の循環なども問題になってきます。

セッターインジェクションでも生成順は問題になってきます。 なぜならインジェクションで呼び出すのはメソッドなので、メソッドで引数のオブジェクトのメソッドが呼び出されるかもしれません。 「セッターではメソッド呼び出しをしないでください」とか言うわけにもいかないので、インジェクションするオブジェクトは完成している必要があります。

そんなこんなで、フィールドインジェクションが作る側の都合は楽っぽい。 その次にインスタンス生成がデフォルトコンストラクタでいいセッターインジェクションが楽で、一番面倒なのがコンストラクタインジェクションだったり。

まぁ、使う側にとっては知ったこっちゃないです。 使う側は安定した完成したインスタンスが手に入ればいいんで。

で、結論

コンストラクタインジェクションを使いましょう。

ことしのふりかえり

2016年12月31日です。 最近の日付表記のマイブームだと 2016-12-31T22:11 です。 大晦日ですね。

さて、今年を振り返ってみよう。 来年になったら忘れる、と言うかもう忘年会してるから忘れちゃってるんですが、なんとか引っ張り出して。

今年もなんだかんだで色々ありました。

対外的なこと

対外的な活動だとこんな感じですかね。

  • WEB+DB PRESSで「Javaの新定石」の連載
  • 2月関ジャバで「よくある業務開発の自動化事情」の再演
  • JJUG CCC 2016 Spring で「テスト自動化のまわりみち」の登壇
  • JJUG CCC 2016 Fall で「どうしようJUnit 5」の登壇
  • 大阪Jenkins勉強会 でのLT
  • 合同勉強会 in 大都会岡山 -2016 Winter- でのLT

お仕事のこと

お仕事では相変わらずJavaなプロジェクトでよくわからない立ち位置でいました。 プログラマなんですけどね。リーダー補助とか、なんだかんだと。

あと、今年の最初には全く予想もしてなかったのですが、なんだかんだで仕事がなくなりました。 なんの準備もしてないので1月から無職。自分でもびっくり。

今年の目標

今年の始まった頃は「xxxxやるぞー」とか思ってたりもしたのですが、 できたものもあれば、できなかったものもあり。

そういえば今月書いてたこの目標。

今年の目標を「1ヶ月に1エントリ以上のペースでブログを書く」にします。

Qiitaに1つ書いたので、それで12ってことで!

来年の抱負

らいねんかんがえる。

ご挨拶

みなさま、今年も一年お世話になりました。 来年もなんとかよろしくお願いいたします。