【イベントレポート】Javaエンジニア勉強会

f:id:pcads_media:20210329181448j:plain

こんにちは!TECH Street編集部です!
今回は2021年2月25日(木)に開催した、 「Javaエンジニア勉強会」のイベントレポートお届けいたします。

今回の登壇者は初代Javaチャンピオンの櫻庭 祐一さん!

f:id:pcads_media:20210329181823j:plain

今回は「モダンなJavaの書き方」をテーマに、ライブデモを交えながらセミナーをしていただきました◎

イベント冒頭では、参加者の皆さまにアンケートを実施。今回のイベントにご参加いただいた方々のJava活用歴は、、、10年以上活用されていると回答した方が最も多い結果となりました…!

f:id:pcads_media:20210329181857p:plain

ここからは櫻庭さんのセミナーをご紹介。記事後半では、質疑応答の内容もご紹介していきます!

「モダンなJavaの書き方」(櫻庭 祐一/初代Javaチャンピオン)

Java チャンピオン(Java Champioin)は現在、全世界におよそ300名いて、日本には5名のチャンピオンがいるのだとか。

今回のセミナーの講師である櫻庭さんは、日本で1人目のJavaチャンピオンで、なんとJava歴は26年!まずは、Javaが変革する背景からお話を伺いました。

Javaが変革する背景

f:id:pcads_media:20210329181944p:plain

まずはハードウェア面での変革について。20年前はシングルコアが当たり前だったものが、現在はマルチコアが当たり前になっています。マルチコアだけでなく、Vector ComputingやGPGPUが導入されて一つの命令で複数のデータを計算することができます。メモリも大量に増加し、通信も超高速となりました。

このようなハードウェアの変革に伴い、ソフトウェア側も様々な変革が起きました。システムが巨大かつ複雑となり、更新は頻繁に。技術的には、マイクロサービス・コンテナ・AI/MLなどの導入という変化があります。このようなサービスは、ハードウェアの変革があったからこそ生まれたものであると櫻庭さんは語ります。

Javaのトレンド

f:id:pcads_media:20210329182223p:plain

Javaには様々なトレンドがあるそうですが、今回は特に3つのトレンドをご紹介いただきました。

①可読性の重視
システムが巨大になり、ソースコードも増えていく中で、コードを見ただけで何をしているかわかる「可読性」は非常に重要なポイントとなりました。

可読性をあげる機能として、var、Switch式、ラムダ式などが追加され、簡潔に可読性のあるコードが書けるようになったそうです。可読性はいつの時代も重要視されており、Java 8、Java 11、Java 17で毎回機能が追加されています。

②関数の活用
Javaの場合ラムダ式で関数を表しますが、関数型言語の特徴を取り入れてきているそうです。ラムダ式をつかったStream APIや、Flow(Reactive Stream)などの機能が追加されています。関数については、Java11までである程度の機能が追加されているとのこと。

③Immutable
関数やImmutableは、マルチスレッドで動かす際に非常に重要な概念なのだとか。こちらは最近になってより重要になってきており、イミュータブルなクラスを簡単に作成できるRecord機能がJava17で正式に追加されるのだとか。

書き方のポイント

f:id:pcads_media:20210329182504p:plain

まずは、ラムダ式を自由に使えるようになることが非常に重要だそうです。また、オブジェクトの状態変化をなるべく避けることも重要なのだとか。昔のオブジェクト指向では、データと手続きがあり、データを変化させるようなイメージで語られていたそうですが、今は逆に、一度オブジェクトを作成した後は、状態を変えないように、そしてできれば変えられないようなクラスを作る方法が推奨されているのだそうです。

この背景としては、もちろん可読性を向上させるという意図もありますが、状態が変わらないとマルチスレッドになりやすいため、マルチスレッド化が容易になるという理由もあるそうです。また、処理の切り分けも容易になるのだとか。大きなシステムでは状態変化を完全に避けることは難しいとは思いますが、変化するところと変化がないところを切り分けて作る意識が大切なのだそうです。

ここから実践!

public classのメソットを使って、顧客リストから製品IDごと購買数マップに変換する様子をいにしえの書き方→Java5での書き方→Java8以降という流れで見せていただきます。

public class Customer {

// 購買履歴を製品IDリストとして返す
public List getHistory() { …

f:id:pcads_media:20210329182645p:plain

いにしえのJavaで書くとこんな感じとのこと。

-----------
public Map convert(List customers) {
 Map productCountMap = new HashMap<>();

  for (int i = 0; i < customers.size(); i++) {
 Customer customer = customers.get(i);

 List ids = customer.getHistory();
 for (int j = 0; j < ids.size(); j++) {
  int pId = ids.get(j);
  Long productCount = productCountMap.get(pId);
  if (productCount == null) {
   productCountMap.put(pId, 1L);
   } else {
   productCount++;
   productCountMap.put(pId, productCount);
   }
   }
  } return productCountMap;
}
-----------

①顧客リストを受け取り、それをfor文で回す
②一つ一つオブジェクト、ヒストリーを取り出す
③二重ループでヒストリーIDを取り出し、マップから購買数を取り出す
④もしない場合は新しく作る、ある場合はインプリメントしてマップに入れ直す
という処理を行うとのこと。

これの何がいにしえかというと、

f:id:pcads_media:20210329182741p:plain

・ミュータブルなマップを使っている
・ループ制御とロジックが混在しており、わかりにくいループカウンターになっている
・Longを小文字のLongにするとNullPointerExceptionが発生するためオードボクシングができない

f:id:pcads_media:20210329182911p:plain

これを、Java 5の書き方にすると、下記の通り。for-each文に変更することで、スッキリします。

-----------
public Map convert(List customers) {
 Map productCountMap = new HashMap<>();
 for (Customer customer: customers) {
  List ids = customer.getHistory();

 for (int pId: ids) {
  Long productCount = productCountMap.get(pId);
  if (productCount == null) {
   productCountMap.put(pId, 1L);
   } else { productCount++;
      productCountMap.put(pId, productCount);
    }
   }
  }
 return productCountMap;
}
-----------

そしてこれをラムダ式とStreamで変えていきます。
ラムダ式とStreamと聞くと、難しいと感じる人もいるかと思いますが、段階的に変えていけばそれほど難しくないのだとか。

f:id:pcads_media:20210329184728p:plain
-----------
public Map convert(List customers) {

 Map productCountMap
  = customers.stream()
         .flatMap(customer -> customer.getHistory().stream())
         .collect(Collectors.groupingBy(pId -> pId, Collectors.counting()));
return productCountMap;
}
-----------

これを使うことで、下記のことが実現。モダンなJavaを書くことがきます。

・マップの要素を変更しないのでImmutableにすることが可能。
・flatMapを使うことで、2重ループの展開
・collectメソッドを使うことで、トリッキーなif文の排除

このようにコードが書けるようになるためには、やはりある程度ラムダ式やStream APIを使う経験が必要なのだそうです。

まとめ

以下の3つのポイントを意識して、コードを記述することがおすすめとのこと。

・可読性の向上
・関数の活用
・状態変化を避けること

特に状態変化についてはRecord機能を使うとImmutableなクラスが簡単に書けるようになるのでそれを活用してほしいとのこと!

また、新しい言語仕様や機能もどんどん増えてきているそうなので、それらを積極的に試すことがおすすめだそうです◎

speakerdeck.com

まとめ


今回は、初代Javaチャンピオンの櫻庭さんによる実演から、たくさんの質疑応答(※次のページで紹介します)と盛りだくさんな内容となりました!Javaを使うエンジニアさんにとって役立つ発見や気づきも多かったのではないでしょうか。

参加者の声

・これだけ回答いただける機会はホントうれしい!
・Javaチャンピオンの櫻庭様のお話を伺えて、何よりの光栄でございました。最近のJavaのトピックや、アンケートのご回答など、とても勉強になり、モチベーションが上がりました。
・さくらばさん!親切丁寧!
・モダンな実装方法を知ることができて非常にうれしかったです。また、質問に答えていただく時間が多くあったため非常に勉強になりました
・櫻庭さんへの色々なアンケートやオンラインコーディングが見られてよかったです。
・実際に櫻庭さんのデモが実際にどういう感覚で書き換えているのか分かりやすく、勉強になったから。

ビデオオンの参加者の方々と運営の集合写真

f:id:pcads_media:20210329190244j:plain

次のページでは参加者からのご質問と櫻庭さんのご回答を紹介します◎

 >>Q&A


参加者からの質問

(Q)できることが多くて習得・活用方法がいまいち分かりません。
(A)今、ライブラリなどがすごく大きくなっているので、大変だと思います。逆にSpring Bootなどのフレームワークを使うことで、フレームワークに則った書き方をしていくことで、いつのまにか書けるようになったりもします。なので、ライブラリを使い、自分が困っていることを解決できるようなものを自分で作ってみるというところから初めてみるのがいいのではないかと思います。

(Q)最近は減りましたが、案件で採用されるJavaバージョンが2世代前だったりします。
(A)Javaの場合はJava8で止まっていることが割と多いと思いますね。

(Q)JDKのバージョンアップはどのような基準・頻度で行われていますか?
(A)やはりLTSが一つのタイミングとなると思います。LTSは3年ごとに出るのでそれを目安に行えばいいと思います。

(Q)Java、5年前といまで一番変わったところってどんなところですか?
(A)5年前というとJava 8ですが、Java 8の後は、言語仕様はかなり変わっていますね。
今、変わりつつあるもので一番大きいのはパターンマッチングなのですが、まだプレビューの状態で、正式にはなってないですね。しかしおそらく、それが正式に入ると多分一番大きく変わるのはパターンマッチングだと思います。普通のプログラムを書くのであればJava 10からSwicth式になったというが結構大きいと思います。

(Q)チャンピオンの立場で思う、Javaの好きなところ、嫌いなところを聞いてみたいです!
(A)Javaは歴史あるというのもそうですが、コミュニティが発達していて、コミュニティでJavaを盛り上げていこうというのが非常に強い言語。コミュニティがいろいろやっているのは面白いなと思います。最近もJavaはどんどん変革していって、新しいものをどんどん取り入れているところとかも好きですね。逆に嫌いなところですが、あまり嫌いなところはないのですが、過去との互換性にとらわれているところですかね。歴史が古いので、過去との互換性にとらわれて新しいのが入りにくくなっているという部分は、少し嫌かなというところがあります。

(Q)最近会社の先輩や知り合いのエンジニアから、これからはJavaではなくKotlinだと言われるのですが、どう思われますか?
(A)Androidを使うのであればいいと思います。いわゆるBetter Javaですね。全てをJavaという世界はもうないと思うので、要所要所で使い分けて、得意な分野で使えば良いと思います。

(Q)Jigsawがあまりライブラリ群含め普及している感じがしないのですが、最近この辺りはどのような動きあるのでしょうか?
(A)Jigsawはなかなか普及していないですが、ライブラリを書くのであればぜひ、Jigsaw対応してほしいと思います。

(Q)Javaを開発するのに最も適しているIDEは何でしょうか?
(A)今勢いがあるのは、IntelliJ IDEAですかね。今日も使いました。

(Q)櫻庭さんが今まで読んだ技術書の中で、印象に残っている/ためになった作品はなんですか?
(A)Javaに関係した本の中ではEffective Javaですかね。初心者には少し難しいですが中級・上級者になりたいのであれば絶対読むべき本だと思います。

(Q)Java Championにはどうなると選ばれるのでしょうか?
(A)チャンピオン同士の互選で選ばれます。まず誰かに推薦してもらわなければなりませんので、近くにいるチャンピオンを探しましょう。チャンピオン同士が次誰にするかを話し合いながら、推薦する人を選んでいます。

(Q)一番好きな Java のクラスは何ですか?
(A)Optionalクラスが好きですね、ストリームも結構好きですが。Optionalはなかなか使っていただけないので、ぜひ使ってほしいです!

(Q)他にも古(いにしえ)のJavaの書き方があればおしえていただきたいです!
(A)Genericsがなかったので、Genericsを使わないで書くというやり方でした。あとはif elseですかね。

(Q)JVMに苦手意識をもっています。。
(A)Javaのアプリケーションを書く上でJVMをそこまで意識する必要はないです。最近は早いので、前のように止まってしまうということは減っています。私もあまりJVMの構造はよく見ていないです。

(Q)テキスト一周したくらいのJava初学者が次に取り掛かることはなんでしょうか?なにか成果物を作るとしたら、どんなものがおすすめでしょうか?
(A)自分が使いたいものを作るのが一番いいと思います。好きなものを作ってみるのが、一番モチベーションが上がると思います。

(Q)Reactiveよくわからないです…。 
(A)Reactiveは使わないとわかりにくいところがあるのですが、Reactiveなフレームワークは色々出ていているので、そういったフレームワークを使った時に理解すればいいと思います。

(Q)プロダクトの規模などにもよるかもしれませんが、レガシーなプロダクトコード(副作用があるmutableなコード)を書き換えていくためのコツなどありますか? テストコードなども作りにくく、新しくクラスを作って既存コードを新しいクラスを呼び出すように徐々に置き換えていく方法を行なっていますが、他に良い方法があれば教えていただきたいです。
(A)テストコードが書けないとやっぱり辛いですよね。いい方法は私も本業では研究に近いものをやっているので、古いものを保守するよりも、新しいものを作っていっているので、私も知りたいです。

(Q)櫻庭さんは「継承よりコンポジション」に対してどのようにお考えですか? 肯定派ですか?否定派ですか?
(A)継承でしか書けない時しか継承は使わないです。ただ継承が絶対ダメというわけではなくて、やはり適材適所だと思います。昔ほど継承を使うと問題が簡単になるということは少なくなっており、やはりコンポジションで書くほうが多くなっているのではないでしょうか。

(Q)先ほどのfor文などをStreamやラムダ式に置き換えるのを覚えるのには、ひたすら書き換えるしか手段ないでしょうか?正直先ほどの説明では話してくれている内容は理解できましたが、自身で再度書き換えようとしても難しいです。
(A)初めはやはり書き換えからやるのがいいと思います。いきなりStreamで全部書こうとしてもどこから手をつけたらいいかわからないと思うので、一回手をつけてみるというのがおすすめです。あとは、何をやりたいのかをちゃんとスペックの中に当てはめられるようになれば、自分で書けるようになると思います。

(Q)Mapコレクションにはまだ現在Immutableにする(BigDecimalやStringみたいに)手段は無いですよね?[自分でオーバライドして用意でもしない限り]
(A)unmodifiableMapというメソットがあるのですが、これを使うとImmutableを作ることができます。どこまでImmutableかというと、Map自体を変更できなくなるところまではできます。

(Q)櫻庭さんはアプリケーションの開発の際にDDDのエッセンスを取り入れていたりしますか?
(A)私はアプリケーションを作っていないのであまりDDDは使っていないですが、要件定義を落とし込む時にはDDDを若干使ったりもします。

(Q)Java EE(Jakarta EEの方が今はいいのでしょうか)サーバーに関する動向など何か共有して頂ければ、知っておいた方がいいことあればお話し伺いたいです。
(A)どっちかというとSpring系の方が動きは活発なような気がします。新しいJakarta EEがJava 11に対応できたので、これからどんどん変わっていくのではないでしょうか。

(Q)ヒープダンプはMAT(Eclipse Memory Analyzer)が有名かと思いますが、スレッドダンプを解析する際に良いツールなどありますでしょうか?
(A)ミッションコントロールなどは使いますが、私自身はあまりヒープダンプをがっり調べることはないです。

(Q)Javaが過去の互換性を捨て去ることはありますでしょうか?
(A)それはないと思いますが、昔ほど互換性にうるさくは無くなってきています。

(Q)Stream API やラムダ式で実装すると、デバックやエラーログからエラーの原因をたどりづらいと思うのですが、なにか工夫している点はありますか?
(A)どうしてもエラーが出てしまう時は、Streamを一度切ってしまうといいでしょう。あとは、ピークメソッドという何もやらないメソットがあるのですが、そういったものを使ってエラーの原因を調べることはしています。

(Q)10年後の Java はどうなっていると思いますか?
(A)予想もつかないですね。昔、ビル・ジョイという有名な方がいたのですが、その方が10年後どうなっているかと聞かれた時も「わからない」と答えていました。今とは予想もつかないような状態になっているかもしれません。

(Q)抽象クラスってどういうときに使えばいいでしょうか?
(A)抽象クラスはあまり登場しないですね。インターフェースにメソットが実装できるようになったので、あまり使わないですが、実装クラスが複数あって、共通部分があるのであれば抽象クラスを使うのがいいと思います。

(Q)デバックを行う際に便利なツール、工夫されていることなどありますでしょうか?また、ライブラリ側、Java側など深いところでエラーになった場合、どのように解析すればいいでしょうか?
(A)デバックはIDAのデバッガを使っているだけですね。ライブラリはほとんどがJavaなので、ソースを読むことが多いです。JVMの内部に関してはあまり追うことはないですね。もしくは英語なのでハードルが高いですが、メーリングリストで投げてみるという手もあります。

(Q)アルゴリズムの勉強をされたことがある場合、どの様に勉強したのか教えて下さい。
(A)逆にアルゴリズムの方が好きで、Cの時代からやっていましたが、主に本を読んで勉強していました。あとは、何か解決したい問題があるのであればそれに関連した書籍を読むなどですかね。

(Q)ラムダ式・Stream API確かにデバッグしにくい。 デバッグするときストリーム切ると良いっていうのは、ストリームをfor文とかに置き換えて再度実行させてデバッグするってことでしょうか?
(A)

f:id:pcads_media:20210329191029p:plain

このような形でflatMapした段階でローカル変数に入れてしまってデバッガでトレースをするときもやりやすくします。

イベントレポートは以上となります◎
櫻庭さん、ご参加いただいたみなさまありがとうございました!
次回のイベントもお楽しみに♪