- 投稿日:2019-12-05T21:54:19+09:00
Swift Network.framework Study 20191205「Server TCP」
Study
Network.framework
Study:Server側環境
Client:Java、NetBeans
Server:Swift、XcodeClient Source Java
package example.java.network; import java.io.PrintWriter; import java.net.Socket; public class ExampleClientSocket { public static void main(String[] args) { try(Socket socket = new Socket("localhost", 7777); PrintWriter printWriter = new PrintWriter(socket.getOutputStream(), true);) { printWriter.println("Example Send Data"); } catch(Exception e) { System.out.println(e); } } }Server Source Swift
NWListerを使用する
コネクション確立後のNWConnectionに受信処理登録、開始実施main.swift
import Foundation import Network var networkServer = NetworkServer() networkServer.startListener() while networkServer.running { sleep(1) }NetworkServer.swift
import Foundation import Network class NetworkServer { public var running = true func startListener() { let myQueue = DispatchQueue(label: "ExampleNetwork") do { let nWListener = try NWListener(using: .tcp, on: 7777) nWListener.newConnectionHandler = { (newConnection) in print("New Connection!!") newConnection.receiveMessage(completion: { (data, context, flag, error) in print("receiveMessage") let receiveData = [UInt8](data!) print(receiveData) }) newConnection.start(queue: myQueue) } nWListener.start(queue: myQueue) print("start") } catch { print(error) } } }
- 投稿日:2019-12-05T21:41:31+09:00
見せて貰おうか、Amazon新サービス「CodeGuru Reviewer」の性能とやらを
はじめに
これは、NTTコミュニケーションズ Advent Calendar 2019 5日目の記事です.関連記事は目次にまとめられています。前日は@TAR_O_RINさんによるTektonでCI/CDパイプラインを手の内化しよう - Qiitaでしたね!
あなたはだ〜れ?!
昨年書いた深層学習を利用した食事画像変換で飯テロ - Qiitaに既に書いてあるので省略します。好きな言語はObjective-Cです。お世話になった言語もObjective-Cです。嫌いな言語もObjective-Cです。最近はPythonしか書いてないのでマンネリ化が激しいです。最近のマイブームは夜な夜なサイクリングロードです。一緒にサイクリングしましょう。
モチベ
複数人で開発を行う上でコードレビューを行う文化は非常に重要ですよねー。ただ、
- 開発者はコードレビューをする時間を確保する必要がある
- 入念にチェックはしても一部の見逃しは発生する(人間だもの)
- レビュー担当者の技術レベルによってレビュー濃度がまちまち
などなど、他人のコードを読むのは大変勉強になるし、もはやレビューに何も抵抗は無いですが、時間が無い時などは後回しにしがち。ぶっちゃけコードレビューって大変ですよね。自動でコードレビューしてくれたらなあ。。。なんてそんなあなたに、
Amazon CodeGuru - Amazon Web Services
今北産業(三行まとめ)
- 2019年12月3日(米国時間)に公開された機械学習ベースの自動コードレビュー新サービスAmazon CodeGuruのCodeGuru Reviewerについての検証
- Amazon CodeGuruの利用方法について導入方法の解説
- Amazon CodeGuruにより自動生成されるレビューコメントへの言及
CodeGuruとは
CodeGuru自体の説明は既にいくつかのメディアが取上げているので概要についてはリンク先に委ねようと思います。要は機械学習ベースの自動コードレビュー(CodeGuru Reviewer)およびプロファイラー(CodeGuru Profiler)機能を提供するサービスです。CodeGuru ProfilerについてはAmazon CodeGuruを試してみた - Qiitaにて記載がありますが、CodeGuru Reviewerの方はあまり記載が無かったので前記のブログを補填する内容になっています。
- 英語記事
- 中国語記事
- 日本語記事
実際のCodeGuruの基調講演はYouTubeに公式がアップロードしていますので以下のYouTube動画を御覧ください。(Qiitaはインライン表示できないので画像リンクとしています。01:58:40からAmazon CodeGuruの発表が始まります。)
簡単な使用手順
ここでは、Amazon CodeGuruの利用方法について導入方法の解説を行います。
CodeGuru Reviewer利用の際の下準備編
自動レビュー機能を試す前にCodeGuruと自動レビュー対象とするレポジトリの紐付けを行います。Preview版ではGitHubおよびAWS CodeCommitをサポートしているようです。今回はGitHubによる検証を行いたいので、GitHubアカウント、GitHubレポジトリとの紐付けを行う想定で記載します。CodeCommitとの紐付けについては公式ガイドGetting Started with Amazon CodeGuru Reviewer - Amazon CodeGuru Reviewer User Guideを参照しましょう。
1. AWSマネジメントコンソールからCodeGuruを選択する
AWSにログイン後、 サービスを検索する の入力欄に CodeGuru と入力して、表示項目をクリック。
2. リージョンの変更
2019/12/5時点では、サポートリージョンが図中の5リージョンのみなので、もし同じ画面に遷移した場合は、何れかのリージョンを選択。今回は米国東部(オハイオ)を選択します。
3. Amazon CodeGuruからCodeGuru Reviewerの選択
Amazon CodeGuruのホーム画面です。今回はCodeGuru Reviewerによるコードの自動レビュー機能を試すので図中のいずれかをクリックしてレポジトリとの紐付けを行います。
4. CodeGuru ReviewerとGitHubアカウントの紐付け
CodeGuru Reviewerによる自動レビューの対象とするレポジトリの紐付けを行います。この時、CodeGuru Reviewerによる自動レビューを行う専用のGitHubアカウントを作成することをオススメします。公式ガイドGetting Started with Amazon CodeGuru Reviewer - Amazon CodeGuru Reviewer User Guideにも記載されていますが、専用のGitHubアカウントを介してCodeGuru Reviewerによるレビューが行われるので、人手によるレビューなのか自動か否かの混乱を防げます。また、複数のレポジトリを自動レビュー対象にする場合は、レポジトリに新規に作成したCodeGuru Reviewer専用のGitHubアカウントをレポジトリにCollaboratorsとして招待すればOKです。※ GitHubアカウントとの紐付け後に別のGitHubアカウントに変更する場合はブラウザのCookieを削除する必要があります。
5. CodeGuru Reviewerとレビュー対象レポジトリの紐付け
GitHub認証が正常に終了したらConnect to GitHubがConnectedへと変わるはずです。これでCodeGuru ReviewerとGitHubとの紐付けが完了します。次に、Repository locationのリストから自動レビューの対象とするレポジトリを選択してAssociateボタンをクリックすれば設定は終了です。(めちゃ楽)※ CodeGuru Reviewer専用のGitHubアカウントを自動レビュー対象レポジトリへ事前に招待して承認しておけば、勿論プライベートレポジトリも全て表示されます。また、Organizationsについても同様です。
6. CodeGuru Reviewer紐付け完了ステータスの確認
Associateボタンをクリック後は以下の画面に遷移します。紐付け直後のStatus部分はAssociatingと表示されますが、(レポジトリの規模によりますが)数分程度でAssociatedへと切り替わり下準備は完成です。(公式ガイドGetting Started with Amazon CodeGuru Reviewer - Amazon CodeGuru Reviewer User Guideでは数分程度と記載してありますが、空のレポジトリということもあり、今回は数秒でした。)※ ブラウザの更新か、画面中のAction左にある更新ボタンを押す必要があります。
簡単な検証
ここでは、Amazon CodeGuruから実際に自動レビューコメントを受けるまでの検証を行います。※ ここからは実験みたいなものなので文章の雰囲気が変わるやもしれません。
CodeGuru Reviewerによる自動レビューの例その1:まじめなコード編
今回はむかしのむかしに書いたコードをぶっこんで見ることにしましょう。現状はJavaのみのサポートのようなのでそれ以外のコードおよび変更点は多分無視されます。CodeGuru Reviewerによる自動レビューのトリガーはPull Requestにより作動します。おもむろにPRを出してやりましょう。
するとCodeGuruのDashboardに変化が見られると思います。色々と実験をしていたのでLines of code analyzedが実際と乖離していますが、自分が出したPRと書いたコードの行数が簡易的に表示されます。※ Reviewer recommendationsはいくら待っても変わりませんでした。バグなのかな?
(数分後…)適当にPR画面を見ていると何やらコメントが11件来てる。。。
恐る恐る確認してみると…なんか色々書かれていて、公式ガイドRecommendations in Amazon CodeGuru Reviewer - Amazon CodeGuru Reviewer User Guideによると以下5項目の観点からレビューをしているっぽい?AWS best practices、Concurrency、Sensitive information leak preventionについては今回のPRには含まれていないはずだからそれっぽいレビューコメントはついていないみたいだ。
The following kinds of recommendations are provided:
- AWS best practices
- Concurrency
- Resource leak prevention
- Sensitive information leak prevention
- Common coding best practices11件のコメントの中でユニークコメントだったのが以下の3つで残りはほぼResource leak preventionに関する指摘で全文まったく同じコメントレビューが記載してあった。問題点と解決策、参考リンクなど簡潔なレビューだけどどうやって生成してるんだろう…
CodeGuru Reviewerによる自動レビューの例その2:ウンコード編
皆さんはウンコード・マニアという神サイトはご存知だろうか?その名の通りウンコード撲滅を目論む崇高な神サイトである。読めば読むほど一度は…と心に刺さるものがあるので是非訪れてほしい。
さて、今回はウンコードをCodeGuru Reviewerにレビューしてもらおう。そう君は今日からUnCodeGuru Reviewerなのだから。
代表的なウンコード
現状のCodeGuru ReviewerはJavaのみのサポートであるので、Java ウンコード-ウンコード・マニアから引用させてもらおう。有名所で申し訳ないが好きなのはやっぱり波動拳ネスト型ウンコードである。(※ 画像はIndent Hadouken : ProgrammerHumorから引用。redditで議論されてて楽しい。)
もちろん、ウンコード・マニアも扱っているので、[Java] フルHD推奨。-ウンコード・マニアを引用する。
// [[Java] フルHD推奨。-ウンコード・マニア](http://unkode-mania.net/view/5040f47b9b6066a52e000003)を一部改変。 public class ClassSample { public static void main(String args[]) { int proc = 0; try { if ( proc == 0 ){ // 正常処理 try { if ( proc == 1 ){ // 正常処理 try { if ( proc == 2 ){ // 正常処理 try { if ( proc == 3 ){ // 正常処理 try { if ( proc == 4 ){ // 正常処理 if ( proc == 5 ){ // 正常処理 // まだまだ続くよ! } else { // 異常処理 } } else { // 異常処理 } } catch(Exception e) { // 例外処理 } } else { // 異常処理 } } catch(Exception e) { // 例外処理 } } else { // 異常処理 } } catch(Exception e) { // 例外処理 } } else { // 異常処理 } } catch(Exception e) { // 例外処理 } } else { // 異常処理 } } catch(Exception e) { // 握りつぶす } System.out.println("Hello! World!"); } }ウンコードのPRを同様に出す
さて先程と同様にPRを出して自動レビューのトリガーを引こう。…が待てよ待てとも一向にレビューが来ない…(ここでボロクソにレビュー来たら面白かったんですがね)前述の5項目の観点外だから音沙汰が無いのか、糞すぎるのか何れにしても何らかの反応はほしいですね。。
まとめ
つい先日公開されましたAmazon CodeGuru - Amazon Web Servicesの導入方法と実際に生成される自動レビューについて簡単に検証してみました。【IT】「Amazon CodeGuru」発表。機械学習したコンピュータが自動でコードレビューは5chスレですが、たしかに自動でコードレビューしてくれても実装難易度爆高な方法を提示されたりなんてことがあったらそれはそれで面白そうです。(囲碁・将棋であったように)実装難易度順に候補複数提示してくれるとかだったら神ですね。
また、今回はまだ試していないマルチスレッディング、AWS API等に関するコードレビューは時間があるときにでも試してみたいです。後、コードレビューの正当性についても言及したい。特にマルチスレッディング系はどんなレビューが生成されるのか気になる。
なにわともあれCodeGuru自体はプレビュー版なのとJavaのみのサポートということもありまだまだ開発途上ではありますが、実際に自動で生成される様を見ると、何やら若干の未来感を感じ心躍る気分になりました。早急にPythonとSwiftに対応してくれ〜〜!使うから!笑
明日は@jkr4869さんの記事です!!お楽しみに〜〜〜!!!
- 投稿日:2019-12-05T19:18:42+09:00
JavaFXでHelloFX
はじめに
はじめまして。
この記事はITRC Advent Calendar 2019の5日目の記事になります。
今回はJavaFXでHelloWorldの様に「Hello,JavaFX」を表示させるところまでやりたいと思います。環境
- ArchLinux
- Java11
- Eclipse
HelloJavaFX
では、HelloJavaFXを表示してみましょう。
JavaFXのダウンロード
こちらからJavaFXを任意の場所にダウンロードしてきます。
今回はVersion11.0.2をダウンロードしました。
Eclipse 1
次にEclipseを開いて、メニューバーから
ヘルプ→Eclipseマーケットプレースを選択します。
検索スペースに「e(fx)clipse」と入力するとでてくるので、それをインストールします。
続いて、新しくJavaFX用のプロジェクトを作成します。今回はHelloJavaFXで作成。
その後に新しくパッケージを作成します。パッケージを作成しないでデフォルトパッケージでやってしまうと後々大変です。今回はhellojavafxで作成。
(そもそもデフォルトパッケージを使うのはあまり良くないとか聞いたので)
module-info.javaは後で作成しますが、今は作らないでおきます。続いて、先程ダウンロードしてきたJavaFXを展開してください。展開すると
legal
とlib
の2つがあると思います。
(Windowsではbin
もあるらしいのですが、Linuxはなかったです)Eclipseに戻って、作ったプロジェクトで右クリックし
ビルド・パス → ビルド・パスの構成 を選択
ライブラリーを選択してモジュールパスを選択し、右側の外部JARの追加を選択します。
先程解凍したJavaFXの中にあったlib
にまでいくと、9個のファイルがあると思うので、その中の.jar
ファイルのみ選択して開くを押してください。これで追加されたので、適用して閉じてください。プロジェクトに「参照ライブラリー」が追加され、先程追加した
.jar
ファイルが入っていると思います。ソースコード
次に、コードを書いていきます。今回はHelloFX.javaで作成。
package hellojavafx; import javafx.application.Application; import javafx.scene.Scene; import javafx.scene.control.Label; import javafx.scene.layout.StackPane; import javafx.scene.text.Font; import javafx.stage.Stage; public class HelloFX extends Application { @Override public void start(Stage stage) { Label label = new Label("Hello, JavaFX"); label.setFont(new Font(50)); stage.setScene(new Scene(new StackPane(label), 500, 400)); stage.show(); } public static void main(String[] args) { launch(); } }内容
Labelの作成
Label label = new Label("Hello, JavaFX");フォント指定
label.setFont(new Font(50));stageにSceneをセット、SceneでPaneを表示(Label)、ウィンドウの大きさ指定(Width,Height)
stage.setScene(new Scene(new StackPane(label), 500, 400));ウィンドウを表示
stage.show();アプリケーションを起動
launch();Eclipse 2
特にソースコードにエラーはでていませんが、実行してみると、コンソールでエラーが表示されて何もできません。
エラー: メイン・クラスhellojavafx.HelloFXを検出およびロードできませんでした 原因: java.lang.NoClassDefFoundError: javafx/application/Applicationここで、プロジェクトを作った時に作成しなかったmodule-info.javaを作成します。
プロジェクトで右クリックし、
構成 → module-info.javaの作成 を選択
モジュール名はなんでも良いですが、デフォルトで表示されているプロジェクト名と同じもので作りましょう。作成されると、すでに中身が書いてあると思います。module HelloJavaFX { exports hellojavafx; requires javafx.base; requires javafx.controls; requires javafx.graphics; }
export
はパッケージの公開。
requires
はモジュールの読み込み。新Javaプロジェクト作成時に作らなかった理由は、最初に作成すると中身が何もないので、このタイミングで作成したほうがそれだけで終わるので楽だからです。
これで実行してみましょう。
今度はしっかり実行できました。
余談ですが、MACを使っている人でJavaFXを使おうとした時に、アイコンは表示されるがウィンドウが表示されないという現象が起こってました。調べたところ、MACで使用する場合には実行の構成の引数にある「XstartOnFirstThread」のチェックボックスをを外すと解決しました。
こちらを参考まとめ
今回はHello Worldみたいな感じでした。
気が向いたら、Hello World以降の何かを書こうかなと思います。
- 投稿日:2019-12-05T18:39:36+09:00
意外と知らないMapの便利な使い方
コレクションの利用
Javaでプログラミングを行う際には List, Map, Set などのコレクションを利用するのはほぼ必須といっても過言ではないかと思います。
その中でもMap はキー・バリュー形式でデータ管理を行えるため、データを事前に取得してMapに詰め込んでおき再利用するといったシーンで多く使われます。特にJava8 から Stream API や ラムダ式、Optional 等が追加されコレクションがより使いやすくなりました。
ただこれらの追加機能の陰に隠れて実は便利なメソッドがたくさん追加されているのはご存知でしょうか。
今回はそんな便利なメソッドをいくつか紹介します。データが取得できない時に初期値をゲット! (getOrDefaut)
会員コード毎にお店で使えるポイントを保持するMapがあったとします。
Map<String, Integer> pointMap = new HashMap<>();ある会員がポイントを保持していない場合0ポイントを返したいといったロジックの場合以下のように記述し取得することができます。
int point = 0; // ポイントを保持している場合ポイントを取得 if (pointMap.containsKey(code)) { point = pointMap.get(code); }いちいち判定するのは面倒だし美しくないですね。
そこで getOrDefault の出番です。int point = pointMap.getOrDefault(code, 0);すっきりと1行で書けましたね。
マップに値が入っていない場合にはある値を取得したいという時にこのようなコードを書くことができます。値を保持していない場合のみセット! (putIfAbsent)
先程のポイント情報に対して、まだポイントを保持していない会員のみポイントを付与するというロジックを書くとします。
// ポイントを保持していない場合ポイントを付与 if (!pointMap.containsKey(code)) { pointMap.put(code, point); }これを Java8からは以下のように書くことができます。
pointMap.putIfAbsent(code, point);このコードでは会員に紐づくポイントが無い場合にのみ point をセットする形になります。
値を保持していない場合初期値をセット! (computeIfAbsent)
ある学生のテストの点数の履歴を追加して保持するMapがあったとします。
Map<String, List<Integer>> scoreMap = new HashMap<>();既に点数が保持されている学生がいる場合は、その点数の後に履歴として新しい点数を加えていきます。
List<Integer> scores = scoreMap.get(code); // 学生の点数がまだ記録されていない場合 if (scores == null) { // 新しい履歴用のリストを作成 scores = new ArrayList<>(); scoreMap.put(code, scores); } scores.add(score);Mapから取得できなかった場合にいちいちListをセットし、そのListに点数を追加する必要があります。
これを computeIfAbsent を使うと以下のように書くことができます。scoreMap.computeIfAbsent(code, k -> new ArrayList<>()).add(score);もし学生のテスト結果が1件も保持されていない場合には新しいListを作成し、既に結果がある場合は既にあるListを返します。
そのまま返されたListに点数を追加しているため1行で書くことができるようになります。値が保持されている場合に値を操作! (computeIfPresent)
授業に参加した学生の出席日数を保持するMapがあり、履修する生徒の学生コードと出席日数0がそれぞれ初期値で入っているとします。
Map<String, Integer> attendanceMap = new HashMap<>();授業を履修していないが学生が来た場合は無視し、履修している学生は出席日数を1増やすとします。
if (attendanceMap.containsKey(code)) { attendanceMap.put(code, attendanceMap.get(code) + 1) ; }これを computeIfPresent を使用すると以下のように書くことができます。
attendanceMap.computeIfPresent(code, (k, v) -> v + 1);computeIfPresent では学生コードに該当する出席日数が存在しない場合には何も処理をせず、該当する学生がいる場合のみ値(出席日数)を増やすことができます。
まとめ
Java8から Stream や ラムダ式など大きく機能が追加されましたが、実は便利な機能がその他にもたくさん増えていっています。
必要な時に必要なAPIをネットで探すのもよいですが、辞書を読んでみるように公式のAPIドキュメントを読んでみるのも面白いですよ。
- 投稿日:2019-12-05T17:33:59+09:00
QuantAnalyzerでEAの安定性を判定する
理屈
安定したEA = 固定ロットでバックテストした際の損益曲線がほぼ直線。凸凹していない。
∴近似直線との相関係数を求めればよくね?
やること
C:\QuantAnalyzer4\extend\Snippets\com\strategyquant\extend\OverviewTab.SQDefault.java
をVSCodeで編集。以下のように書き換え。Java用の拡張機能がインストールされていれば勝手にビルドされるはず。
上書き保存した後、QuantAnalyzerを起動。何かを読み込む。SQDefault.javaの179行目replace("S4_3", "Correlation Factor", stats == null ? NA : d2(stats.getDouble(StatsConst.STABILITY)))結果(右下のCorrelation Factorに注目)
この数値は、最初のトレードと最後のトレードを結んだ直線との相関係数です。
取引回数ベースではなく、時間ベースでの計算のようです。
1に近いほど相関あり、-1に近いほど逆相関ありです。理想は0.99みたいな数字。
このヒストリーは安定して下降しているので、相関ありとなっています。
Equity Controlのところで出てくるやつだから、もしかしたら、最初の30%分のデータかもしれません。
- 投稿日:2019-12-05T17:26:41+09:00
JavaエンジニアがGoをやりはじめて半年経ちました
QualiArts Advent Calendar 2019、7日目担当の朝倉です。
スマートフォンゲームのバックエンドにGoを使いはじめて半年たったのでその中で感じたことを書きます。はじめに
QualiArtsのスマートフォンゲームのバックエンドは、これまでJavaやNodeJSで開発していました。
今年の5月ごろから新規タイトルの開発がスタートし、新たなチャレンジとしてGoを採用して開発を進めています。
現在、新規タイトルのバックエンドエンジニアは私を含め5名で、その全員がもともとJavaエンジニアです。
長年Javaエンジニアだった私達がGoをやりはじめて感じたことを書きたいと思います。
(JavaがいいとかGoがいいとか言語の優劣を話したい訳ではありません)JavaからGoをやりはじめて感じたこと
静的型付け、コンパイル言語の安心感
スマートフォンゲームは、リリース後の運用での開発も活発で場合によっては何年も運用するので静的型付けでコンパイル時にチェックできるのはJavaと同じく安心感を感じます。
静的型付けのおかげでIDEでのコード補完も効きやすいし、他のメンバーが書いたコードも追いやすいです。コードの統一がしやすい
Goは標準でformatterやlinterががついているのでコードの統一がしやすいです。
標準でついてるのでコードの書き方などでチームメンバー内で対立することも少ないです。
私達のチームでは複数のlinterを組み合わせていて、不要なコードや冗長な書き方も指摘してくれるのでコードを綺麗に保つことができてるかなと思っています。標準機能でだいたいのことができる
formatterやlinterの他にも、テストやテンプレートエンジンなどもGoの標準機能に含まれています。
私達のチームでは標準のテンプレートエンジンを使って各種コードや定義ファイルなどの自動生成を行っています。
また、ベンチマークやプロファイルの機能も標準でが簡単に取れるので、Javaで開発していたときよりも頻繁に計測している気がします。コンパイル、起動が早い
Javaと比較してコンパイルやプログラムの起動が早いです。Javaで開発していたタイトルと比べるとまだコードの量が少ないですが、コード書いてコンパイルして動作確認のストレスが少ないです。
また、Javaの時はSpringBootを使っていたのもあって起動にかなり時間がかかっていましたが、Goは起動も早いのでオートスケールとの相性も良さそうです。Interfaceに慣れるの時間かかる
GoのInterfaceはJavaのように明示的にimplementsする形ではなく、interfaceの中にある関数と同じシグニチャの関数が全て実装されているとそのinterfaceの型として扱うことができます。
明示的なinterfaceに慣れていたので、Goのinterfaceの扱い方に慣れるのに苦労しました。type Animal interface { Cry() string } type Dog struct { } func (d Dog) Cry() string { return "ワン" } func AnimalCry(animal Animal) { fmt.Println(animal.Cry()) } func main() { // DogはAnimalインターフェースを満たしているのでAnimal型の引数に渡すことができる AnimalCry(Dog{}) }ちなみにGoにはJavaのような継承がないですが、interfaceで満たせることが多いので今のところ継承がなくて困ることは少ないです。
Collection操作が大変
JavaにあるGenericやStreamAPIなどはないので、GoでSlice(JavaのListのようなもの)やMapの操作は、下記のように結構ゴリゴリの実装が必要で大変です。
// フィルター関数 func Filter(scores []int) []int { ret := make([]int,0) for _,v := range scores { if v > 50 { ret = append(ret, v) } } return ret } // Map変換 func ToMap(cards []*Card) map[int]*Card { ret := make(map[int]*Card) for _, card := range cards { ret[card.ID] = card } return ret }ゲームロジックで頻繁にSliceやMapの操作することがあるので、マスタデータやユーザデータを表すSliceの操作は自動生成しようかと考えています。
例外処理が大変
GoにはJavaのようなtry〜catch〜finallyやthrowのようなエラー処理はありません。
戻り値としてエラーを表すerrorインターフェースを返すことでエラー処理をします。
呼び出し元では、戻り値で返ってくるエラーを処理(さらに呼び出し元にエラーを戻すなど)する必要があります。func Func1(ctx context.Context) error { err := Func2(ctx) // error処理 if err != nil { return err } ・・・ }Goで開発を始めた当初は面倒だなと思ってましたが、半年したらこのエラー処理にも慣れました。
エラー処理がされているかもlinterでチェックしているのでエラー処理を忘れることもなさそうです。3項演算子が使いたくなる
Goには3項演算子がないので、ちょっとした分岐でも下記のようにifで分岐が必要です。
func Func(isSuccess bool) int { value := 0 if isSuccess { value = 10 } return value }特に困ることはないのですが、3項演算子使いたいなぁとたまに思ったりします。
おわりに
今回は、Javaエンジニアだった私達がGoで開発をはじめて感じたことを紹介してみました。
まだGoを使い始めたばかりで日々模索しながら開発を進めています。
これからもGoの開発で経験したことを発信できればと思います。
- 投稿日:2019-12-05T15:30:54+09:00
JavaのHashSetが恋しくなるTypescript信者
圧倒的手抜き記事 @ぶぅちゃんズ Advent Calendar
いつも使っているutils.ts
に書いてあったやつをコピペして終わり。何がしたいか
- JavascriptのSetはプリミティブ型でしか重複なし集合として扱われない
- JavaのHashSetは自分で同値条件を設定できる。同値判定を最小限に抑えるために、hash関数を設定することもできる。
- Javaのやつのほうが便利なことが多いので、HashSet実装する
注意
- Javaは
Java.lang.Object
でメソッドequals
とhashCode
を実装する必要がありますが、同値判定とハッシュ関数は高階関数で実現します。コーーーーード
export class ObjectSet<T> { map: Map<string, T[]> constructor(list: T[], public isEq: (t1: T, t2: T) => boolean, public hash: (t: T) => string) { this.map = new Map(); for(let l of list) { this.add(l); } } add(t: T) { let lst = this.map.get(this.hash(t)); if(lst) { if(lst.every(l => !this.isEq(l, t))) { lst.push(t); } } else { this.map.set(this.hash(t), [t]); } } has(t: T): boolean { let lst = this.map.get(this.hash(t)); return !!lst && lst.some(l => this.isEq(l, t)); } toList(): T[] { return Array.from(this.map.entries()).map(([_, ts]) => ts).flat(); } }
- 投稿日:2019-12-05T14:58:21+09:00
【JPA】一対多のリレーションシップがあるEntityをmany側ごとCreateができない
事象
@OneToMany
+@JoinColumn
で定義したEntityをmany側のEntityごとCreateしたい。→エラーが発生してしまう。
原因
以下記事にて解説されていました。
- 自分が確かめた限りでは、これが確実に動くのは検索のみ。
table_beta.alpha_id
が NULL 不可の場合、上記実装だと INSERT 時にエラーになる(table_beta.alpha_id
に NULL をセットしようとしてエラーになる)。
- Hibernate であれば、
@JoinColumn
にnullable=false
を設定することで INSERT, や DELETE も動くようになる。- Eclipse Link では、
nullable=false
を設定してもやはり INSERT でエラーになった。table_beta.alpha_id
が NULL 可の場合は、 Eclipse Link, Hibernate ともに上記実装で INSERT, DELETE が動作した。JPA における一対多のリレーションシップ - EclipseLink - なんとなくな Developer のメモ
(a) の単方向の場合、データ登録時に下記のような挙動となるため、product_id へ外部キー制約を付けたりすると不都合が生じます。 (product_id のデフォルト値を 0 としているのもそのためです)
(1) product_id を指定せずに product_variation へ insert 文を実行
(2) update 文で product_id を設定なるほど、先にidをnullでinsertしたあとにupdateを行うんですね。
Eclipse Linkを使用しているのでお手上げそうです。解決
many側のidのNULL制約を外しました。
同じエラーにあたった人の参考になると幸いです。
- 投稿日:2019-12-05T14:46:07+09:00
ジェネリックスを見ても逃げない
この記事はチームラボエンジニアリングアドベントカレンダー11日目の記事です!
はじめに
Javaで開発していると、以下のようなジェネリックスにぶち当たることがあると思います。僕はこのようなコードがあると逃げていました。。。
そうは言っていられず、自分でジェネリックスを扱わなければいけない場面があり、調べて自分なりに理解できたのでそれをここでまとめようと思います。List<?> hoge; List<? extends Number> hoge; List<? super Integer> hoge; class Hoge<E extends Number> { private E hoge; public E get() { return this.hoge; } public void set(E hoge ) { this.hoge = hoge; } public <T> T fuga(T hoge) { return hoge; } public <E> E hoge(E e) { return e; } }ジェネリックスとは
ジェネリックスとは、
List<E>
で書かれている<E>
の部分のことをジェネリックスと言います。
- クラスやメソッドを汎用的に使いたい
- 汎用的に使うクラスやメソッドを制限をかけて安全に使いたい
このようなときに、ジェネリックスを使います。
まず、ここで扱うJavaの世界には以下のWrapperクラス、Animalクラス、Dogクラス、MiniDogクラス、そしてObjectクラスしかないと仮定して話を進めていきます。
Animalクラス、Dogクラス、MiniDogクラス
class Animal {} class Dog extends Animal {} class MiniDog extends Dog {}AnimalクラスをDogクラスが継承し、そのDogクラスをMiniDogクラスが継承していることとします。
Wrapperクラスは以下のように定義します。
Wrapperクラス
// ジェネリックスクラス class Wrapper<T> { private T t; Wrapper(T t) { this.t = t; } public T get() { return t; } public void set(T t) { this.t = t; } // ジェネリックスメソッド(今回は使わない) ここでListインタフェースとArrayListクラスを使っているのですが許してください。。。 public <E> List<E> hoge(E e) { List<E> list = new ArrayList<>(); list.add(e); return list; } }ジェネリックスを使用しているWrapperクラスのようなクラスのことをジェネリックスクラスといいます。
class Wrapper<T> { ... }
のように書くことで、クラス内で変数Tという型パラメータを使用することができます。Wrapperクラスでは。
フィールド、get()メソッド、set()メソッド
に型パラメータTを使用しています。
※しかしここで注意なのですが staticなフィールド、staticなメソッド には使用することができません。
Wrapperクラスの中でジェネリックスメソッド(Wrapperクラスのhogeメソッド)というものがあります。
戻り値の型の定義を書いている手前に<E>
のように書くことで、メソッド内で変数Eという型パラメータを使用することができます。<E>
と書くことで、このメソッド内で型パラメータとしてEを使いますよという宣言をしていることになります。
もし、hoge()メソッド内でT型を使いたい場合には、戻り値の型の定義を書いている手前に<T>
と書く必要はありません。
なぜなら、Wrapperクラスを定義するときにclass Wrapper<T> { ... }
のように、すでにT型は定義されているためです。
変数Tのスコープは、Wrapperクラス内ということになります。// ジェネリックスクラス class Wrapper<T> { // 省略 // ジェネリックスメソッド public List<T> hoge(T t) { List<T> list = new ArrayList<>(); list.add(t); return list; } }ジェネリックスを使うことで、いろんな型を汎用的に扱うことができるようになります。
Wrapperクラスで使われるTのことを仮型パラーメータといい、このTには、基本データ型以外の型を入れることができます。
ちなみに、Tというのは変数です。
基本的には一文字で下記の慣例があるようです。
- E:Element
- K:Key
- V:Value
- T:Type
- N:Number
このジェネリックスを用いたWrapperクラスを使って、AnimalやDogなどを汎用的に扱うことができます。
Wrapper<Animal> animalWrapper = new Wrapper<>(new Animal()); Wrapper<Dog> dogWrapper = new Wrapper<>(new Dog()); Animal animal = animalWrapper.get(); Dog dog = dogWrapper.get();変性
ジェネリックスは、変性という性質を持っています。変性には、非変、共変、反変の3つがあります。
非変
先程のWrapperクラスを使った例を見てみます。
Animal animal = new Animal(); Dog dog = new Dog(); animal = dog; Wrapper<Animal> animalWrapper = new Wrapper<>(animal); Wrapper<Dog> dogWrapper = new Wrapper<>(dog); animalWrapper = dogWrapper; //コンパイルエラーこのように、
Animal
とDog
には継承関係があり、スーパータイプのAnimalインスタンスにサブタイプのdogインスタンスを代入可能です。しかし。Wrapper<Animal>
にWrapper<Dog>
を代入することができません。このような性質のことを非変といいます。共変
Animal
とDog
には継承関係があります。
このとき、Wrapper<Animal>
にWrapper<Dog>
を代入可能になるような性質のことを共変といいます。Animal animal = new Dog(); Wrapper<Animal> animalWrapper = new Wrapper<Dog>(new Dog());反変
反変は共変と逆の性質をもちます。
Animal
とDog
には継承関係があります。
このとき、Wrapper<Dog>
にWrapper<Animal>
を代入可能になるような性質のことを反変といいます。Animal animal = new Dog(); Wrapper<Dog> = new Wrapper<Animal>(new Animal());ワイルドカード型
型パラメータに
?
が書かれているジェネリックス型のことをワイルドカード型といいます。
ワイルドカード型には、非境界ワイルドカード型と境界ワイルドカード型の2つがあります。非境界ワイルドカード型
非境界ワイルドカード型は、
Wrapper<?> w
のように、?のみの型パラメータがことです。
境界がないということなので、Wrapper<?>
には、どんな型(基本データ型以外)でも代入することができます。Wrapper<?> animalList = new ArrayList<Animal>(); Wrapper<?> dogList = new ArrayList<Dog>(); animalList = dogList;非境界ワイルドカード型は、以下の制約があります。
- メソッドの戻り値がObject型となる。
- nullしかsetすることができない
基本的にはget()やset()は使うことができません。
例外として、Object型で値を受け取ったり、nullをsetすることはできます。Animalクラス、Dogクラス、MiniDogクラス
WrapperクラスWrapper<?> animalWrapper = new Wrapper<>(new Animal()); Wrapper<?> dogWrapper = new Wrapper<>(new Dog()); // getter Animal animal = animalWrapper.get(); //コンパイルエラー Dog dog = dogWrapper.get(); //コンパイルエラー Object objectAnimal = animalWrapper.get(); Object objectDog = dogWrapper.get(); // setter animalWrapper.set(new Animal()); //コンパイルエラー dogWrapper.set(new Dog()); //コンパイルエラー animalWrapper.set(null); dogWrapper.set(null);境界ワイルドカード型
境界ワイルドカード型には、上限付きワイルドカード型と下限付きワイルドカード型の2つがあります。
上限付き境界ワイルドカード型(変性:共変)
上限付きワイルドカード型は、
Wrapper<? extends Animal>
のような書き方をします。名前の通り、なんでも許容ではなく、制限として、上限があるワイルドカード(この例でいうとスーパータイプがAnimalクラスになるものだけ)のことになります。
Wrapper<? extends Animal>
このように書くことで共変のような性質を持つことができるようになります。Wrapper<Dog> dogWrapper = new Wrapper<>(new Dog()); Wrapper<? extends Animal> animalEWrapper = dogWrapper;上限付き境界ワイルドカード型は、基本的には
- 宣言した型パラメータ(
<? extends Animal>
)の戻り値が宣言時の型パラメータ型(Animal)になる- nullのみsetすることができる
実際にコードを見ながら整理していきます。
Animalクラス、Dogクラス、MiniDogクラス Wrapperクラス
Wrapper<? extends Animal> animalEWrapper = new Wrapper<>(new Dog()); Object object = animalEWrapper.get(); Animal animal = animalEWrapper.get(); Dog dog = animalEWrapper.get(); //コンパイルエラー MiniDog miniDog = animalEWrapper.get(); //コンパイルエラー animalEWrapper.set(null); animalEWrapper.set(new Object()); //コンパイルエラー animalEWrapper.set(new Animal()); //コンパイルエラー animalEWrapper.set(new Dog()); //コンパイルエラー animalEWrapper.set(new MiniDog()); //コンパイルエラーanimalEWrapperには、Animalを継承しているクラスを代入することができます。animalEWrapperの型パラメータの宣言を見ると、戻り値はAnimalになります。そのため、Animal型とObject型で受け取ることはできますが、AnimalのサブタイプのDogやMiniDogでは受け取ることはできません。
set()メソッドではnullのみsetすることができます。下限付き境界ワイルドカード型(変性:反変)
下限付きワイルドカード型は、
Wrapper<? super Dog>
のような書き方をします。名前の通り、制限として、下限があるワイルドカード(この例でいうとサブタイプとしてDogクラスを持つクラス)のことになります。Wrapper<Animal> animalWrapper = new Wrapper<>(new Animal()); Wrapper<? super Dog> dogSWrapper = animalWrapper;
Wrapper<? super Dog>
にWrapper<Animal>
に代入が可能になります。このように書くことで、反変のような性質を持つことができるようになります。下限付き境界ワイルドカード型は、基本的には
- 宣言した型パラメータ(
<? super Dog>
)の戻り値がObject型になる- Dogを継承したクラスとnullのみsetすることができます。
実際にコードを見ながら整理していきます。
Animalクラス、Dogクラス、MiniDogクラス Wrapperクラス
Wrapper<? super Dog> dogSWrapper = new Wrapper<>(new Animal()); Object object = dogSWrapper.get(); Animal animal = dogSWrapper.get(); //コンパイルエラー Dog dog = dogSWrapper.get(); //コンパイルエラー MiniDog miniDog = dogSWrapper.get(); //コンパイルエラー dogSWrapper.set(null); dogSWrapper.set(new Object()); //コンパイルエラー dogSWrapper.set(new Animal()); //コンパイルエラー dogSWrapper.set(new Dog()); dogSWrapper.set(new MiniDog());dogSWrapperには、Dogをサブタイプとして持っているクラスを代入することができます。
下限付き境界ワイルドカード型のdogSWrapperの戻り値はObject型になります。そのため、Object型でのみ受け取ることはできます。
set()メソッドでは、Dogを継承したクラスとnullのみsetすることができます。まとめ
はじめて、技術記事を書いたのですが、自分なりに理解していても文章にまとめるのは難しいなと改めて感じました。わかりづらくなっているところもあるかと思いますが、誰かの役に立てばと願っています。
参考文献
- 投稿日:2019-12-05T11:16:15+09:00
Spring SecurityのPreAuthorizeで独自メソッドを呼び出す
Spring Securityのメソッド認可でちょっと複雑なロジックで認可したい場合、任意のbeanのメソッドを呼び出すことが出来ます。
ちなみにカスタム認証を正しく追加したい場合は PermissionEvaluator を実装するのが恐らく正当なアプローチです。
PermissionEvaluatorの実装についてはこちらの記事が分かりやすく詳しかったです。
拡張フレームワーク開発などで汎用的なEvaluatorを追加したい場合はこちらがよいでしょう。
https://www.codeflow.site/ja/article/spring-security-create-new-custom-security-expression今回はもうちょっとお手軽な方法でも書けるよという手順です。
Controller側のPreAuthorize定義
Controller.java@PreAuthorize("@customPreAuthorizer.belongGroup(#groupId, authentication.principal)") @RequestMapping("/group/{groupId}/list") public String list(Model model, @PathVariable("groupId") Long groupId) { }Bean定義
CustomPreAuthorizer.java@Component public class CustomPreAuthorizer { public boolean belongGroup(Long groupId, UserDetails userDetails) { // ここで独自認可を実装。実行を許可する場合にtrueを返す。 return true; } }解説
PreAuthorizeのexpression内でBean名の先頭に@をつけることで、コンポーネント登録されたBeanを参照出来ます。
@customPreAuthorizer
認証ユーザのユーザ情報は
authentication.principal
で引数に渡すことが出来ます。PreAuthorizeで引数に渡すのが記述として冗長であれば、メソッド内でSecurityContextを呼んでも同じものが取得できるので、メソッド内で取り出すことも可能です。
CustomPreAuthorizer.java@Component public class CustomPreAuthorizer { public boolean belongGroup(Long groupId) { var userDetails = (UserDetails)SecurityContextHolder.getContext().getAuthentication().getPrincipal(); return true; } }あとはUserDetailsを拡張して必要な情報をログイン時に持たせておけば、拡張した独自認可を実装して提供できます。
- 投稿日:2019-12-05T11:13:51+09:00
未経験からIT企業に就職した勉強メモ
29歳IT業界未経験で
某無料プログラミングスクールで1.5か月学んで就職しました
年明け入場予定。(大手)勉強前のスペック
・文系私大卒
・macユーザ winいじれるけど苦手
・HTMLは分かる
・ワードプレスでホームページ公開したがことある(ロリポップ使って)
・プログラミングは一切触ったことがない。
・エクセルとワードMOS2003エキスパート所持
でもマクロとか全然忘れてる自分的勉強まとめ(Java)
■就職するまでの勉強
・スクールの勉強 基礎構文→SQL→バグ改修→個人開発
・ドットインストール有料登録
寝る前とかに15分くらいドットインストールを見ていた。
・個人開発では、在庫管理システムを作る
mySQLで登録、削除、追加、更新、画像の登録削除更新、キーワード検索、ソート機能など
→自習が苦手ですぐ人を頼る自分の打開策■SES会社に入ってからjavaに関してやった勉強一覧(現在も)
(資格を取ったわけではない)・Java基礎構文の総復習
基本的なifとかforとか。スクールで忘れている部分を復習
会社がWinを使っていて、Macユーザだったので、とにかくwinPCになれるところから始まるw・プロゲート有料登録 → java,javascript,CSS,コマンドプロンプト(少し)
・PAIZAラーニング開始 最初はEとかDランク
全然解けない、慣れない、 制限時間が終わる(涙)
タイムアップしたPAIZAをゆっくりと解く。(Dランク)・サーティファイ3級、2級 無料問題
☆PAIZA Cランク到達
・PAIZA有料登録
HashMapの使い方の動画が役に立つ!・オラクルsilver アプリ問題集インストール(有料:2500円くらい)
オラクルのアプリは電車のなかでもちょこちょこできるしボリュームがすごいので2500円の価値ある。
内容一緒で本買うと4000円以上するからお得感ある。
インクリメントとかスコープの範囲とか勉強になる・レスポンシブ対応のホームページ公開
一週間くらい掛けて制作。プロゲートのCSS講座(上級)がかなり役に立つ・Cランク問題を10問くらい解く
・現場経験10年くらいの先輩に週1回2時間くらい教わる。(これがめちゃためになる!先輩ありがとう!)
☆PAIZA Bランク到達
制限時間2時間あったので少し余裕をもって取り組めた
随時更新。
- 投稿日:2019-12-05T11:13:51+09:00
未経験からIT企業に就職した勉強メモー
29歳IT業界未経験で
某無料プログラミングスクール1.5か月 → 就職
年明け入場予定。(大手)
(ただのメモ)勉強前のスペック
・文系私大卒
・macユーザ winも持ってたしいじれるけど苦手
・HTMLは分かる
・ワードプレスでホームページ公開したがことある(ロリポップ使って)
・プログラミングは一切触ったことがない。
・エクセルとワードMOS2003エキスパート所持
でもマクロとか全然忘れてる自分的勉強まとめ(Java)
■就職するまでの勉強
・スクールの勉強 基礎構文→SQL→バグ改修→個人開発
・ドットインストール有料登録
寝る前とかに15分くらいドットインストールを見ていた。
・個人開発では、在庫管理システムを作る
mySQLで登録、削除、追加、更新、画像の登録削除更新、キーワード検索、ソート機能など
→自習が苦手ですぐ人を頼る自分の打開策■SES会社に入ってからjavaに関してやった勉強一覧(現在も)
(資格を取ったわけではない)・Java基礎構文の総復習
基本的なifとかforとか。スクールで忘れている部分を復習
会社がWinを使っていて、Macユーザだったので、とにかくwinPCになれるところから始まるw・プロゲート有料登録 → java,javascript,CSS,コマンドプロンプト(少し)
・PAIZAラーニング開始 最初はEとかDランク
全然解けない、慣れない、 制限時間が終わる(涙)
タイムアップしたPAIZAをゆっくりと解く。・サーティファイ3級、2級 無料問題
☆PAIZA Cランク到達
・PAIZA有料登録
HashMapの使い方の動画が役に立つ。・オラクルsilver アプリ問題集インストール(有料:2500円くらい)
オラクルのアプリは電車のなかとかでちょこちょこできるしボリュームがすごい
インクリメントとかスコープの範囲とか勉強になる・レスポンシブ対応のホームページ公開
一週間くらいで政策。プロゲートのCSS講座(上級)がかなり役に立つ・経験10年くらいの先輩に週1回2時間くらい教わる。(これがめちゃためになる、先輩ありがとう)
☆PAIZA Bランク到達
随時更新。
- 投稿日:2019-12-05T10:11:39+09:00
なんだかんだ使うUtil
ライブラリ自作
備忘録です
コメントとか適当なのであしからずUtils.javapackage util; import java.util.function.Function; import java.util.function.Predicate; import java.util.Arrays; public class Utils { /** --------------- Object --------------- */ /** nullでなけれは真 */ public static final Predicate<Object> nonNull = v -> v != null; /** nullなら空文字に変換 */ public static final Function<Object, String> nullToEmpty = v -> v == null ? "" : v.toString(); /** nullと数値でないものなら0に変換 */ public static final Function<Object, Integer> nullToZero = v -> { try { return v == null ? 0 : Integer.parseInt(v.toString()); } catch (NumberFormatException e) { return 0; } }; /** --------------- String --------------- */ /** nullでも空文字でもなければ真 */ public static final Predicate<String> nonEmpty = v -> v != null && !v.equals(""); /** nullでも空文字でも空白文字(半角全角)でもなければ真 */ public static final Predicate<String> nonBlank = v -> v != null && !v.equals("") && Arrays.stream(v.split("")).anyMatch(x -> !x.equals(" ") && !x.equals(" ")); }日付型とかはもうほんと勘弁してほしいだいっきらい
- 投稿日:2019-12-05T03:34:15+09:00
Javaで作る自動販売機のサンプル
目的
Javaを使ってプログラミングの経験を積むこと
対象者
Javaの基本を学んだ初学者
達成目標
フローチャートと機能詳細を元にプログラミングができる。
フローチャート
機能詳細
・商品リスト初期化
商品は以下の3つ固定とする。
コーラ100円
オレンジジュース120円
水80円・入金
1円単位で入金可能とする。
購入可能な最低金額が入金されるまで入金を促す。
(今回の場合、水の80円以上)・商品選択
入金額の範囲で商品を表示する。
商品名で選択する。・販売
選択した商品を提供する。・課金
入金額から購入金額を引く。
釣銭を返す。サンプルコード
package vm; import java.util.HashMap; import java.util.Map; import java.util.Scanner; public class Main { public static void main(String[] args) { // 商品の初期化 Map<String, Integer> items = new HashMap<String, Integer>(); items.put("コーラ", 100); items.put("オレンジジュース", 120); items.put("水", 80); // 購入最低金額の場合、追加入金 int deposit = 0; int minSaleAmount = -1; Scanner scanner = new Scanner(System.in); do { // 入金処理 System.out.println("お金を入れて下さい。"); deposit = deposit + scanner.nextInt(); // 金額チェック(最低購入金額) int loopCount = 0; for (String itemKey: items.keySet()) { if(loopCount == 0 || minSaleAmount > items.get(itemKey)) { minSaleAmount = items.get(itemKey); } loopCount++; } } while(deposit < minSaleAmount); // 商品選択 System.out.println("商品を選択してください。"); Map<String, Integer> availablePurchases = new HashMap<String, Integer>(); for (String itemKey: items.keySet()) { if(deposit >= items.get(itemKey)) { System.out.println(itemKey + ":" + items.get(itemKey) + "円"); availablePurchases.put(itemKey, items.get(itemKey)); } } // 販売 String itemName; while(true) { itemName = scanner.next(); if (availablePurchases.containsKey(itemName)){ break; } System.out.println("商品名の指定が誤っています。商品名を指定し直してください。"); } scanner.close(); System.out.println(itemName + "です!"); // 課金機能 deposit = deposit - availablePurchases.get(itemName).intValue(); System.out.println("おつりは、" + deposit + "円です。"); } }
- 投稿日:2019-12-05T00:56:53+09:00
[Java] ラムダ式入門
緒言
Javaでスレッドを実行するためには,Threadクラスを継承したサブクラスを用いる方法と,
Runnableインタフェースを実装したサブクラスを用いる方法があります.
今回,ラムダ式の簡易的な説明のために後者を例にします.Function.javapublic class Function implements Runnable{ @Override public void run() { // スレッドで処理する内容 System.out.println("hello!"); } }Main.javapublic class Main { public static void main(String[] args) { Thread thread = new Thread(new Function()); thread.start(); System.out.println("finish!"); } }出力はこんな感じになると思います.
finish! hello!ThreadクラスとRunnableインタフェースの関係は
GoFデザインパターンのStrategyパターン設計が使われています.
Strategyパターンは大まかにいえば「付け替えできるアルゴリズムを実現する」設計で,
もともと1つだったクラスを処理の全体の流れを担当するクラスと,
具体的なアルゴリズムを担当するクラスに分けて考えます.
Strategyパターンを使うことで,使う側(Threadクラス)と
使われる側(Runnableインタフェースを実現したクラス)を
直接関係せずに済むため,あとから付け替えできるアルゴリズム実現することができます.
(Strategyパターンを詳しく!という方はこちらを見ると良いかも)
しかしながら,インタフェースを実現したクラスには複雑なもの(UML図のFunctionHardクラス)と
簡単なコードで済むクラス(UML図のFunctionEasyクラス)のようなものがあります.簡単なコードで済むのに,逐一新しいクラスを
定義しなくてはならないのめんどくないですか?ということです.ラムダ式の効力
緒言で提示した問題点を解決するために,Java SE8から導入されたラムダ式を利用します.
以下のコードはFunctionEasyクラスを作る代わりにラムダ式に置換したものです.Lambda.javapublic class Lambda { public static void main(String[] args) { Runnable r = () -> System.out.println("hello!"); Thread thread = new Thread(r); thread.start(); System.out.println("finish!"); } }このように1行で済みます.スッキリ!
ラムダ式は「関数型インタフェースをインスタンス化するときに
めんどい記述しなくても済むような記法」と思えばよいです.実装が必要なメソッドを1つだけ持つインタフェースを「関数型インタフェース」や
「SAM(Single Abstarct Method)インタフェース」と呼んだりします.SAMということからわかる通り,抽象メソッドを1つだけ持つため,
ラムダ式がどのメソッドを実装するのか?引数と戻り値はどうなのか?
推論が可能になります.逐一メソッド名を決める必要がないです.
インタフェースを実現したクラスを用意せずに,ポリモーフィズムを実現できます.匿名クラスとラムダ式
Java8以前は匿名クラスで実現されていました.
匿名クラスは無名クラスとも呼ばれることがあります.Runnable r = new Runnable() { @Override public void run() { System.out.println("hello!"); } }長くて可読性が悪いですね.
Java8でラムダ式が登場し,これをもっと簡単に記述できるようになりました.
Runnable r = () -> { System.out.println("hello!"); }ラムダ式の宣言は,以下のように引数の変数宣言と処理ブロックで構成されます.
( 引数 ) -> { 処理; };「->」をアロー演算子と呼びます.
ラムダ式省略記法
ラムダ式では省略できるものがあります.
以下の原型を例にたどってみましょう.原型Sample sample = (String a) -> { System.out.println(a);};省略記法1
引数が1つのときだけの場合,カッコ「()」を省略できます.
また,引数の型は型推論できるため,省略することができます.省略記法1Sample sample = a -> { System.out.println(a);};省略記法2
また,メソッド本体が1行の場合は,中カッコ「{}」と文末のセミコロン「;」を省略できます.
return文の場合はreturnも省略できます.省略記法2Sample sample = a -> System.out.println(a);省略記法3
さらに,引数が推論できる場合,メソッド呼び出しは
クラス名::メソッド名
オブジェクト名::メソッド名
のように省略することができてしまいます.省略記法3Sample sample = System.out::println;ラムダ式の変数スコープ
注意1
メソッド内で宣言したローカル変数と同じ名前の変数をラムダ式の引数名として使えない.
以下はコンパイルエラーになります.public class Sample { public static void main(String[] args) { String a = "sample"; Function f = a -> System.out.println(a); // エラー // 略 } }すでにaという変数名がローカル変数として宣言されているので,ラムダ式の引数名として使えません.
注意2
ラムダ式外で宣言されたローカル変数にラムダ式内からアクセスするには、ローカル変数がfinalでなければいけない.
final修飾子が付いていない場合は実質的finalでなければいけない.ラムダ式からその式を囲むメソッドのローカル変数にアクセスすることはできます.
(ラムダ式に変換前のことを考えればそれはそうという感じです.)
以下のように,ラムダ式内から,式を宣言しているメソッドのローカル変数にアクセスできます.「実質的にfinal」というのは,finalで修飾されていなくても,変更されない変数という意味です.
以下のように,ラムダ式内でローカル変数の値を変更してしまうとコンパイルエラーになります.public class Sample { public static void main(String[] args) { String a = "sample"; Function f = () -> { a = "change"; // エラー System.out.println(a); }; // 略 } }java.util.functionパッケージとCollection API
頻繁に使われる関数型インタフェースはjava.util.functionパッケージにあります.
特に,有名どころは以下の5つの関数型インタフェースです.
関数型インタフェース メソッド 引数 戻り値 説明 Consumer<T> void accept(T) あり なし 値を返さない「消費者」 Supplier<T> T get() なし あり 値を返す「供給者」 Predicate<T> boolean test(T) あり あり 真偽値を返す「断定」 Function<T, R> R apply(T) あり あり 引数を受け取り指定の型の結果を返す「処理」 UnaryOperator<T> T apply(T t) 2つあり あり 演算結果を返す「操作」 Collection APIにはListやMapを操作するデフォルトメソッドがたくさんあります.
例えば,java.util.ListではremoveIf(Predicate filter)というのメソッドがあり,filterに一致する要素を削除します.
ラムダ式を使えば1行で済んでしまいます.RemoveIf.javaimport java.util.ArrayList; import java.util.Arrays; public class RemoveIf { public static void main(String[] args) { ArrayList<String> list = new ArrayList<String>(Arrays.asList("aaaa", "bbb", "cc")); list.removeIf(v -> v.length() > 3); System.out.println(list); // 結果: [bbb, cc] } }他にもラムダ式を指定できるメソッドがたくさん用意されています.
各自で調べてラムダ式で実装して慣れていきましょう.結言
サーバーサイドがJava8で動いているWEBアプリケーションがたくさん存在しています.
そのため,現在Javaをやる上でラムダ式 & Stream APIは避けては通れない知識だと思っています.
にもかかわらず,Java新人研修では教わらないところもある模様なので,
新人向けにできるだけわかりやすくまとめてみました.わかりにくい部分があったら申し訳ないです.次回,再来週あたりにStream API入門についてまとめようと思います.
- 投稿日:2019-12-05T00:48:35+09:00
授業で習ったJavaの復習をつらつらと part2
はじめに
part1を読んでから、もしくはJavaでif文が取り敢えずわかったら読んでね。
今回はpartを前提に、for文や文字入力等、オブジェクト指向ではない部分を取り扱う予定。
ループ文
Javaにはforとwhileがある。例文を使い分けができればなお良い。
まずはfor文から。
java/for.javapublic class for { public static void main(String[] args) { System.out.println("terminalに1~5を出力"); //for文で使えるint型のiを定義、制御に使っている for(int i = 1; i <= 5; i++) //i++ でループする時にiに1を足して再実行できる { System.out.println(i); } } }for文での注意点は以下。
- ループする回数を自分で理解して使うこと
- 区切り文字が「,」ではなく「;」
区切り文字は構文なので仕方ないと思う。
「自分で理解して使う」という点だが、例えばfor文の中のint iの初期値が0だったらどうなるだろう。今回の場合だと、「1から5まで」の整数を出力したいので、初期値を0にしてしまうとターミナルに0も出力してしまう。また、「i <= 5」の部分の条件を「i < 5」にしてしまうと5が出力されない。
このようにfor文は便利だが、自分が誤った範囲を指定してしまうと予期せぬエラーを起こしてしまうので注意。
注意点には書いていないが、変数にはスコープという概念がある。これは「{}」の中(ブロックと言う)の中で定義した変数はそのブロックの中でしか使えない、というものだ。例えば今回のプログラムでfor文の中で定義した「int i」は、for文の下に「System.out.println(i)」などとfor文の外で使おうとするとエラーを起こす。続いてwhile文。同じく繰り返しなのだが、判定が若干違うので注意。
java/while.javapublic class while { public static void main(String[] args) { System.out.println("Terminalに1~5を出力"); int i = 1; //while文で使う整数を定義 while(i <= 5) //この条件の間はずっと実行する { System.out.println(i); i++; //ないと無限ループするよ } } }while文での注意点は以下である。
- 制御する変数や値の代入を逐一書く
- for文とは判定が逆
変数を条件定義の時点で入れられるfor文と違って、while文は先に書いておく。(できないと思うけどそのへんは知らん。)
for文は「〜の条件になるまで実行する」に対して、while文は「〜の間ずっと実行する」という制約条件なので、for文とは使用用途が少し違う。具体例としては、自動販売機でジュースを買わせるプログラムを書くときにwhile文だったら所持金がなくなるまで買わせるプログラムを簡単にかける。(鬼畜)
ここのまとめ
- for文の書き方、動き方
- 変数のスコープ
- while文とfor文の違い
続きは明日。多分ね
- 投稿日:2019-12-05T00:38:14+09:00
SpringBootではじめるメッセージ連携
目次
- メッセージ連携とは
- 記事の趣旨
- 前提環境
- メッセージング基盤を構築する
- Sourceを作る
- Sinkを作る
- Processorを作る
- 動作確認
- まとめ
メッセージ連携とは
メッセージ連携とは、アプリケーションプログラム同士で
データや処理要求など「メッセージ」を交換することで、
複数アプリケーションを非同期に連携させるための仕組み。マイクロサービス設計においては
メッセージを一時的に保持し、この受け渡しを仲介するミドルウェアを導入することで
各サービスを疎に保ち、スケーラビリティ向上を図る方針が採られている。みんな大好きSpringFrameworkおよびSpringBootでは、
メッセージ連携を担うサービスの軽量開発を実現する
Spring Cloud Streamプロジェクトが注目されている。記事の趣旨
Spring Cloud Streamを使ってメッセージ連携に触れてみましょう。
完成したコードはこちら。
記事では解説しないがテストクラス付き。構成図は以下。読み進める中で、判らなくなったら見返してください。
動作イメージ知りたければ、先にページ下部の章「動作確認」を読んでください。
前提環境
- Spring Tool Suiteが導入されていること。
- JRE 8以上が導入されていること。
- Dockerが導入されていること。
筆者の環境は以下。
Spring Tool Suite 3 Version: 3.9.9.RELEASE Build Id: 201906181741 Platform: Eclipse 2019-06 (4.12.0)$ docker version Client: Docker Engine - Community Version: 19.03.2 API version: 1.40 Go version: go1.12.8 (中略) Server: Docker Engine - Community Engine: Version: 19.03.2 API version: 1.40 (minimum version 1.12) Go version: go1.12.8 (中略) containerd: Version: v1.2.6 (中略) runc: Version: 1.0.0-rc8 (中略) docker-init: Version: 0.18.0 (中略) $$ java --version java 9 Java(TM) SE Runtime Environment (build 9+181) Java HotSpot(TM) 64-Bit Server VM (build 9+181, mixed mode) $メッセージング基盤を構築する
導入が容易であることから、RabbitMQを採用する。
DockerHubからイメージをpullし、runでコンテナを起動する。
RabbitMQコンテナのマニュアルはこちら。$ docker pull rabbitmq //起動コマンド $ docker run -p 15672:15672 -p 5672:5672 rabbitmq:3-management起動が完了すると、管理コンソールにアクセスできる。URLは以下。
http://localhost:15672/IDおよびPWは、初期設定ではguest:guestとなる。
Sourceを作る
Spring Cloud Streamではサービス外、例えばREST API等からデータを受け取って
メッセージング基盤へ連携(OUTPUT)するサービスをSourceと呼ぶ。コード全量はこちら。
まずメッセージング基盤の対象キュー名を、application.propertiesに定義する。
spring.cloud.stream.bindings.output.destination=hello-processor次に@EnableBindingアノテーションで
POJOなクラスと、メッセージング基盤の対象キューを紐づける。加えてSourceオブジェクトをDIし
Source.output.send()メソッドを呼び出してメッセージを送信する。(前略) @EnableBinding(Source.class) public class HelloSourceApplication { private final Source source; public HelloSourceApplication(Source source) { this.source=source; } (中略) @PostMapping public void tweet(@RequestBody Tweet tweet) { source.output().send(MessageBuilder.withPayload(tweet).build()); } public static class Tweet { public String tweet; } }Sinkを作る
Sourceとは逆に、メッセージング基盤から連携(INPUT)を受けて
バックエンドにデータを連携するサービスを
Spring Cloud StreamではSinkと呼ぶ。コード全量はこちら。
まずメッセージング基盤の対象キュー名を、application.propertiesに定義する。
spring.cloud.stream.bindings.input.destination=hello-sink次に@EnableBindingアノテーションで
POJOなクラスと、メッセージング基盤の対象キューを紐づける。
また@StreamListenerアノテーションにより、
キューから受け取ったメッセージを直接、DTOと紐づける。今回は、受け取ったDTOを標準出力に出して終了する。
(前略) @EnableBinding(Sink.class) public class HelloSinkApplication { (中略) @StreamListener(Sink.INPUT) public void print(Tweet tweet) { System.out.println("Received " + tweet.tweet); } public static class Tweet { public String tweet; } }Processorを作る
メッセージング基盤から連携(INPUT)を受けて
メッセージング基盤へ連携(OUTPUT)するサービスをProcessorと呼ぶ。コード全量はこちら
まずメッセージング基盤の対象キュー名をapplication.propertiesに定義する。
今回の例では、OUTPUTにSinkサービスが購読する対象キュー名を定義している。
またINPUTとして、Sourceがメッセージを送る先の対象キュー名を定義している。spring.cloud.stream.bindings.output.destination=hello-sink spring.cloud.stream.bindings.input.destination=hello-processor次に@EnableBindingアノテーションで
POJOなクラスと、メッセージング基盤の対象キューを紐づける。更に、@StreamListenerアノテーションを用いて、受け取ったメッセージをDTOに紐づけ。
加えて@SendToアノテーションで
メッセージを送る対象キューおよびメッセージ本文を、メソッド戻り値であるDTOに紐づけ。結果として、メッセージを受信したら
その値を加工(後ろに" processing!"を付与)して、メッセージを送信する挙動となる。(前略) @EnableBinding(Processor.class) public class HelloProcessorApplication { (中略) @StreamListener(Processor.INPUT) @SendTo(Processor.OUTPUT) public Tweet transform(Tweet tweet) { tweet.tweet += " processing!"; return tweet; } public static class Tweet { public String tweet; } }動作確認
それぞれのプロジェクトについてmaven clean packageを実行。
target配下に生成された各Jarを以下のとおり起動する。$ java -jar target/hello-source-0.0.1-SNAPSHOT.jar --server.port=8080 $ java -jar target/hello-sink-0.0.1-SNAPSHOT.jar --server.port=8082 $ java -jar target/hello-processor-0.0.1-SNAPSHOT.jar --server.port=8090curlで、Sourceに対してPOSTリクエストを送る。
$ curl -v localhost:8080 -d '{"tweet":"Hello"}' -H 'Content-Type: application/json'SinkのJarを起動したコンソールに、メッセージが出力される。
Received Hello processing!まとめ
SpringBootならびにRabbitMQ、Spring Cloud Streamを用いて
マイクロサービスなメッセージ連携の設計/実装方法を学んだ。是非それぞれのサービスを沢山、起動して遊んで頂きたい。
参考文献
Stream Processing with RabbitMQ