- 投稿日:2019-03-02T23:50:22+09:00
いまさらJavaのStream APIにおける「stream」「collect」の意味を考察する
はじめに
既に大分前になりますが、JavaにStream APIという関数型プログラミング的手法が導入されました。
関数型プログラミングは既に一般的に広まっており、Javaはかなり後発の部類に入りますが、やはり導入された理由は、うまく使いこなせれば可読性の高いコードを効率的に書けるようになるメリットがあるからと考えています。
(参考:「平凡なプログラマにとっての関数型プログラミング」)しかし先日、とある講演にて、「Javaのコレクション操作(Stream API)は、Haskellなど純関数型言語と比べると無理やり感があって使いたくない」という話を聞きました。
具体的に言えば、関数型言語ならlist.map(...)
で済むものを、Javaの場合は一々
list.stream().map(/* */).collect(Collectors.toList())
みたいに
stream()
を挟んでやる必要があり、冗長な書き方になってしまうところかなと思いました。それでも無いよりは良いので私自身Stream APIは比較的好んで使いますが、関数型言語を好む人からすれば受け付けない面はあるのかなと思いました。
では、なぜJavaが
list.map(...)
のような簡単な書き方を採用せず、一々stream()
を呼び出してStreamという別の型に変換して、collect
にて再度変換するようにしたか、についてです。Javaは、言語設計を入念に行う文化があり、その結果に賛否はあれど何かしらの理由があるはずです。その理由は大きく次の2つであると考えています。
- 遅延評価
- オブジェクト指向による制約
以下、詳しく考えを述べていきます。
遅延評価とは
一般にコレクション操作を行う際、その過程でいちいちコレクションの作り直しをしていては無駄ですし、パフォーマンスへの懸念が生まれます。
これを防ぐには、コードの見た目上はコレクションを徐々に変化させているように見えても、実際には最後に最後にまとめて生成するようにします。このように、値が必要になるまで計算しないという計算方法を遅延評価と呼びます。
例えば、List<String> list = Arrays.asList("foo", "bar", "hoge", "foo", "fuga"); list.stream() .filter(s -> s.startsWith("f")) .map(s -> s.toUpperCase()) .collect(Collectors.toSet()); // ["FOO", "FUGA"]という感じに、
- 文字列のリストのうち、"f"から始まるものを抽出する
- 文字列を大文字に変換する
- 重複を取り除く(Setに変換)
というコレクション操作を行う際、
filter
が呼び出された時点で新たな要素数3のコレクションが生まれることはありません。
実際にコレクションが生成されるのは、最後にcollect(Collectors.toSet())
が呼び出されたタイミングになります。※ちなみに、厳密にはこれを遅延評価とは呼ばないみたいですが、他に適切な呼び方がないため遅延評価と呼ぶことにします。(参考:「遅延評価ってなんなのさ」)
遅延評価の対義語は正格評価です。値が不要であってもその時点で計算してしまう方法です。通常はこちらの方が一般的です。
遅延評価のデメリット
メリットがある一方で、注意して使わないと思わぬ落とし穴があります。
以下は(あまり好ましくない書き方ですが)実際に遅延評価により見た目と実際の実行結果の認識齟齬が生まれそうな例です。// 入出力データ定義 List<String> input = Arrays.asList("foo", "bar", "hoge", "foo", "fuga"); List<String> copy1 = new ArrayList<>(); List<String> copy2 = new ArrayList<>(); // コレクション操作開始. filterを実行 Stream<String> stream = input.stream() .filter(s -> { copy1.add(s); return s.startsWith("f"); }); System.out.println(copy1.size()); // この時点では、filter操作は実際に評価されないため、copy1は空のままで0が出力される System.out.println(copy2.size()); // 当然copy2も空のままなので0が出力される // 続いてコレクション操作のmapを実行 stream = stream .map(s -> { copy2.add(s); return s.toUpperCase(); }); System.out.println(copy1.size()); // この時点でもまだfilter操作は評価されないため、0が出力される System.out.println(copy2.size()); // 同様にmap操作も評価されないため、0が出力される stream.collect(Collectors.toList()); System.out.println(copy1.size()); // stream.collectによりようやくfilterが評価されるため、5が出力される System.out.println(copy2.size()); // 同様にmap操作も評価されるため、3が出力される上のコードは、一見すると
filter
,map
を呼び出した際にcopy1
,copy2
のサイズが増えるように見えますが、
実際の動きとしてはcopy1
,copy2
のサイズが増えるのはstream.collect
を呼び出したタイミングとなります。
このように、見た目と実際の評価タイミングにずれがあると、何か問題が起きた際にデバッグしづらく原因の特定が難しくなってしまう危険性があります。どのようにバランスを取るか
遅延評価は使い方を誤ると複雑なバグを埋め込む危険性があります。かといって、遅延評価を全く使わない場合、無駄にコレクションが生成されてパフォーマンス低下の危険性があります。
Javaの場合、バックエンドで大量データを扱う可能性が普通に考えられる以上、後者のリスクは避けたいため、遅延評価を導入せざるを得ません。しかも、意図せず正格評価が使われないよう、自然(?)と遅延評価になっているようなものが好ましいでしょう。
しかし、だからといってJava標準機能の広範囲に渡り遅延評価を適用できるようにするのはリスキーです。
そのため、遅延評価は特定の型に限定させ、他の型では遅延評価が使われないような作りが妥当な落としどころと考えたのでしょう。「Stream」の登場
ストリーム とは:
ストリーム(stream)とはデータを「流れるもの」として捉え、流れ込んでくるデータを入力、流れ出ていくデータを出力として扱う抽象データ型である。
先に述べた、唯一、遅延評価を行える型を「Stream」と名付けました。
そして、コレクション操作APIの名前もそのまま「Stream API」、要はコレクション操作がやりたかったら、とりあえず名前の通りstream()
を使えということでしょう。そうすることにより、遅延評価を強制させ正格評価によるパフォーマンス低下のリスクを回避しようとしたと考えています。
オブジェクト指向による制約
stream
を介する別の理由として、オブジェクト指向による制約があります。
(厳密にはオブジェクト指向というよりは、型の扱いによる制約に該当しますが、関数型とオブジェクト指向とで対比されることが多いため、ここでは「オブジェクト指向」という言葉を使います。)
仮に、List型に対してdefaultのmapメソッドを定義したとします。interface List<E> { default <R> List<R> map(Function<? super E, ? extends R> mapper) { List<R> result = new ArrayList<>(); for (E elem : this) { result.add(mapper.apply(elem)); } return result; } }このようにしておけば、とりあえずList型の変換を
list.map(...)
のように行うことができます。
filter等のメソッドや、他のコレクション型も同じような実装すれば、streamを介さずとも簡潔なという書き方でコレクションの変換を行うことができます。しかし、この方法は重大な欠点を抱えています。
それは標準ライブラリ以外のコレクション型です。例えば、ある開発者がListインターフェースを実装したMyListを作成しており、固有のメソッドdoSomethingを追加していたとします。
ここで、MyList型を上記方法でmap変換すると、変換後は別のList型になってしまい、doSomethingが呼び出せなくなってしまいます。MyList<> mylist = new MyList<>(); // 中略 mylist.doSomething(); //OK myList.map(x -> new AnotherType(x)).doSomething(); //コンパイルエラーこのあたり、オブジェクト指向言語に関数型プログラミングを取り入れる際の難点となるところでしょう。
とはいえ、実際にこういったケースはあまり見かけないため気にしなくても良い気もしますが、やはりそこはJavaという言語の性格上、許容できないところなのでしょう。なお、Scalaに関して言えば、この難点を暗黙的な型解決とやらで見事突破しています。
下記補足Aで紹介する書籍に記述されていますので、興味がある方は是非見てみてください。「Collector」の登場
上記理由により、コレクションの変換操作を始めると、もともとあったコレクションと別のものを再生成せざるを得なくなり、またコレクションの指定はライブラリ側ではなく呼び出し側の責務となります。
それを担うのが「Collector」であり、Stream.collectにより呼び出し側でどのコレクション型に変換するか指定します。
下のソースコードはCollectors.toList
の実装内容です。public static <T> Collector<T, ?, List<T>> toList() { return new CollectorImpl<>((Supplier<List<T>>) ArrayList::new, List::add, (left, right) -> { left.addAll(right); return left; }, CH_ID); }自前で作成したコレクション型MyListも、これと同様の方法でCollectorインスタンスを生成するメソッドを用意しておけば、MyListからMyListへの変換を行うことが可能でしょう。
これにより、Stream API導入以前に作成されたコレクション型も、Stream API上で特別大きな変更を加えず使うことができます。ちなみに、Stream型にtoListメソッドを作成しない理由
コレクションの変換はCollector型に集約することで見通しが良くなるとはいえ、List型などは日常で頻繁に出てきます。
せめて、stream.collect(Collectors.toList())
ではなくstream.toList()
ぐらいに簡潔に書けても良いんじゃないかという気もしてきます。
これを行わない理由はおそらく、型の依存関係にあると考えています。
要は、Collection型からStream型への参照は必須ですが、Stream型からCollection型を参照すると相互参照することになり型設計として好ましくない、という理由かなと考えています。おまじないを唱えて安全に使おう
上記のように、様々なバランス、整合性を考えた結果が
list.stream().map(/* */).collect(Collectors.toList())
という冗長とも取れるコレクション操作の形に落ち着いたのだと考察しています。ある意味で、とてもJavaらしい結論ではないかなと考えています。
Javaを使う際に危ないからStream API使っちゃいけないよという謎プロジェクトが世の中には存在するみたいですが、このように安全性を考慮した作りになっているので、普通に使う分には取り立てて不安がる必要はありません。定型通りに
stream
,collect
を唱えれば、よっぽどのことがない限り問題は起きないでしょう。おわりに
純関数型言語に拘りがある人以外は、冗長な記述もまぁ許容範囲に収まると思います。
関数型プログラミングをうまく使いこなせれば、可読性の高いコードを効率的に書けるようになります。まだ使っていない人はぜひ試してみてください。
(参考:Java Stream APIをいまさら入門)補足A. Java以外の言語
あまり詳しくありませんが、他の言語がどのような機能を提供しているか参考程度に記載します。
C#
C#のLINQは、Javaと同様に遅延評価です。
Javaと異なるのは、stream()を呼び出して開始する必要がなく、コレクション化する際も例えばToListを呼び出すだけで済むことが多いので、Javaより大分簡潔に書けて便利です。
(参考:「[雑記] LINQ と遅延評価」)
(参考:「C#erなら当然知ってるよね!?LINQ遅延評価のメリット・デメリット」)Scala
Scalaは関数型言語というのもあり、遅延評価・正格評価の使い分けが可能です。
例えば、list.map(...)
で正格評価による別コレクションへの変換ができます。
また、list.view.map(...).filter(...).force
のようにview, forceという形で遅延評価による別コレクションへの変換も可能です。
(参考:「Scala でジェネレータを作ったり、遅延評価してみる」)なお、はるか昔は正格評価と遅延評価の区別が付き辛く混乱を招いていた時期もあったらしいですが、ある時境界を明確にして
- View
- Stream
という2つの型のみ遅延評価の対象として整理したらしいです。
なお、Scalaに関しては「Scalaスケーラブルプログラミング」という本に恐ろしく詳しいことが色々と書かれているので、興味がある方はぜひ読んでみてください。JavaScript
JavaScript標準には遅延評価はありません。Array.prototypeに、map, filterなど標準的なコレクション操作APIがありますが、どれも正格評価です。
これはおそらく、ほぼクライアントサイドで使用されるJavaScriptで大量データ処理を扱うことはないという前提で、遅延評価は標準搭載不要と考えたのでしょう。Haskell
Haskellは純関数型言語というのもあり、上に挙げたものとまた毛色が違うようです。
通常の言語は基本が正格評価なのに対して、Haskellは基本が遅延評価です。
そのため、本記事で気にしているような正格評価と遅延評価のバランスと、そもそも無縁のようです。
(参考:「正格評価と遅延評価(詳細編)」)上記以外(PHP, Ruby, Python, etc...)
そのうち調べます。
補足B. 他のライブラリを使うという選択肢
Java標準以外に、Eclipse collectionsというコレクション操作ライブラリがあります。
これを使うと、Stream APIでは冗長になるものがすっきりと記述できます。
(参考:「Eclipse Collectionsを触ってみた」)
(参考:「Eclipse Collectionsチートシート」)また、ImmutableListという、不変なListインターフェースが用意されており、より関数型手法の色合いが濃いライブラリです。
Stream APIよりもより関数型的なコレクション操作を扱いたいなら導入も1つの選択肢かと思います。ただし、全面的にStream APIをEclipse Collectionsに置き換えようとしたら相当な工夫が必要でしょう。
導入する際は、実際に導入を敢行した現場の話「Eclipse Collectionsを現場に浸透させるためのフレームワーク対応」が参考になるかと思います。
- 投稿日:2019-03-02T22:57:56+09:00
java Optionalの使い方について
Optionalの宣言方法
Optionalを使用するには型をラップして使う
Optional<String> opt1;自作クラスでも同様
testClass.javapublic class testClass { private String no; private BigDecimal val; }OPtional<testClass> opt2;使い方
optional型に値をいれるにはoptional.of か optional.ofNullableを使用する。
Optional<String> opt1 = Optional.of("test");ただ、optional.ofは引数がnullの場合Exceptionが発生するので注意。
Optional<String> opt1 = Optional.of(null);よって、optional.ofNullableを使用する。
Optional<String> opt1 = Optional.ofNullable("test"); Optional<String> opt2 = Optional.ofNullable(null); testClass test = new testClass(); Optional<String> opt3 = Optional.ofNullable(test);値の取り出し方
値を取り出すには、以下を使用する。
get : nullの場合、Exception発生
orElse : null出ない場合変数値、nullの場合orElseの引数を返却
orElseGet : null出ない場合変数値、nullの場合,suppelierの結果を返却String val1 = opt1.get(); String val2 = opt1.orElse("")自作クラスから値を取り出すときは、以下を使用してフィールドを取得できる。
mapString val1 = opt3.map(testClass::getNo).orElse("1"); String val2 = opt3.map(v -> v.getNo()).orElse("2");
- 投稿日:2019-03-02T21:33:53+09:00
Spock Test FrameworkでSystem.outをMockにする
はじめに
今年の新人向け研修向けにJavaコードを全部手直し+テストを書いて変更してもテストで保証するようにという対応をしていました。
その際にパラメータも使うしSpockでいいやとして作った時に際にはまったのでメモです。
(Junit5で楽になりましたが、それでも面倒なので自分はspock使うことが多いです)環境
Java8
Groovy 2.5
spock-core 1.2-groovy-2.5
Gradle4.10今回の現象
spockでJavaのSystem.outをMock化して表示したものを確認するというテストを作成していました。
その際にさらさらーっと書いて動かしたら起動時にエラーとなってしまうという現象に見舞われました。問題となるコード
現象を再現するためにHello Worldで確認します。
本体のコードとテストコードは以下の通りです。public class HelloWorld { public static void main(String[] args) { System.out.println("Hello World"); } }class HelloWorldTest extends Specification { def mainTest() { setup: PrintStream printStream = Mock() System.out = printStream when: HelloWorld.main(null) then: 1 * printStream.println("Hello World") } }解決策
cglib-nodepとobjenesisをDependencyとして追加してあげればOKです(エラーにもそう書いてます)。
なので、追加して動かせば終わりです。
最終的に動作した.build.gradleは以下の通りです。plugins { id 'java' id 'groovy' } group 'com.tasogarei' version '1.0-SNAPSHOT' sourceCompatibility = 1.8 repositories { mavenCentral() } dependencies { testCompile "org.codehaus.groovy:groovy:2.5.6" testCompile "org.spockframework:spock-core:1.2-groovy-2.5" testCompile "cglib:cglib-nodep:3.2.10" testCompile "org.objenesis:objenesis:3.0.1" }
- 投稿日:2019-03-02T21:33:53+09:00
Spock Test FrameworkdでSystem.outをMockにする
はじめに
今年の新人向け研修向けにJavaコードを全部手直し+テストを書いて変更してもテストで保証するようにという対応をしていました。
その際にパラメータも使うしSpockでいいやとして作った時に際にはまったのでメモです。
(Junit5で楽になりましたが、それでも面倒なので自分はspock使うことが多いです)環境
Java8
Groovy 2.5
spock-core 1.2-groovy-2.5
Gradle4.10今回の現象
spockでJavaのSystem.outをMock化して表示したものを確認するというテストを作成していました。
その際にさらさらーっと書いて動かしたら起動時にエラーとなってしまうという現象に見舞われました。問題となるコード
現象を再現するためにHello Worldで確認します。
本体のコードとテストコードは以下の通りです。public class HelloWorld { public static void main(String[] args) { System.out.println("Hello World"); } }class HelloWorldTest extends Specification { def mainTest() { setup: PrintStream printStream = Mock() System.out = printStream when: HelloWorld.main(null) then: 1 * printStream.println("Hello World") } }解決策
cglib-nodepとobjenesisをDependencyとして追加してあげればOKです(エラーにもそう書いてます)。
なので、追加して動かせば終わりです。
最終的に動作した.build.gradleは以下の通りです。plugins { id 'java' id 'groovy' } group 'com.tasogarei' version '1.0-SNAPSHOT' sourceCompatibility = 1.8 repositories { mavenCentral() } dependencies { testCompile "org.codehaus.groovy:groovy:2.5.6" testCompile "org.spockframework:spock-core:1.2-groovy-2.5" testCompile "cglib:cglib-nodep:3.2.10" testCompile "org.objenesis:objenesis:3.0.1" }
- 投稿日:2019-03-02T21:24:07+09:00
Java 初心者備忘録2 JSPサーブレット
Udemy サーチマン佐藤氏のJava講座 サーブレット編
JSPサーブレットについてメモ
・動的プロジェクト→サーバー登録
webconten->index.jsp
webinf->web.xml
src(ソース)->infoindex.jspより右クリック、サーバーで実行
文字化け対処
https://siragumohuinblog.wordpress.com/2015/07/24/eclipse-tomcat-servlet-mysql%E3%81%AB%E3%81%8A%E3%81%91%E3%82%8B%E6%97%A5%E6%9C%AC%E8%AA%9E%E3%81%AE%E6%96%87%E5%AD%97%E5%8C%96%E3%81%91%E3%81%AE%E8%A7%A3%E6%B1%BA/
http://pandazx.hatenablog.com/entry/2013/12/26/223314
http://craftone.hatenablog.com/entry/20080702/1215012494結果:サーチマン佐藤氏の回答にあったutf-8を、デフォルトのMS932 にしてみましょう。
であっさり解決。。
eclipseの表記が一瞬で直りました。サーバーは都度再起動する。MySQL 苦戦したところ
https://qiita.com/_natsu_no_yuki_/items/ae4c94187093e4ab3cdc
- 投稿日:2019-03-02T21:24:07+09:00
Java 初心者備忘録2
Udemy サーチマン佐藤氏のJava講座 サーブレット編
JSPサーブレットについてメモ
・動的プロジェクト→サーバー登録
webconten->index.jsp
webinf->web.xml
src(ソース)->infoindex.jspより右クリック、サーバーで実行
文字化け対処
https://siragumohuinblog.wordpress.com/2015/07/24/eclipse-tomcat-servlet-mysql%E3%81%AB%E3%81%8A%E3%81%91%E3%82%8B%E6%97%A5%E6%9C%AC%E8%AA%9E%E3%81%AE%E6%96%87%E5%AD%97%E5%8C%96%E3%81%91%E3%81%AE%E8%A7%A3%E6%B1%BA/
http://pandazx.hatenablog.com/entry/2013/12/26/223314
http://craftone.hatenablog.com/entry/20080702/1215012494結果:サーチマン佐藤氏の回答にあったutf-8を、デフォルトのMS932 にしてみましょう。
であっさり解決。。
eclipseの表記が一瞬で直りました。サーバーは都度再起動する。
- 投稿日:2019-03-02T21:05:04+09:00
Canvasから動画を生成するWebサービスを試作してみた。
はじめに
3年ぶりの投稿です。
最近、Codepenなどを使ってCanvasで遊んでいるのですが、Canvasの画像・動きを動画として残せないかと考えるようになりました。
調べてみると、CCapture.jsなど、Canvasから動画を生成するライブラリがあるようなので、Herokuを使って、簡単なCanvas動画生成サービスを作ってみました。作ったもの
Canvasから動画を生成するWebサービスを試作してみました。https://t.co/Nd265nEUGw pic.twitter.com/sweoAaaEZt
— t_mat (@t_mat) March 2, 2019使い方
htmlヘッダーに以下の行を追加します。なお、idはcanvasのid、fpsはフレームレート、timeは録画時間(ms)を指定します。
'C'キーを推すとCanvasのキャプチャが開始され、録画終了後、動画(webm)がダウンロードされます。
<script type="text/javascript" src="https://capcanvas.herokuapp.com/capcanvas/id=app&fps=60&time=10000"></script>
実装
■JavaScript
Jsは、CCaputureのサンプルコードを参考に、以下のように実装しました。なお、$URL
、$ID
、$FPS
、$TIME
はサーバー側で文字列に置換します。CapCanvas.jswindow.onload = function(){ var script = document.createElement("script"); script.type = "text/javascript"; script.src = "$URL"; document.body.appendChild(script); }; window.addEventListener('keydown', event => { if(event.key=='c'||event.key=='C'){ let canvas=document.getElementById("$ID"); startCapture(canvas,$FPS,$TIME); } }); let startCapture=function(canvas,fps,time){ let cap=new CCapture({format:'webm',framerate: fps,verbose: true}); let render=function(){ requestAnimationFrame(render); cap.capture( canvas ); }; requestAnimationFrame(render); cap.start(); setTimeout(function(){ cap.stop(); cap.save(); },time); };■サーバー
サーバー側はリクエストパラメータを解析し、CapCanvas.jsを書き換えて送信します。
個人的にお気に入りのSpark Frameworkで作成しています。Main.javapackage net.termat.webapp.capcanvas; import static spark.Spark.get; import java.io.BufferedReader; import java.io.InputStreamReader; import java.net.URL; import java.util.HashMap; import java.util.Map; import java.util.Optional; import spark.ModelAndView; import spark.Spark; import spark.template.mustache.MustacheTemplateEngine; public class Main { private static String code; public static void main(String[] args) { Spark.staticFileLocation("/public"); code=getJs(); Optional<String> optionalPort = Optional.ofNullable(System.getenv("PORT")); optionalPort.ifPresent(p -> { int port = Integer.parseInt(p); Spark.port(port); }); get("/", (request, response) -> { Map<String, Object> model = new HashMap<>(); return new ModelAndView(model, "index.mustache"); }, new MustacheTemplateEngine()); get("/capcanvas/:param", (request, response) -> { try{ String url="https://capcanvas.herokuapp.com/js/CCapture.all.min.js"; Map<String,String> map=paramMap(request.params("param")); String id=map.get("id"); String fps=map.get("fps"); String time=map.get("time"); response.status(200); String ret=new String(code); ret=ret.replace("$URL", url); ret=ret.replace("$ID", id); ret=ret.replace("$FPS", fps); ret=ret.replace("$TIME", time); return ret; }catch(Exception e){ e.printStackTrace(); response.status(400); response.type("application/json"); return ""; } }); } private static String getJs(){ try{ URL url=Main.class.getResource("CapCanvas.js"); BufferedReader br=new BufferedReader(new InputStreamReader(url.openStream())); StringBuffer buf=new StringBuffer(); String line=null; while((line=br.readLine())!=null){ buf.append(line); } return buf.toString(); }catch(Exception e){ return ""; } } private static Map<String,String> paramMap(String param) throws Exception{ Map<String,String> ret=new HashMap<String,String>(); String[] p=param.split("&"); for(int i=0;i<p.length;i++){ String[] k=p[i].split("="); if(k.length<2)continue; ret.put(k[0], k[1]); } return ret; } }デプロイ
herokuへは、heroku-maven-pluginを使用してデプロイしました。
Port番号の解決に気が付かず、実際に動作させるまで2~3時間かかってしまいました。
- 投稿日:2019-03-02T20:24:02+09:00
VScodeエクステンションRUN-CODEでのjavaプログラムの文字コードによるエラー回避
どうしたいか。
VScodeで簡単にコンパイル・実行するためにCode Runnerという拡張機能を使用しています。
MAC上のVScodeで作成したjavaプログラムを、他のOS上のVScodeで動かした場合に文字コードによるエラーが出るので解消する設定を記載します。流行のCoderでもうまくいくようです。設定内容
個別に文字コードの設定をしてやる。(ここでは、UTF-8)
"code-runner.executorMap": {
"java": "cd $dir && javac -encoding UTF-8 $fileName && java -Dfile.encoding=UTF8 $fileNameWithoutExt"
}
関連情報
- 投稿日:2019-03-02T20:24:02+09:00
VScode拡張機能RUN-CODE使用時のjavaでの文字コードエラー回避
どうしたいか。
VScodeで簡単にコンパイル・実行するためにCode Runnerという拡張機能を使用しています。
MAC上のVScodeで作成したjavaプログラムを、他のOS上のVScodeで動かした場合に文字コードによるエラーが出るので解消する設定を記載します。流行のCoderでもうまくいくようです。設定内容
個別に文字コードの設定をしてやる。(ここでは、UTF-8)
"code-runner.executorMap": {
"java": "cd $dir && javac -encoding UTF-8 $fileName && java -Dfile.encoding=UTF8 $fileNameWithoutExt"
}
関連情報
- 投稿日:2019-03-02T18:28:51+09:00
オブジェクト指向でじゃんけん
はじめに
最近Javaを勉強し始めたので、オブジェクト指向で何かを作ってみたかった。
流れ
①名前を入力する。(相手の名前はランダムに決まる。)
②出す手を選ぶ。(相手の手はランダムに決まる。)
③結果を出力する。
④終わり。(今回は一回だけの勝負です。)クラスたち
①Janken.java
メインの関数です。Janken.javapublic class Janken { public static void main(String[] args) { Player you = new You(); Player enemy = new Enemy(); System.out.print("おなまえを入力して下さい。: > "); you.setName(); enemy.setName(); String yourName = you.getName(); String enemyName = enemy.getName(); System.out.println("あなたの なまえは " + yourName); System.out.println("あいての なまえは " + enemyName); Hand yourHand = you.nextHand(); Hand enemyHand = enemy.nextHand(); System.out.println(yourName + "は " + yourHand + " をだした。"); System.out.println(enemyName + "は " + enemyHand + " をだした。"); if (yourHand.winTo(enemyHand)) { System.out.println(yourName + " のかち!"); } else if (yourHand.loseTo(enemyHand)) { System.out.println(yourName + " のまけ…"); } else { System.out.println("あいこです。"); } } }②Player.java
プレイヤーの大雑把な情報を持つ抽象クラスです。
このクラスを継承して、あなたと相手のクラスを作ります。Player.javapublic abstract class Player { protected String name; public String getName(){ return this.name; } public abstract void setName(); public abstract Hand nextHand(); }③you.java
You.javaimport java.util.*; public class You extends Player { @Override public void setName() { Scanner scanner = new Scanner(System.in); String name = scanner.nextLine(); this.name = name; } @Override public Hand nextHand() { Scanner scanner = new Scanner(System.in); while (true) { System.out.print("何を出しますか? グー:0 チョキ:1 パー:2 > "); try { int hand_number = Integer.parseInt(scanner.nextLine()); if (0 <= hand_number && hand_number <= 2) { return Hand.fromInt(hand_number); } else { System.err.println("範囲外の数字が入力されています。"); } } catch (NumberFormatException e) { System.err.println("数字以外が入力されています"); } } } }④Enemy.java
Enemy.javapublic class Enemy extends Player { private final String[] names = {"安倍", "小泉", "麻生", "菅"}; @Override public void setName() { String enemyName = names[(int) (Math.random() * names.length)]; this.name = enemyName; } @Override public Hand nextHand() { return Hand.fromInt((int) (Math.random() * 3)); } }⑤Hand.java
列挙型のクラスを使いました。Hand.javapublic enum Hand { Rock, Scissors, Paper; @Override public String toString() { switch (this) { case Rock : return "グー"; case Scissors : return "チョキ"; case Paper : return "パー"; } throw new IllegalStateException(); } public static Hand fromInt(int n) { switch (n) { case 0 : return Rock; case 1 : return Scissors; case 2 : return Paper; } throw new IllegalArgumentException(Integer.toString(n)); } public boolean winTo(Hand hand) { switch (this) { case Rock : return hand == Scissors; case Scissors : return hand == Paper; case Paper : return hand == Rock; } throw new IllegalStateException(); } public boolean loseTo(Hand hand) { return this != hand && !this.winTo(hand); } }以上。
敵いっぱい作ってトーナメント戦やったり、作戦クラスを作ってみるのも面白そうですね。
改善点あればよろしくお願いいたします。
- 投稿日:2019-03-02T16:31:28+09:00
【Java】protectedで修飾されたメソッドがサブクラス内から呼べるとは限らない
概要
Javaのアクセス修飾子の一つに、protectedというものがあります。protectedについて「同一パッケージ内およびサブクラスからのみアクセス可能」というような説明を聞いたことがないでしょうか。このような理解だと若干の誤解が生じるかもしれないという話をします。
同一パッケージ内からのアクセスについては特に混乱する要素はないと思うので、サブクラスからのアクセスについてのみ言及します。
対象読者
Java初心者
環境
Java11で動作確認を行いましたが、内容としては古いバージョンでも特に変わらないと思います。
うまくいかない例
「サブクラスからアクセス可能」という理解だと、下記のサンプルコードは動作すると思えますが、実際にはコンパイルエラーになります。
(ネーミングセンスについてはご容赦願います…)package test.sp; public class Super { protected void method() { System.out.println("Super#method() called."); } }package test.sub; import test.sp.Super; public class Sub extends Super { public static void main(String[] args) { Sub sub = new Sub(); sub.run(); } private void run() { Super sp = new Super(); sp.method(); // エラー: method()はSuperでprotectedアクセスされます } }protectedで修飾されたmethod()メソッドを持つSuperクラスと、そのSuperクラスを継承したSubクラスがあります。
SuperクラスとSubクラスはそれぞれ別のパッケージにありますが、SubクラスはSuperクラスを継承している(SubクラスはSuperクラスのサブクラスである)ため、Subクラスからmethod()メソッドを呼び出すことは可能であるように思えます。しかし、上記のコードだとコンパイルエラーとなってしまいます。うまくいく例
package test.sp; public class Super { protected void method() { System.out.println("Super#method() called."); } }package test.sub; import test.sp.Super; public class Sub extends Super { public static void main(String[] args) { Sub sub = new Sub(); sub.run(); } private void run() { // (1) Sub sub = new Sub(); sub.method(); // (2) this.method(); // (3) method(); // (4) super.method(); } }今度はうまくいく例ですが、SuperクラスおよびSuperクラスとSubクラスとの関係は変わらず、Subクラスからの呼び出し方法だけ変わっています。(1)から(4)までありますが、どれもコンパイルが通り、実行時にも特にエラーは発生せずに動作しました。
// (1) Sub sub = new Sub(); sub.method();(1)はSubクラスのインスタンスを明示的に生成して、そのインスタンスのmethod()を呼び出しています。Superクラスのインスタンスではコンパイルエラーになりましたが、Subクラスのインスタンスであれば問題ないようです。
// (2) this.method(); // (3) method();(2)はthisでの呼び出しです。これも動作します。(3)はthisの記述を省略しているだけで、(2)と実質同じです。thisは自身のインスタンスを指すわけなので、Subクラスのインスタンスのmethod()を呼んでいるという意味では、(1)〜(3)のいずれも同じです。
// (4) super.method();(1)〜(3)と少し異なるのが(4)です。(4)はsuperのmethod()を呼び出していますが、これも動作します。superは(自身と関連づけられている)スーパークラスのインスタンスを指します。Superクラスのインスタンスを明示的に生成して、そのインスタンスのmethod()を呼び出した際はコンパイルエラーとなったのに、superであれば問題ないのですね。
いずれもSuperクラスのインスタンスという点では変わらないので同じ挙動になると思っていたのですが、ちょっと意外な結果となりました。まとめ
protectedで修飾されたメソッドについて、別パッケージで定義されたサブクラスからの可視性は下記のようになります。
- サブクラス内で、newで明示的に生成されたサブクラス自身のインスタンスから呼び出せる
- サブクラス内のthisから呼び出せる。thisの記述を省略して呼び出す場合も同様
- サブクラス内のsuperから呼び出せる
- サブクラス内で、newで明示的に生成されたスーパークラスのインスタンスからは呼び出せない
- protectedの可視性について「同一パッケージ内およびサブクラスからのみアクセス可能」と理解していると、上記の呼び出せないパターンについて呼び出せると勘違いする可能性がある
蛇足
同一パッケージ内からのみ呼び出し可能にしたいという意図でprotectedで修飾されていると思われるメソッドを見かけることがありますが、この場合はprotectedではなくパッケージプライベート(修飾子なし)を使うべきです。
protectedって個人的にはあまり使いません。(使い方を理解してないのかもしれませんが…)
- 投稿日:2019-03-02T16:19:15+09:00
意外と知らない、Javaの予約語まとめ
概要
Javaの予約語は、頻繁に見かけるものからあまり見かけないものまで、色々あります。
私の独断と偏見で選んだ、あまり見かけない予約語について簡単に紹介します。あまり長くならないように、一つ一つに対してそれほど詳細な話はしません。
なお、Javaの言語仕様上「予約語」は「キーワード(keyword)」と呼ばれるそうですが、
「予約語」のほうが一般的な気がするので、この記事では「予約語」という呼称を使用します対象読者
Java初心者
Javaをある程度使いこなしている方にとっては既知の内容になるかもしれません(予防線)環境
Java11
Javaの予約語一覧
まずは予約語の一覧を見てみます。公式の言語仕様書によると、Java11時点で予約語は下記の通り51個あります。
https://docs.oracle.com/javase/specs/jls/se11/html/jls-3.html#jls-3.9abstract continue for new switch assert default if package synchronized boolean do goto private this break double implements protected throw byte else import public throws case enum instanceof return transient catch extends int short try char final interface static void class finally long strictfp volatile const float native super while _ (underscore)この中からいくつかピックアップして紹介してみます。
予約語(keyword)
assert
Javaで表明(assertion)を使うための予約語です。
表明とはプログラムを実行する際に満たされているべき前提条件を記述するもので、誤解を恐れずに言えば変数の値が想定外の値になっていないかどうか等をチェックする仕組みのようなものです。assertの後に条件式を書き、条件式の評価値がfalseだったらAssertionErrorが投げられます。
例えば、メソッドの引数の値が想定通りの値かどうかをチェックするなどの用途で使用できます。// 割り算 private int divide(int x, int y) { assert y != 0; // 割り算なのでyは0であってはならない return x / y; }また、assertは実行時に-enableassertions(または-ea)を指定しないと有効になりません。
上記オプションを渡すかどうかで、有効無効を切り替えられます。
開発中は有効にして、本番稼働時は無効にするといったことが可能です。const
C言語などでは定数を宣言するための予約語ですが、Javaでは何の意味も持たない予約語です。
何の意味もないのに、なぜか予約語としては存在しています。
constは構文上は何の意味も持たないため、使用するとコンパイルエラーになります。なお、Javaで定数を宣言するにはconstではなくfinalを使用します。
// 予約語なのでシンタックスハイライトはされる const int num = 1; // エラー: // 式の開始が不正です // const int num = 1; // ^default
複数の用途がある珍しい予約語です。バージョンが上がるにつれて追加されていきました。
- switch文で、合致する項目がなかった場合に実行される箇所を示すラベル
- アノテーションの定義で、定数のデフォルト値を指定する修飾子(Java 5から)
- インタフェースの定義で、デフォルトメソッドであることを示す修飾子(Java 8から)
switch文の例
switch(i) { case 1: System.out.println("一"); break; case 2: System.out.println("二"); break; default: System.out.println("その他"); }アノテーションの例
@Target({ElementType.FIELD}) @Retention(RetentionPolicy.RUNTIME) @Documented public @interface Range { int min() default Integer.MIN_VALUE; int max() default Integer.MAX_VALUE; }インタフェースのデフォルトメソッドの例
public interface Person { default String name() { return "unknown"; } }goto
C言語などでは任意の箇所に制御を飛ばすのに使用する予約語ですが、const同様にJavaでは何の意味もないのに存在している予約語です。
gotoに相当する機能はJavaにはありません。gotoを乱用するとソースコードの可読性が著しく下がるため、比較的モダンな言語では同等の機能が存在しないことが多いです。constと同様、使用するともちろんコンパイルエラーです。
// やはりシンタックスハイライトはされる goto label; // エラー: // 式の開始が不正です // goto label; // ^native
メソッドの中身がC言語などのネイティブ言語で実装されていることを示す予約語です。
JNIなどを使って実行されます。public native boolean createDirectory(File f);strictfp
浮動小数点の計算結果が、実行環境に依存して変化しないようにする(どの実行環境でも同じ結果になるようにする)ための予約語です。
メソッドやクラスを修飾します。
逆に言えば、strictfpで修飾しない場合は浮動小数点の計算結果は実行環境によって変わる場合があるということです。個人的には、実際に使われているのを一度も見たことがありません。
private strictfp double calc(double x, double y) { return x * y; }transient
修飾したフィールドがシリアライズの対象外になります。
シリアライズとは、オブジェクトの内容をファイルに保存したり、他のマシンに送信したりできるように変換を行うことです。
transientで修飾することで、シリアライズ後のデータから該当のフィールドが除外されます。public class Person implements Serializable { private String firstName; private String lastName; private transient String fullName; }volatile
修飾したフィールドについて、シングルスレッド前提の最適化を抑止します。
見たことはあるが、どういう意味があるのかわからないという方もいらっしゃると思います。
私もよくわかりません。volatileで修飾しない場合、コンパイラが最適化のためにソースと若干違う内容のバイトコードを出力することがありますが、volatileによってそのような最適化が抑止されます。
シングルスレッドの場合は特に影響ありませんが、マルチスレッドだとコンパイラによる最適化によって不整合が生じる場合があります。volatileはそのような不整合の発生を防ぐのに使われます。volatile boolean shutdownRequested; ... public void shutdown() { shutdownRequested = true; } public void doWork() { while (!shutdownRequested) { // do stuff } }https://www.ibm.com/developerworks/jp/java/library/j-jtp06197.html より
上記例では、shutdown()が他のスレッドから呼ばれたらループを抜けるという動作を期待していますが、shutdownRequestedをvolatileで修飾しないと想定通りに動作しない可能性があります。
…そのはずだったのですが、実際試したらvolatileなしでも普通に動きました。誰か詳しい人教えてください!
_(underscore)
Scalaなどの言語では、_はプレースホルダ等の用途で使用しますが、Javaにはそのような機能はありません。const、gotoと同様に意味のない予約語(記号?)です。
_が予約語になったのはJava9と比較的最近で、将来の機能拡張を見据えてのことなのかもしれません。int _ = 1; // エラー: // リリース9から'_'はキーワードなので識別子として使用することはできません // int _ = 1; // ^番外編
かつて存在した予約語
widefp
strictfpの逆で、浮動小数点の計算結果が環境に依存するようになります。
現在は既定でそのような動作であるため、widefpは削除されました。予約語っぽいけど予約語じゃないもの
リテラル
true および false
おなじみのbooleanリテラルです。実はこれらはJavaの言語仕様上は予約語ではないのです。
まあ、識別子としては使えないので実質予約語みたいなものですけどね。null
おなじみのnullリテラルです。これもbooleanリテラルと同様に、識別子としては使えませんが、言語仕様上は予約語ではありません。
制限されたキーワード (restricted keyword)
open, module, requires, transitive, exports, opens, to, uses, provides, with
いずれもモジュール機能のためにJava9で追加されました。一つ一つ取り上げていると長くなる
し私もよく把握できていないので説明は省略します。
いずれも特別な意味を持つ言葉ですが、変数名などとして使うことが可能です。おそらく、互換性のためでしょうね。String module = "module";予約された型名(reserved type name)
var
Java10から追加された型推論機能のために使われます。
これも上記の「制限されたキーワード」と同様、特別な意味を持ちますが変数名などとして使うことが可能です。これも互換性のために予約語としていないのかもしれません。var var = 1;まとめ
Javaの予約語から、予約語っぽいけど予約語じゃないものまで、いろいろ見てきました。
そんなのあったんだ、と思ってもらえたらうれしいです。参考リンク
https://docs.oracle.com/javase/specs/jls/se11/html/jls-3.html#jls-3.9
https://ja.wikipedia.org/wiki/%E3%82%AD%E3%83%BC%E3%83%AF%E3%83%BC%E3%83%89_(Java)
https://java.keicode.com/lang/assert.php
https://www.wdic.org/w/TECH/strictfp
https://www.ibm.com/developerworks/jp/java/library/j-jtp06197.html
https://www.wdic.org/w/TECH/%E4%BA%88%E7%B4%84%E8%AA%9E%20%28Java%29
- 投稿日:2019-03-02T15:41:23+09:00
SalesforceのデータローダーがZulu OpenJDK 11でリリース
概要
Salesforceのデータローダーは、これまでOracle Java 8でビルドされていましたが、Ver.45のリリースで「Zulu OpenJDK 11」でビルドされるようになりました。下記のGitHubで公開されているので、早速導入してみました。
https://github.com/forcedotcom/dataloader/releases/tag/v45.0.0Zuluとは
https://qiita.com/nowokay/items/edb5c5df4dbfc4a99ffb
ZuluはAzul Systemsが提供しているOpenJDKビルドです。Azul Systemsは OpenJDKのサポートを行う企業で元OracleのSimon Ritter氏が属している会社です。
CustomerにMicrosoftもあるので、たぶんMicrosoft Azureもzuluを使っているんではなかろうか。手順
Dataloaderをダウンロード
https://github.com/forcedotcom/dataloader/releases/download/v45.0.0/dataloader_mac.zip
現時点では、まだdmgは公開されておらず、jarを直接実行して起動する必要があります。
Zuluを入れないで、Java 8で実行すると下記のエラーになります。Error: A JNI error has occurred, please check your installation and try again Exception in thread "main" java.lang.UnsupportedClassVersionError: com/salesforce/dataloader/process/DataLoaderRunner has been compiled by a more recent version of the Java Runtime (class file version 55.0), this version of the Java Runtime only recognizes class file versions up to 52.0 at java.lang.ClassLoader.defineClass1(Native Method) at java.lang.ClassLoader.defineClass(ClassLoader.java:763) at java.security.SecureClassLoader.defineClass(SecureClassLoader.java:142) at java.net.URLClassLoader.defineClass(URLClassLoader.java:468) at java.net.URLClassLoader.access$100(URLClassLoader.java:74) at java.net.URLClassLoader$1.run(URLClassLoader.java:369) at java.net.URLClassLoader$1.run(URLClassLoader.java:363) at java.security.AccessController.doPrivileged(Native Method) at java.net.URLClassLoader.findClass(URLClassLoader.java:362) at java.lang.ClassLoader.loadClass(ClassLoader.java:424) at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:349) at java.lang.ClassLoader.loadClass(ClassLoader.java:357) at sun.launcher.LauncherHelper.checkAndLoadMain(LauncherHelper.java:495)Zulu Open JDKをインストール
https://www.azul.com/downloads/zulu
※Java 8の無料アップデートは、あと45日とのこと・・・PATH(
JAVA_HOME
)をアップデートJava 8のパスが設定されているはずなので、Zuluのパスでアップデートします。
export JAVA_HOME=/Library/Java/JavaVirtualMachines/<zulu_package>/Contents/Home/コマンドから起動
Macである場合は、下記のコマンドからDataloaderを起動できます。
java -XstartOnFirstThread -jar target/dataloader-xx.0-uber.jarまとめ
JDKが変わっただけで大きな変化はないと思いますが、本番での作業等を行う場合には念の為に早めに新しいバージョンに切り替えて実施した方がいいです。
- 投稿日:2019-03-02T14:59:39+09:00
Java 融通がきかないStringクラスのsubstring
JDK 1.8で書いています。
String
クラスのsubstring
String
クラスには、文字列の一部を切り出すsubstring
メソッドがあります。引数の数に応じて2種類の切り出し方をしてくれます。引数が1つの場合
定義
substring(int beginIndex)
beginIndex
以降の文字を切り出してくれる。文字列のインデックスは0
から数えることに注意。使用例
String str = ("ドラえもん"); String cutStr = str.substring(2); System.out.println("切り出し後:" + cutStr);結果
切り出し後:えもん指定しているインデックス
2
にある文字え
以降の文字列が切り出される。引数が2つの場合
定義
substring(int beginIndex, int endIndex)
beginIndex
以降の文字をendIndex-1
の文字まで含めて切り出してくれる。
使用例String str = ("ドラえもん"); String cutStr = str.substring(2, 4); System.out.println("切り出し後:" + cutStr);結果
切り出し後:えも指定している
beginIndex = 2
にある文字え
以降から、endIndex - 1 = 3
にある文字も
までの文字列えも
が切り出される。ここが微妙
引数が2つの場合、文字列が
endIndex - 1
に満たない長さの場合、例外が出ます。ユースケース
画面表示する文字列を出力する。画面には4文字までしか表示できない制限があるため、出力する文字列を4文字までに絞りたい。もし4文字に満たない文字列であればそのまま出したい。
コード
String str1 = ("ドラえもん"); String str2 = ("のび太"); String str3 = ("スネ夫"); List<String> sourceStrList = Arrays.asList(str1, str2, str3); List<String> outStrList = new ArrayList<>(); for (String sourceStr : sourceStrList) { outStrList.add(sourceStr.substring(0, 4)); } System.out.println("切り出し後:" + outStrList);結果
Exception in thread "main" java.lang.StringIndexOutOfBoundsException: String index out of range: 4はい、例外発生。ここでは
のび太
とスネ夫
はインデックスが2
までしかないので、endIndex - 1 = 3
に満たないため、例外になるのです。
解消するには事前に文字の長さを調べる必要があるので面倒くさいですね~。
StringUtils
のsubstring
を使って解決
org.apache.commons.lang3.StringUtils
に用意されているsubstring
を使えば、この辺の気をきかして解決してくれます。substring(String str, int start, int end)
str
に入れた切り出し対象の文字列のインデックスがstart
からend - 1
の文字列が出力されます。コード
String str1 = ("ドラえもん"); String str2 = ("のび太"); String str3 = ("スネ夫"); List<String> sourceStrList = Arrays.asList(str1, str2, str3); List<String> outStrList = new ArrayList<>(); for (String sourceStr : sourceStrList) { outStrList.add(StringUtils.substring(sourceStr, 0, 4)); } System.out.println("切り出し後:" + outStrList);結果
切り出し後[ドラえも, のび太, スネ夫]
end - 1 = 3
に満たない文字列も、そのまま出力してくれます。参考
java.lang String substring
org.apache.commons.lang3 Class StringUtils substring
- 投稿日:2019-03-02T13:36:24+09:00
doma2でデータベース連携(Spring boot)
※アウトプット用メモですので他記事と被っている箇所があるかもしれません。悪しからず。
Doma2とは
データベースアクセスに使います。特徴としては以下。
公式(https://doma.readthedocs.io/en/stable/)によるとDoma 2は、Java 8以降用のデータベースアクセスフレームワークです。Domaにはさまざまな長所があります。
・注釈処理を使用してコンパイル時にソースコードを検証して生成します。
・データベース列をユーザー定義のJavaオブジェクトにマップします。
・「2-way SQL」と呼ばれるSQLテンプレートを使用します。
・java.time.LocalDate、 java.util.Optionalおよびjava.util.stream.StreamなどJava 8で導入されたクラスをサポート
・他のライブラリに依存しないほとんど翻訳のままですので少し日本語がおかしいかもしれません。。。
大きな利点としては以下の2点であるかと
・JRE 以外のライブラリへの依存が一切ない(依存性には敏感ですww)
・SQLをファイルですべて管理できる(疎結合ってやつですかね?)
これは自分が使ってみた意見ですが、SQLファイルで管理することで過剰な共通化を防ぐ利点があると思いました。
共通化のしすぎは逆にわかりにくくなることがあります。実装
公式にサンプルプロジェクトがありましたのでこちらがわかりやすいかと思います。
公式サンプル
主に必要な構成としては
・Dao (Daoはデータベースアクセスのためのインタフェース)
・Entity (テーブルを表現する(だけではないですが)クラス)
・SQLファイル (クエリー)
になります。DaoでCRUD処理をするメソッドを定義します。
MstEmployeeDao.javapackage com.example.dao; import com.example.entity.MstEmployee; import com.example.entity.UserEntity; import org.seasar.doma.Dao; import org.seasar.doma.Insert; import org.seasar.doma.Select; import org.seasar.doma.boot.ConfigAutowireable; import org.springframework.transaction.annotation.Transactional; import java.util.List; /** 従業員マスタのDaoインターフェース. */ @ConfigAutowireable @Dao public interface MstEmployeeDao { @Select List<MstEmployee> selectAll(); @Select MstEmployee selectOne(String id); @Select UserEntity selectUser(String id); @Insert @Transactional int insert(MstEmployee mstEmployee); }このメソッドでやりたい問い合わせをSQLファイルにしてやろう。という使い方です。SQLファイルを配置する場所は
META-INF
の中に上記クラス内のpackage com.example.dao;
と同じようにフォルダ階層を作成しかつファイル名をメソッド名と同じ名前にします(拡張子はsql)サンプルプロジェクトのtreeを見るとわかりやすいと思います。
tree├── pom.xml ... Maven設定ファイル └── src ├── main │ ├── java │ │ └── com │ │ └── example │ │ ├── App.java ... アプリケーション実行ファイル │ │ ├── SecurityConfig.java ... セキュリティ設定ファイル │ │ ├── dao ... Doma2のDAOクラス格納パッケージ │ │ │ ├── MstEmployeeDao.java │ │ │ ├── MstNewsDao.java │ │ │ └── MstRoleDao.java │ │ ├── dto │ │ │ └── NewsDto.java │ │ ├── entity ... Doma2のEntityクラス格納パッケージ │ │ │ ├── AuditEntity.java │ │ │ ├── MstEmployee.java │ │ │ ├── MstNews.java │ │ │ ├── MstRole.java │ │ │ ├── UserEntity.java │ │ │ └── listener │ │ │ └── AuditListener.java │ │ ├── security ... Spring Securityの認証機能 │ │ │ ├── LoginUserDetails.java │ │ │ ├── LoginUserDetailsService.java │ │ │ └── UserInfo.java │ │ ├── service ... サービスクラス格納パッケージ │ │ │ ├── NewsService.java │ │ │ └── NewsServiceImpl.java │ │ └── web ... Spring MVC コントローラクラス格納パッケージ │ │ ├── LoginController.java │ │ ├── NewsController.java │ │ ├── TopController.java │ │ └── manager │ │ ├── NewsForm.java │ │ ├── NewsManagerEditController.java │ │ ├── NewsManagerListController.java │ │ └── NewsManagerRegisterController.java │ └── resources │ ├── META-INF │ │ └── com │ │ └── example │ │ └── dao ... Doma2のSQLファイル格納パッケージ │ │ ├── MstEmployeeDao │ │ │ ├── selectAll.sql │ │ │ ├── selectOne.sql │ │ │ └── selectUser.sql │ │ ├── MstNewsDao │ │ │ ├── selectAll.sql │ │ │ ├── selectNewsDtoByCond.sql │ │ │ └── selectOneNewsDto.sql │ │ └── MstRoleDao │ │ └── selectAll.sql │ ├── ValidationMessages.properties ... バリデーションチェックのメッセージ設定ファイル │ ├── application.yml ... アプリケーション設定ファイル │ ├── data.sql ... 起動時に実行するDDL文 │ ├── messages.properties ... バリデーションチェックメッセージのFormプロパティ置換設定ファイル │ ├── schema.sql ... 起動時に実行するDML文 │ ├── static ... 実行時にはコンテキストルート直下になる静的ファイル格納フォルダ │ │ ├── css │ │ │ └── lib ... CSSライブラリ格納フォルダ │ │ │ ├── bootstrap-theme.min.css │ │ │ └── bootstrap.min.css │ │ ├── fonts ... フォント格納フォルダ(Bootstrap) │ │ │ ├── glyphicons-halflings-regular.eot │ │ │ ├── glyphicons-halflings-regular.svg │ │ │ ├── glyphicons-halflings-regular.ttf │ │ │ ├── glyphicons-halflings-regular.woff │ │ │ └── glyphicons-halflings-regular.woff2 │ │ ├── js │ │ │ └── lib ... JavaScriptライブラリ格納フォルダ │ │ │ └── bootstrap.min.js │ │ └── movie │ │ ├── sample │ │ │ └── nc144421.mp4 │ │ └── thumbnail │ │ ├── nc144421.jpg │ │ └── nc144555.jpg │ └── templates ... Thymeleafテンプレート格納フォルダ │ ├── learning │ │ └── learning.html │ ├── loginForm.html │ ├── manager │ │ ├── managerLayout.html │ │ └── news │ │ ├── edit │ │ │ ├── newsEditComplete.html │ │ │ ├── newsEditConfirm.html │ │ │ └── newsEditInput.html │ │ ├── list │ │ │ └── newsList.html │ │ └── register │ │ ├── newsRegisterComplete.html │ │ ├── newsRegisterConfirm.html │ │ └── newsRegisterInput.html │ ├── news │ │ └── news.html │ └── top │ └── top.html └── test ... テストコード格納フォルダ └── java └── com └── example └── service └── NewsServiceImplTest.javaパラメータの書き方
以下のようにsqlファイルを記述することでDaoクラスの変数とsqlファイルの変数をバインドすることができます。
selectOneSELECT employee_id, employee_last_name, employee_first_name, role_id FROM mst_employee WHERE employee_id = /* id */'employee_id';さらに、以下のように記述することで動的条件を表現できます。
if文とか、アノテーションが使えるんですね。selectNewsDtoByCond.sqlselect n.mst_news_id id, n.role_id role_id, r.role_name role_nm, n.subject subject, n.url url, n.version version FROM mst_news n INNER JOIN mst_role r ON n.role_id = r.role_id WHERE /*%if @isNotEmpty(url) */ n.url LIKE /* @prefix(url) */'http' /*%end*/ /*%if @isNotEmpty(subject) */ AND n.subject LIKE /* @prefix(subject) */'今日' /*%end*/ /*%if @isNotEmpty(roleId) */ AND n.role_id = /* roleId */'01' /*%end*/ ORDER BY n.mst_news_idSpring bootのデータベースアクセスではJPAを使っていましたが、こっちの方が標準みたいですね。
- 投稿日:2019-03-02T13:26:58+09:00
迷路を作って解くJava実装
本文
- やったこと:Javaで迷路を作り、到達可能か求める(深さ優先探索 DFS)、または最短経路を求める(幅優先探索 BFS)
- ソース => GitHub
- 実行結果 => Ideone
- その他の入力例 => README
実行時の注意
- 6つの引数が必須、順番は固定
-debug
で迷路・経路を標準出力に書き込める
- ただし出力量制限はない
- 小さい迷路から大きな迷路へ、どのくらい表示できるか確認しながら出力した方がいい
-debug
の代わりに-no-denug
とか適当に入れれば標準出力OFFになる- 大きさは2501*2501を超えた辺りから、生成の実行時間が長くてつらい
- 4つ目の引数circuitsに大きな数字を入れると、生成の実行時間が長くてやっぱりつらい
- 100000とか無理
雑感
- 迷路を作る実装はレアかも
- 1.最短距離と2.書き換えた経路の両方を返す方が好ましいが、面倒なのでやってない
- GoとかPythonとかRubyとか見習ってほしい
- 投稿日:2019-03-02T11:28:26+09:00
Android の CSV ライブラリ
Java のライブラリなら、
なんでも Android で使用できると思っていたが。
そうではなかったので、まとめておく。Open CSV
カンマで分解して、文字列の配列にして、並び順で取得する。
http://opencsv.sourceforge.net/
サンプルプログラムは、ここに
https://github.com/ohwada/Android_Samples/tree/master/Csv1下記のようなヘッダー行があるファイル
は、Java オブジェクトにマッピングする機能もある。"Name","Quantity" "Apple", "10" "Banana", "20"しかし、Android では、下記のエラーになる。
NoClassDefFoundError: Failed resolution of: Ljava/beans/Introspector
Android に は必要なライブラリがないようです。
参考 : Didn't find class “java.beans.Introspector” when Using OpenCSV to parse csv files
univocity-parsers
CSV ファイルを Java オブジェクトにマッピングする機能がある。
https://www.univocity.com/pages/univocity_parsers_tutorial
使い方
CSV ファイルのカラムに合わせたJavaクラスを用意する。
アノテーションでカラムと変数の対応を指定する。@Data public class Hoge { @Parsed(field = "Name") public String name = ""; @Parsed(field = "Quantity") public int quantity = 0;CSV ファイルの読み込み
// Assetフォルダーから読み込む場合 Reader reader = new InputStreamReader( getAssets().open(ファイル名) ); CsvParserSettings settings = new CsvParserSettings(); BeanListProcessor<Shopping> rowProcessor = new BeanListProcessor<>(Hoge.class); settings.setProcessor(rowProcessor); CsvRoutines routines = new CsvRoutines(settings); List<Hoge> list = routines.parseAll( Hoge.class, reader );サンプルプログラムは、ここに
https://github.com/ohwada/Android_Samples/tree/master/Csv2Apache Commons CSV
カンマで分解して、文字列の配列にして、並び順で取得する。
https://commons.apache.org/proper/commons-csv/
サンプルプログラムは、ここに
https://github.com/ohwada/Android_Samples/tree/master/Csv3
- 投稿日:2019-03-02T06:17:42+09:00
Java Servlet 4.0のweb.xmlのスキーマ定義
Java Servlet 4.0のスキーマ定義が覚えられないのでメモ。
公式の定義がどこに載っているのかわからなかった。web.xml<?xml version="1.0" encoding="UTF-8"?> <web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd" version="4.0"> </web-app>ついでにスキーマ定義の意味もわかっていなかったので調べてみた
xmlns="http://xmlns.jcp.org/xml/ns/javaee"
「名前空間の名前」を定義する。
これでweb.xmlで使っているタグ要素とかがどの名前空間に属しているかが定義される。
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
(一つ宣言を飛ばして)
ここで先に定義した名前空間http://xmlns.jcp.org/xml/ns/javaee
のスキーマ定義ファイル(.xsd)の参照先を指定している。
つまり、とかといった要素は「名前空間http://xmlns.jcp.org/xml/ns/javaee
に属していて、その定義内容はhttp://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd
の定義ファイルを参照してね。」って意味と理解しました。
ちなみにweb-app_4_0.xsdが、Java Servlet 4.0を指していると思われる。
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
で、一つ飛ばしたこの宣言は、
xsi:schemaLocation
としてスキーマ定義ファイルの場所を指定する要素schemaLocation
が属している名前空間を定義していて、それを別名xsi
で宣言している。つまりまとめると
web.xmlでは2つの名前空間を定義しており、それぞれの用途は以下の通りという解釈。
- 使っている要素(タグ)の名前空間は、
http://xmlns.jcp.org/xml/ns/javaee
に属している。http://www.w3.org/2001/XMLSchema-instance
は、1の名前空間の定義ファイルの場所を示すschemaLocation
を使うためだけに定義している。参考
- 投稿日:2019-03-02T01:04:27+09:00
Dockerでのデータベース環境構築in Spring boot(IntellJ)
Spring boot + dockerでデータベース連携するぞ!
初記事。
ご指摘たくさんください。Spring bootでポートフォリオを作ろうと思った時にdockerでのDB構築に手間取ったのでメモ。
今回のゴール
サンプルプロジェクトにてdockerを用いて構築したデータベース(Mysql)の値を画面へ出力する
・対象読者...
docker初心者
or
開発経験の浅い(1、2年)エンジニア今回注力する観点
・dockerfileの作成の仕方
・docker上に作成したMySQL環境とspring bootプロジェクトの連携※dockerの概念などはザックリ説明→とりあえずdockerとアプリケーションを連携するところまで
dockerとは
@kotaro-drさんの記事を載せさせていただきます。
【図解】Dockerの全体像を理解する -前編-
┗ドキュメントが綺麗で初学者でもかなりわかりやすい
【図解】Dockerの全体像を理解する -中編-
┗中編の最初にはデータベースの永続化にも関わってくるデータ管理の話が出てくるので今回は最低そこまでチラ見すればOKです。
ついでに後編も載せておきます。
【図解】Dockerの全体像を理解する -後編-なぜdockerなのか
・Vurtualboxよりも軽量である←テーマと焦点がズレるため詳しくは割愛。
・開発環境をすぐに用意できる
┗「この端末では動くのに自分の端末では動かない。」ということがなくなる。
┗知り合いに質問したりteratailなどのQAサービスで質問したりするときに自分の今の環境をそのまま相手に渡すことができ、回答が返って来やすくなる。
※今回はローカルマシンにIntelliJとJavaが入っていることを前提に進めます。Javaをdockerに含めることもできますが今回の観点を
「dockerでのMysql環境の構築とアプリケーションとの連携」としているため。
IntelliJとJavaのインストールについては以下に載せます。
■IntelliJ
IntelliJ -for mac
IntelliJ -for Windows
■Java11
Java -for mac
Java -for Windows早速やってみよう
■環境情報
言語:Java(jdk11)
FW:Spring boot
IDE:IntelliJ
※今回、IDEをIntelliJにしてみました。操作感はほぼeclipseと同じですが、UIがなんかいい感じ。今では世界シェアはeclipseを抜いているとかいないとか。。。
まずはプロジェクトを簡単に作ってみたいと思います。
その前にIntelliJにjdkのパスを登録します。以下リンク内で、【Javaプロジェクトの作り方】を検索すると出てきます。
JDKの登録今回作成するプロジェクトのtreeです。(画像ですみません)
今までリンクばかりでしたが、そろそろ自分の言葉を出していきたいと思います。
プロジェクトの作成
今回はデータベースの値を全て出す簡単なモノを作ります。
というか見ればわかるレベルなので3分クッキングパターンで、出来たものが以下になります。
docker(github)
※要所でコメントを入れていますのでもし解釈が間違っている等ありましたらご指摘頂けると助かります。
application.properties
についてspring.datasource.url=jdbc:mysql://localhost:3306/demo?characterEncoding=UTF-8&serverTimezone=JST #//demoが今回のデータベース名 spring.datasource.username=root //ユーザ名 spring.datasource.password=p@ssw0rd //パスワード spring.jpa.hibernate.ddl-auto=update //アプリケーション起動時に、Entityに対応するテーブルがなければ作成こちらはデータベース連携に必要な最低限の設定です。のちに作成するdocker-compose.ymlの内容と合わせる必要がありますので注意してください。
dockerfileの作成
今回はsrcと同じ階層にdockerというフォルダを作成しその中に関連ファイルを作っていこうと思います。
場所についての記述が見つけられなかったため特に指定はないと認識しています。fockerfileFROM mysql:8.0 #dockerhubより取得するイメージを指定 RUN /bin/cp -f /etc/localtime /etc/localtime.org RUN /bin/cp -f /usr/share/zoneinfo/Asia/Tokyo /etc/localtime COPY ./my.cnf /etc/mysql/conf.d/ RUN mkdir -p /var/log/mysql RUN chown mysql.mysql /var/log/mysqlCOPY:コマンドの左側がローカル側、右側がdockerイメージ側のことを書いております。
RUN:対象のイメージにインストールされているコマンドを実行できるdocker-compose.ymlversion: '3' services: mysql: build: ./mysql environment: - MYSQL_DATABASE=demo - MYSQL_ROOT_USER=root - MYSQL_ROOT_PASSWORD=p@ssw0rd - TZ=Japan volumes: - ./initdb.d:/docker-entrypoint-initdb.d - ./dbdata:/var/lib/mysql ports: - "3306:3306"↑コロン(:)で区切られている場合
・左側がホスト側、右側がコンテナ側のパスを表す
・初期データ投入→initdb.dディレクトリに最初に読み込むsqlファイルを入れる
永続化→dbdataに処理をするごとにデータ相当のファイルがどんどん入ってくる
docker-composeを起動させよう
プロジェクト上のdocker-compose.ymlを右クリックし「再生」をします。
すると、さっき書いたdockerfile
やdocker-compose.yml
を解釈してせっせか環境構築が始まります。
以下のように'Compose: docker' has been deployed successfully.
と出たら完成です。
では早速、データベースが出来上がっているかコンテナに入って確認してみます。
ターミナル(Windowsの場合はコマンドプロンプト)でプロジェクトのディレクトリへ移動します。
その中で
docker ps
コマンドで現在のコンテナの稼働状況を確認します。
実行後が下図です
mysqlのイメージができていることがわかります。
起動にはこちらのCONTAINER IDもしくはNAMESを使います。今回はCONTAINER IDを使って起動してみます。
docker exec -it 【CONTAINER ID】 bash
でコンテナに入ります。
root@37c06170b19b:/#
こんな感じのターミナル(コマンドプロンプト)になっていれば入れています。
この画面よりrootユーザでMysqlへ接続します。
mysql -u root
今回はパスワードをp@ssw0rd
としてます。
以下のようにmysqlへ接続
use demo
で今回のサンプルデータベースを指定。あとは適当にsqlをうって確認します。初期データとしてはuser
テーブルが入っていますので。
select * from user;
テーブルの中身を確認できました。あとは自分のショボいサンプルプロジェクトを
DockerApplication
から実行すると...
以下のようにデータベースとの連携に成功しました。
このようにdockerを使えば環境ごと相手に渡すことができるのでわざわざ相手にデータベースのセットアップやデータの投入をさせずに済みます。
dockerの知識はまだまだ浅いですが、これからどんどん使われていく事になると思いますので、積極的に使っていきたいものです。次回は、Spring bootで認証画面を試してみたいと思います。