- 投稿日:2020-07-10T23:58:01+09:00
例外処理が必須なクラス
チェック例外とは、開くべきはずのファイルがない等、
プログラムの不具合とは関係なく、 状況によっては発生しうる状況とされるもので、例外処理が必須となります。ClassNotFondException
Excptionクラスのサブクラス。
クラスをロード出来ない場合に発生します。
例外処理は必須です。IOException
Exceptionクラスのサブクラス。
入出力機能を使用する場合に発生します。
例外処理は必須です。FileNotFoundException
Exceptionクラスのサブクラス。
ファイルに対する読み書きを行う際、対象のファイルが存在しない場合に発生します。
例外処理は必須です。
- 投稿日:2020-07-10T22:43:22+09:00
Android studioに関する質問です。
自作のandroidアプリを作成しているのですが、アプリを起動しても繰り返し停止しています、と表示され、うまく動きませんLogcatを確認したのですが、以下の通り表示されるのですが、よくわかりません。どうやって修正すればいいでしょうか。
2020-07-10 22:39:52.674 17730-17730/? E/AndroidRuntime: FATAL EXCEPTION: main
Process: com.example.fumemo, PID: 17730
java.lang.RuntimeException: Unable to instantiate activity ComponentInfo{com.example.fumemo/com.example.fumemo.MainActivity}: java.lang.NullPointerException: Attempt to invoke virtual method 'android.content.pm.ApplicationInfo android.content.Context.getApplicationInfo()' on a null object reference
at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:3194)
at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:3409)
at android.app.servertransaction.LaunchActivityItem.execute(LaunchActivityItem.java:83)
at android.app.servertransaction.TransactionExecutor.executeCallbacks(TransactionExecutor.java:135)
at android.app.servertransaction.TransactionExecutor.execute(TransactionExecutor.java:95)
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:2016)
at android.os.Handler.dispatchMessage(Handler.java:107)
at android.os.Looper.loop(Looper.java:214)
at android.app.ActivityThread.main(ActivityThread.java:7356)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:492)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:930)
Caused by: java.lang.NullPointerException: Attempt to invoke virtual method 'android.content.pm.ApplicationInfo android.content.Context.getApplicationInfo()' on a null object reference
at android.content.ContextWrapper.getApplicationInfo(ContextWrapper.java:163)
at android.view.ContextThemeWrapper.getTheme(ContextThemeWrapper.java:174)
at android.content.Context.obtainStyledAttributes(Context.java:738)
at androidx.appcompat.app.AppCompatDelegateImpl.createSubDecor(AppCompatDelegateImpl.java:692)
at androidx.appcompat.app.AppCompatDelegateImpl.ensureSubDecor(AppCompatDelegateImpl.java:659)
at androidx.appcompat.app.AppCompatDelegateImpl.findViewById(AppCompatDelegateImpl.java:479)
at androidx.appcompat.app.AppCompatActivity.findViewById(AppCompatActivity.java:214)
at com.example.fumemo.MainActivity.(MainActivity.java:35)
at java.lang.Class.newInstance(Native Method)
at android.app.AppComponentFactory.instantiateActivity(AppComponentFactory.java:95)
at androidx.core.app.CoreComponentFactory.instantiateActivity(CoreComponentFactory.java:41)
at android.app.Instrumentation.newActivity(Instrumentation.java:1250)
at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:3182)
at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:3409)
at android.app.servertransaction.LaunchActivityItem.execute(LaunchActivityItem.java:83)
at android.app.servertransaction.TransactionExecutor.executeCallbacks(TransactionExecutor.java:135)
at android.app.servertransaction.TransactionExecutor.execute(TransactionExecutor.java:95)
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:2016)
at android.os.Handler.dispatchMessage(Handler.java:107)
at android.os.Looper.loop(Looper.java:214)
at android.app.ActivityThread.main(ActivityThread.java:7356)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:492)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:930)
- 投稿日:2020-07-10T18:58:08+09:00
Ruby と Java で解く AtCoder ABC129 D 2次元配列
はじめに
AtCoder Problems の Recommendation を利用して、過去の問題を解いています。
AtCoder さん、AtCoder Problems さん、ありがとうございます。今回のお題
AtCoder Beginner Contest D - Lamp
Difficulty: 1080今回のテーマ、2次元配列
やっていることは、単純です。
...#..#. 元の配列 12301201 元の配列を左から右にスキャン 33302201 スキャンした配列を右から左にスキャン 合計2回スキャンして左右方向の光の届く範囲を求めます。
次に、上下に2回スキャンして上下方向の光の届く範囲を求めます。左右用の配列と上下用の配列の値を合計し、その最大値を求めます。
Java
lamp.javaimport java.util.*; class Main { public static void main(String[] args) { final Scanner sc = new Scanner(System.in); final int H = Integer.parseInt(sc.next()); final int W = Integer.parseInt(sc.next()); final char S[][] = new char[H+2][W+2]; for (int i=1; i<H+1; i++) { S[i] = ("#" + sc.next() + "#").toCharArray(); } sc.close(); for (int i=0; i<W+2; i++) { S[0][i] = '#'; S[H+1][i] = '#'; } int lr[][] = new int[H+2][W+2]; int ud[][] = new int[H+2][W+2]; for (int i=1; i<H+1; i++) { int cnt = 0; for (int j=0; j<W+1; j++) { if (S[i][j]=='.') { cnt++; } else { cnt = 0; } lr[i][j] = cnt; } for (int j=W; j>0; j--) { if (lr[i][j]==0) { cnt = 0; } else if (cnt==0) { cnt = lr[i][j]; } else { lr[i][j] = cnt; } } } for (int j=1; j<W+1; j++) { int cnt = 0; for (int i=0; i<H+1; i++) { if (S[i][j]=='.') { cnt++; } else { cnt = 0; } ud[i][j] = cnt; } for (int i=H; i>0; i--) { if (ud[i][j]==0) { cnt = 0; } else if (cnt==0) { cnt = ud[i][j]; } else { ud[i][j] = cnt; } } } int ans = 0; for (int i=1; i<H+1; i++) { for (int j=1; j<W+1; j++) { int cnt = lr[i][j] + ud[i][j]; if (ans<cnt) ans = cnt; } } ans -= 1; System.out.println(ans); } }なんの捻りもないコードです。
スクリプト言語などで競プロをすることについて
スクリプト言語の速度について書かれたブログです。
その中で、スクリプト言語で厳しい例として取り上げられているのが、今回の D - Lampになります。Ruby
ruby.rbh, w = gets.split.map(&:to_i) s = Array.new(h + 1).map{Array.new(w + 1, 0)} 1.upto(h) do |i| c = 0 l = 1 b = gets 1.upto(w) do |j| if b[j - 1] == '.' l = j if c == 0 c += 1 elsif c > 0 l.upto(j - 1) do |k| s[i][k] = c end c = 0 end if j == w && c > 0 l.upto(j) do |k| s[i][k] = c end end end end ans = 0 1.upto(w) do |j| c = 0 l = 1 1.upto(h) do |i| if (s[i][j] > 0) l = i if c == 0 c += 1 elsif c > 0 l.upto(i - 1) do |k| ans = s[k][j] + c if ans < s[k][j] + c end c = 0 end if i == h && c > 0 l.upto(i) do |k| ans = s[k][j] + c if ans < s[k][j] + c end end end end puts ans - 1スキャンの回数を少し工夫したコードで旧環境
Ruby (2.3.3)
ではTLE
でしたが、新環境Ruby (2.7.1)
では何とか通る様です。
C++14 Java Ruby 2.3.1 Ruby 2.7.1 旧環境 旧環境 旧環境 新環境 コード長 (Byte) 1420 2044 797 797 実行時間 (ms) 94 465 TLE 1567 メモリ (KB) 35328 108364 36988 47324 どのくらい速くなったか
ケース名 実行時間(旧) 実行時間(新) 新旧比 01.txt 7 59 02.txt 7 63 12.txt 1460 1089 1.34 13.txt 1844 1250 1.48 18.txt 1983 1289 1.54 19.txt 1949 1360 1.43 20.txt 11 59 21.txt 7 63 22.txt 22 70 23.txt 1449 981 1.48 24.txt 9 62 25.txt 10 63 大雑把に、
1.4倍速くなっている
ようです。
ruby
の時代が来ましたね。まとめ
- ABC 129 D が通った
- Ruby が速くなった
- コンパイル系にはかなわないけどね
参照したサイト
スクリプト言語などで競プロをすることについて
- 投稿日:2020-07-10T15:52:05+09:00
Javaのequalsで気をつけたい点
その1 nullチェック
public static void main(String[] args) { String str1 = null; String str2 = "test"; System.out.println(str1.equals(str2));//実行1 System.out.println(str2.equals(str1));//実行2 }実行1は、java.lang.NullPointerExceptionが発生します。
例外発生の原因は、str1にnullを入れてしまい、nullのオブジェクトからメソッドを呼び出してしまったことです。
nullでないstr2からメソッドを呼び出すと、例外が発生しなくなりfalseを返すようになります。実行2の書き方は、str1がnullであっても例外が発生しないというメリットがありますが、異常系としてnullが入ることを想定せずに実装していた場合、コードミスに気づきにくいという短所があると思います。
nullチェックを意識するのであれば、例外処理で捕まえる方が安全かもしれません。
public static void main(String[] args) { String str1 = null; String str2 = "test2"; try { System.out.println(str1.equals(str2)); }catch(NullPointerException ex) { System.out.println("例外キャッチ"); } }その2 コンスタントプール
public static void main(String[] args) { String str1 = "test1"; String str2 = "test1"; System.out.println(str1.equals(str2));//結果1 System.out.println(str1 == str2); //結果2 }結果1はもちろんtrueになりますが、結果2はどうなるでしょうか?
結論、結果2もtrueになります。これは、コンスタントプールという仕組みがあるためです。
文字リテラルは、プログラム中に頻繁に現れます。しかし、そのたびにStringのインスタンスを生成していては、メモリを大量に消費することになります。もし、同じ文字列リテラルが再度登場すれば、定数用のメモリ空間にある文字列インスタンスへの参照が「使いまわし」されます。これがコンスタントプールという仕組みです。
このコンスタントプールは、文字列リテラルを使ったときだけ有効です。
new演算子を使って明示的に新しいインスタンスをつくることを記述した場合には、その都度インスタンスがつくられ、それぞれの変数が異なる参照を持ちます。public static void main(String[] args) { String str1 = new String("test"); String str2 = "test"; System.out.println(str1.equals(str2));//true System.out.println(str1 == str2); //false }実際のプログラムではまずお目にかかりませんが、Java Silverとかでよく出てくるひっかけなので気をつけましょう。
その3 大文字小文字を区別しない判定
equalsIgnoreCaseメソッドを使うと、大文字小文字を区別せずに同値性を判定できます。
ただequalsに比べて出現頻度がかなり低いと思います。public static void main(String[] args) { String str1 = "abc"; String str2 = "ABC"; System.out.println(str1.equals(str2)); //false System.out.println(str1.equalsIgnoreCase(str2));//true }以上
- 投稿日:2020-07-10T15:27:51+09:00
MySQLのTableをJDBCでconnection.getMetaData().getColumns()したときのカラム名リスト
JDBCを使って、テーブルのスキーマ情報を取得する際の完全個人用備忘録。
index順になっています。
そのうち説明も追加します
カラム名 説明 値の例 TABLE_CAT データベース名 database_name TABLE_SCHEM null TABLE_NAME テーブル名 table_name COLUMN_NAME カラム名 id DATA_TYPE 4 TYPE_NAME INT UNSIGNED COLUMN_SIZE 10 BUFFER_LENGTH 65535 DECIMAL_DIGITS null NUM_PREC_RADIX 10 NULLABLE 0 REMARKS カラムについたコメント カラムコメント COLUMN_DEF null SQL_DATA_TYPE 0 SQL_DATETIME_SUB 0 CHAR_OCTET_LENGTH null ORDINAL_POSITION 1 IS_NULLABLE NO SCOPE_CATALOG null SCOPE_SCHEMA null SCOPE_TABLE null SOURCE_DATA_TYPE null IS_AUTOINCREMENT YES IS_GENERATEDCOLUMN NO
- 投稿日:2020-07-10T15:00:36+09:00
継承とかについて(JavaSilver)
JavaSilverの取得のためにJavaの基礎で理解が浅い部分をまとめてみています。
継承
継承の概念自体は理解しているつもりです。
継承というより機能を拡張すると表現したほうが個人的には理解しやすいかと思います。
スーパークラスの方が抽象度が高くて機能が少なくて、より具体的な機能が追加されてスーパークラス+α(拡張)になったのがサブクラスというイメージです。・スーパークラス
SuperA.javaclass SuperA {}・スーパークラスを継承したサブクラス
SubA.javaclass SubA extends SuperA {}できないこと
- extendsのあとに複数のスーパークラスは指定できない。
SubA.javaclass SubA extends SuperA, SuperB {}
スーパークラスで定義したprivateなメンバはサブクラスで使用できない。
スーパークラスで定義したメンバはサブクラスで使用できるがprivateなメンバは同一クラスのからしか使用できないため、サブクラスでも使用できない。
メソッドをオーバーライドする場合のアクセス修飾子はスーパークラスで指定している以上の公開範囲でないといけない。
例えばスーパークラスでpublicを指定しているメソッドをオーバーライドする場合はpublicを指定しないといけない。(公開範囲は狭められない。)
・アクセス修飾子の公開範囲
広い ─ ─ 狭い public protected デフォルト private どのクラスからでもアクセス可 サブクラス or 同一パッケージ 同一パッケージ 同一クラスのみ
- final修飾子のついているメンバはオーバーライドできない。
参照型の型変換
スーパークラスとサブクラスでの型変換については以下の2種類があります。
※ついでにインターフェースについても。。。
暗黙型変換
サブクラス→スーパークラス
実装クラス→インターフェースキャストによる型変換
サブクラス←スーパークラス
実装クラス←インターフェースつまり変換後に使用できる機能が減らなければ暗黙型変換ができるというイメージですかね?
その他
オーバーライド時に呼び出されるメンバ
サブクラスのオブジェクトをスーパークラス型の変数に代入した場合、インスタンスメソッド以外はスーパークラスのメンバが呼び出される。
- 投稿日:2020-07-10T14:09:02+09:00
【SpringBoot入門】フォームのバリデーションチェック
目的
Spring Quickstart Guideを取り組み終えた方、SpringBootを学び始めた方、復習をしたい方に向けて、
公式ガイドValidating Form Inputを実際に取り組み学んだことを共有します。
完成形はこちらになります。
不正な値が入力したままSubmitボタンが押されるとエラーメッセージを表示して、
有効な値が入力されている場合は、別の画面へと遷移出来るように実装していきます。
開発環境、これまでのおさらいは以下になります。
開発環境OS: macOS Mojave バージョン10.14.6 テキストエディタ: Visual Studio Code(以下VSCode) Java: 11.0.2QuickstartGuideのおさらいはこちらから
Building a RESTful Web Service編のおさらいはこちらから
Consuming a RESTful Web Service編のおさらいはこちらから
Accessing Data with JPA編のおさらいはこちらから
Handling Form Submission編のおさらいはこちらから1.SpringBoot projectを始めよう!
まずは、spring initializr にアクセスします。
1.ADD DEPENDENCIESボタンをクリックして、
Spring Web
とThymeleaf
を追加。
2.Artifact, Nameは、validating-form-input
に変更。
3.Javaを11
に変更。そして
GENERATE
ボタンをクリックしてZipファイルをダウンロードします。ダウンロードしたZipファイルを展開したら準備完了です。
2.コードを追加しよう!
先ほどのフォルダをVSCodeで開きます。
拡張機能のJava Extension Packのインストールの推奨します。と言われるのでインストールしておきましょう。PersonForm.javaを作成しよう!
src/main/java/com/example/validatingforminput/
にPersonForm.javaファイルを作成します。公式を参考にコードを追加します。
PersonForm.javapackage com.example.validatingforminput; import javax.validation.constraints.Min; import javax.validation.constraints.NotNull; import javax.validation.constraints.Size; public class PersonForm { @NotNull @Size(min=2, max=30) private String name; @NotNull @Min(18) private Integer age; public String getName() { return this.name; } public void setName(String name) { this.name = name; } public Integer getAge() { return age; } public void setAge(Integer age) { this.age = age; } public String toString() { return "Person(Name: " + this.name + ", Age: " + this.age + ")"; } }追加したコードを深掘りしていきます。
①
@NotNull
と@Size
PersonForm.java@NotNull @Size(min=2, max=30) private String name;String型のname変数を宣言時に2つバリデーションのアノテーションを付与しています。
@NotNull
は、nullを許可しません。あくまでnullは許可しないだけで、空文字や空白は許可されるので注意してください。
@Size
は、()
の中で指定したmin以上、max以下であるかを検証しています。
今回は、@Size(min=2, max=30)
ですので、2文字以上30文字以下ではないとエラーになります。②
@Min
PersonForm.java@NotNull @Min(18) private Integer age;Integer型のage変数を宣言時に2つバリデーションのアノテーションを付与しています。(1つは上述の
@NotNull
)
@Min
は、()
の中に記述した値より小さいかどうか検証しています。
今回は、@Min(18)
ですので、18未満はエラーになります。③ゲッター/セッター、toStringメソッド
PersonForm.javapublic String getName() { return this.name; } public void setName(String name) { this.name = name; } public Integer getAge() { return age; } public void setAge(Integer age) { this.age = age; } public String toString() { return "Person(Name: " + this.name + ", Age: " + this.age + ")"; }変数name、ageの値を取得、変更するためのゲッター/セッターメソッドを定義しています。
また、name、ageを文字列として表示するためのtoStringメソッドを定義しています。(今回使用していない気がしますが、デバッグ用なのかな?)pom.xmlにvalidationの依存関係を追加しよう!
公式ガイドには記載がなかったのですが、Spring Boot 2.3以上の時は、
PersonForm.java
のimport javax.validation.constraints.〇〇
がエラーになります。
(Spring Boot2.3 Relealse Notes)以下をdependenciesに追加してください。
pom.xml<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-validation</artifactId> </dependency>WebController.javaを作成しよう!
src/main/java/com/example/validatingforminput/
にWebController.javaファイルを作成します。公式を参考にコードを追加します。
WebController.javapackage com.example.validatingforminput; import javax.validation.Valid; import org.springframework.stereotype.Controller; import org.springframework.validation.BindingResult; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.servlet.config.annotation.ViewControllerRegistry; import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; @Controller public class WebController implements WebMvcConfigurer { @Override public void addViewControllers(ViewControllerRegistry registry) { registry.addViewController("/results").setViewName("results"); } @GetMapping("/") public String showForm(PersonForm personForm) { return "form"; } @PostMapping("/") public String checkPersonInfo(@Valid PersonForm personForm, BindingResult bindingResult) { if (bindingResult.hasErrors()) { return "form"; } return "redirect:/results"; } }追加したコードを深掘りしていきます。
①WebMvcConfigurerインターフェースとaddViewControllersメソッド
WebController.java@Controller public class WebController implements WebMvcConfigurer { @Override public void addViewControllers(ViewControllerRegistry registry) { registry.addViewController("/results").setViewName("results"); } // 以下略 }WebMvcConfigurerインターフェースを実装、addViewControllersメソッドをオーバーライドしています。
http://localhost:8080/results
というURLの時、results.htmlというテンプレートを参照するように設定していると解釈しています。
URLとテンプレートがマッピングされるようにしているみたいです。results.htmlの実装は後ほど。②showFormメソッド
WebController.java@GetMapping("/") public String showForm(PersonForm personForm) { return "form"; }
@GetMapping
は、http://localhost:8080/
でGETリクエストがあった時、showFormメソッドを呼ぶためのアノテーションです。
引数にPersonFormを受け取っています。メソッドの戻り値である、form.html
のform属性でPersonFormを関連づける事ができます。
form.htmlの実装は後ほど。③checkPersonInfoメソッド
WebController.java@PostMapping("/") public String checkPersonInfo(@Valid PersonForm personForm, BindingResult bindingResult) { if (bindingResult.hasErrors()) { return "form"; } return "redirect:/results"; }
@PostMapping
は、http://localhost:8080/
でPOSTリクエストがあった時、checkPersonInfoメソッドを呼ぶためのアノテーションです。
1つ目の引数@Valid PersonForm personForm
は入力されたデータの検証をしています。
2つ目の引数BindingResult bindingResult
は、入力されたデータと検証結果(エラーがあるかどうか)を保持するためのアノテーションです。
bindingResult.hasErrors()
が書かれているif文では、エラーがあるかどうかを確認しています。
もしエラーがある場合、form.htmlをエラーメッセージと入力されていた値と共に再描画します。エラーが無ければ、
http://localhost:8080/results
にリダイレクトされます。form.htmlを作成しよう!
src/main/resources/templates/
にform.htmlファイルを作成します。公式を参考にコードを追加します。
form.html<html xmlns:th="http://www.thymeleaf.org"> <body> <form action="#" th:action="@{/}" th:object="${personForm}" method="post"> <table> <tr> <td>Name:</td> <td><input type="text" th:field="*{name}" /></td> <td th:if="${#fields.hasErrors('name')}" th:errors="*{name}">Name Error</td> </tr> <tr> <td>Age:</td> <td><input type="text" th:field="*{age}" /></td> <td th:if="${#fields.hasErrors('age')}" th:errors="*{age}">Age Error</td> </tr> <tr> <td><button type="submit">Submit</button></td> </tr> </table> </form> </body> </html>nameとageを入力するフォーム画面ですね。
追加したコードのthymeleafの記述について深掘りしていきます。
thymeleafとは、springbootで扱う事が出来るテンプレートエンジンです。th:〇〇と記述します。
日本語で書かれたthymeleafチュートリアルもあります!th:action
formタグのaction属性の内容を置換しています。記述の仕方は、
th:action="@{}"
です。
method="post"となっているので、Submitボタンが押された時にWebControllerのcheckPersonInfoメソッドが呼ばれます。th:object
th:object
でオブジェクトを指定しています。これにより、オブジェクト内の変数の参照の仕方がpersonForm.nameではなく、*{name}のような記述方法が可能になります。th:field
th:objectで指定したオブジェクト内の変数を表示するために
th:field="*{変数名}"
と記述します。
今回は、PersonFormクラスの中にname、ageがあるので、th:field="*{name}"
、th:field="*{age}"
となります。
また、th:field="*{変数名}"
の中に記述した変数名がinputのid属性とname属性になります。th:if
th:if=条件
と記述します。trueであった場合、そのタグ、子要素が表示されます。
今回の条件は、エラーがあった場合trueになります。th:errors
th:errors="*{変数名}"
と記述します。エラーがあった場合のエラーメッセージが表示されます。
th:ifとth:errorsでエラーの有無を確認して、エラーメッセージ表示する領域を確保している感じですね。results.htmlを作成しよう!
src/main/resources/templates/
にresults.htmlファイルを作成します。公式を参考にコードを追加します。
results.html<html> <body> Congratulations! You are old enough to sign up for this site. </body> </html>form画面で不正な値が入力されることが無ければこちらの画面に遷移します。
3.実行してみよう!
アプリケーション実行の準備が出来たので確認しましょう。
ターミナルで以下のコマンドを入力してEnterしてください。
ターミナル$ ./mvnw spring-boot:runそして、
http://localhost:8080/
にアクセスすると以下のフォーム画面が表示されるはずです。(form.htmlが表示される)nameに1文字、ageに18未満の数値を入力して、Submitボタンを押すとエラーメッセージが表示されます。
ageに何も入力せずにSubmitボタンを押してもエラーメッセージが表示されます。
nameに2文字以上、ageを18以上の数値を入力して、Submitボタンを押すとresult画面(result.html)が表示されます。
お疲れ様でした!完成です!
参考サイト
Spring Bootで入力値の検証
ビュー名を返すだけのControllerなら、Controllerは別にいらないらしいよ!
検証とエラーメッセージ
- 投稿日:2020-07-10T14:03:13+09:00
Javaのラムダ式
ラムダ式とは
Java8で導入された記述方法です。
同じくJava8で導入されたStream API1はラムダ式を使うことが前提とされているため、ラムダ式を学んでおくとメリットがありそうです。また、ラムダ式を使うと、「匿名クラスを使った関数型インターフェースの記述」を簡潔にできるというメリットもあります。
ラムダ式の書き方
引数が複数ある時
インターフェース名 オブジェクト名 = (引数1, 引数2, ...) -> { return 処理内容 };コンパイラが型推論してくれるので、引数の型は指定する必要はありません。
引数が1つしかない時
インターフェース名 オブジェクト名 = 引数 -> 処理;引数が1つしかない時は、returnや引数を囲む()、処理を書く{}等は要りません。
ラムダ式を読む時はシンプルにこの形式を覚えておいて、書く時は引数の数によって記述を気をつければいいと思います。
匿名クラスを使った実装
匿名(無名)クラスとは、インターフェースを実装したローカルクラスの、宣言部分を省略したものです。
ラムダ式を使えば、匿名クラスを使わずに簡潔にわかりやすく書くことができます。たとえば以下のような匿名クラスがあったとします。
main.javainterface InterfaceTest{ // 抽象メソッド public String name(String name); } public class Main { public static void main(String[] args) { // 匿名クラスを使った書き方 InterfaceTest greeting = new InterfaceTest() { // オーバーライド public String name(String name) { return "Hello " + name; } }; System.out.println(greeting.name("momoji")); } }上記では、インターフェースのインスタンスを生成しているように見えますが、実際はインターフェースをもった、無名クラスのインスタンスを生成しています。
実行結果↓
Hello momojiラムダ式での実装
これをラムダ式で書き換えてみます。
main.javainterface InterfaceTest{ // 抽象メソッド public String name(String name); } public class Main { public static void main(String[] args) { // ラムダ式を使った書き方 InterfaceTest greeting = (name) -> { return "Hello " + name; }; System.out.println(greeting.name("momoji")); } }{}の中にはgreetingメソッドで行う処理内容が書かれています。
短くなった上、何をしているかが分かりやすくなりました。また、引数の型指定をしなくてよくなりました。実行結果↓
Hello momojiforEach文をラムダ式を使ってもっと短く書く
Javaにはfor-each文と同じ性質を持ったもので、拡張for文というものがあります。配列やリストの全ての要素に指定した処理を行うという性質を持ちます。
拡張for分の書き方
for (データ型 変数名: listや配列){ 処理; ... }リストの要素を全て順番に出力するには、拡張for文だと以下のように書きます。
main.javamain.java class Main { public static void main (String[] args) { List<String> list = new ArrayList<>(); list.add("a"); list.add("b"); list.add("c"); // 拡張for文 for(String l : list) { System.out.println(l); } } }上記の例では、
①String型のリスト「list」を定義する
②addメソッドでlistに"a","b","c"の要素をひとつずつ詰めていく
③拡張for文を使ってlistの要素をひとつずつ出力していく。ということをしています。
実行結果は以下のようになります。
a b cこれを、ラムダ式とforEach文を使うともっと短くすることができます。
main.javaclass Main { public static void main (String[] args) { List<String> list = new ArrayList<>(); list.add("a"); list.add("b"); list.add("c"); // 拡張for文 // for(String l : list) { // System.out.println(l); // } // ラムダ式を使ったforEach文 list.forEach(l -> System.out.println(l)); } }実行結果↓
a b c3行使って書いていたところを、1行に収めることができました。
これを「メソッド参照」を使うともっと短くすることができます。メソッド参照とは
これもJava8から導入された記法になります。
メソッド参照とは、メソッドの引数としてメソッドを参照できる仕組みのことです。
定義済みのメソッドを引数なしで呼び出すことができます。メソッド参照は以下のように書きます。
クラス名 :: メソッド名クラス名の後に「::」、呼び出したいメソッド名を書きます。メソッド名に()は不要です。
さっそくラムダ式と組み合わせてみます。
main.java// メソッド参照を使ったラムダ式 list.forEach(System.out::println);実行結果↓
a b cラムダ式とメソッド参照の合わせ技で、ずいぶん短く書けるようになりました。
配列やリストなどのコレクションを扱う為のもので、値の集計やデータを使った処理を分かりやすいコードで実装することができるAPI。 ↩
- 投稿日:2020-07-10T12:19:46+09:00
SpringBootでapplication.ymlやapplication-[プロファイル名].yml以外のファイル名のプロパティファイルを読みたい
なかなかコレ!というサンプルが見つからなかったのでメモ。
やりたかったこと
- SpringBoot に
application.yml
以外のファイル名の yaml のプロパティファイルを読み込ませる- その際、
application-test.yml
のようにプロファイル名をくっつけたファイルも読み込ませる- 読み込んだプロパティは Configuration クラスにマッピングする
マルチプロジェクトの構成で、共通の部品を集めたプロジェクト側にもプロパティファイルを置きたかったが、application.yml だと利用側のプロパティファイルとバッティングするため別名にする必要があった。
実現方法
ポイント
- Configuration クラスで
@PropertySource
を使用して読み込むプロパティファイルを指定する@PropertySource
は yaml ファイルの読み込みに対応していないので読み込み用のクラスを作成して読み込めるようにするソースコード
完全なソースコードはこちら
Configuration クラス
@PropertySource
のfactory
で Yaml 読み込み用のクラスを指定する。FooConfig.java@Configuration @ConfigurationProperties(prefix = "foo") @Component @PropertySource(value = {"classpath:/foo-config.yml", "classpath:/foo-config-${spring.profiles.active}.yml"}, factory = YamlPropertySourceFactory.class) @Data public class FooConfig { private BarConfig bar; private BazConfig baz; @Data public static class BarConfig { private String setting1; } @Data public static class BazConfig { private String setting1; private String setting2; } }Factory クラス
どこかから拝借したソース。
Spring のYamlPropertiesFactoryBean
を使用して読み込んだ Yaml をProperties
に変換し、PropertySource
にして返しているよう。YamlPropertySourceFactory.javapublic class YamlPropertySourceFactory implements PropertySourceFactory { @Override public PropertySource<?> createPropertySource(@Nullable String name, EncodedResource resource) throws IOException { Properties propertiesFromYaml = loadYamlIntoProperties(resource); String sourceName = name != null ? name : resource.getResource().getFilename(); return new PropertiesPropertySource(sourceName, propertiesFromYaml); } private static Properties loadYamlIntoProperties(EncodedResource resource) throws FileNotFoundException { try { YamlPropertiesFactoryBean factory = new YamlPropertiesFactoryBean(); factory.setResources(resource.getResource()); factory.afterPropertiesSet(); return factory.getObject(); } catch (IllegalStateException e) { Throwable cause = e.getCause(); if (cause instanceof FileNotFoundException) { throw (FileNotFoundException) e.getCause(); } throw e; } } }動作確認
SpringPropertySourceTest.java@SpringBootTest @ActiveProfiles("test") public class SpringPropertySourceTest { @Autowired FooConfig fooConfig; @Test public void test() { assertThat(fooConfig.getBar().getSetting1()).isEqualTo("barbar1"); assertThat(fooConfig.getBaz().getSetting2()).isEqualTo("bazbaz2"); } }おまけ
SpringBoot2.3.0 以降だと@PropertySource の
${spring.profiles.active}
を解決できずにエラーが出ます。別記事に書いておきました。(これのせいでめっちゃ時間食った・・・)SpringBoot2.3.0 以降で@PropertySource の value に使用しているプレースホルダの解決に失敗する
- 投稿日:2020-07-10T11:59:11+09:00
WindowsでmvnをするとNB: JAVA_HOME should point to a JDK not a JRE でエラーになる
私の原因
- 環境変数で
M2_HOME
,PATH
をユーザー環境変数で設定し、JAVA_HOME
はシステム環境変数で設定していた。- また、
NB: JAVA_HOME should point to a JDK not a JRE
のエラーは、パス先がJREだから起こるものではなく、JDKを認識できないときに一般的に表示されるものだった。解決方法
動作環境
- Winodws 10
- jdk-13.0.1
- Maven 3.6.3
経緯
- 普段はMacでAndroid開発をしているが、たまたまWindowsでSpring Bootを触ることになり、Java,MarvenをWindowsにインストールした。
- 環境変数で
M2_HOME
を設定し、Pathも設定した。mvn -v
で以下のエラーが起こり、Mavenが実行できない
The JAVA_HOME environment variable is not defined correctly This environment variable is needed to run this program NB: JAVA_HOME should point to a JDK not a JREPCにはすでに
JAVA_HOME
が設定されていて、Path
も設定済みだったのだが、、、参照
解決にあたって以下の記事のお世話になりました
- WindowsへのMavenインストール方法
- How do I fix maven error The JAVA_HOME environment variable is not defined correctly?
- MavenのJAVA_HOMEで躓いた時に見るメモ
- 投稿日:2020-07-10T10:52:58+09:00
SpringBoot2.3.0以降で@PropertySourceのvalueに使用しているプレースホルダの解決に失敗する
内容
タイトルそのままです。
SpringBoot2.3.0 以降で@PropertySource
の value の指定にclasspath:/something-config-${spring.profiles.active}.yml
のようにプレースホルダを使っていると、解決できずに例外が発生する。
現在の Boot の最新バージョンは 2.3.1 です。ソース
SomethingConfig.java@Configuration @ConfigurationProperties(prefix = "something") @Component @PropertySource(value = {"classpath:/something-config.yml","classpath:/something-config-${spring.profiles.active}.yml"}, factory = YamlPropertySourceFactory.class) @Data public class SomethingConfig { private String setting1; private String setting2; }結果
Caused by: java.lang.IllegalArgumentException: Could not resolve placeholder 'spring.profiles.active' in value "classpath:/somethign-config-${spring.profiles.active}.yml"解決方法
とりあえず 2.2 系を使いました。
issue
- 投稿日:2020-07-10T10:17:16+09:00
電卓プログラム
//キーボード入力インポート
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.math.BigDecimal;//クラス開始
public class Original {//メインメソッド開始************************** public static void main(String[] args) throws IOException { //キーボード入力準備 BufferedReader br = new BufferedReader (new InputStreamReader(System.in)); String now = "0"; String input = ""; String enzan = ""; String msg = "数値"; String data = ""; //無限ループ while(true){ try { //****************************************** System.out.println("\n"+"***************************"); System.out.println(" "); System.out.println("現在の値: " + now); System.out.println("演算子: " + enzan); System.out.println("入力された値: " + input); System.out.print(msg + "を入力してください>> "); //****************************************** //dataに入力値を代入する data = br.readLine(); //A-1 if(hantei(data)){ BigDecimal bd_data = new BigDecimal(data);//Decimalに変換 BigDecimal er = new BigDecimal(0); bd_data = bd_data.stripTrailingZeros();//ゼロ消す //B-1 if("数値".equals(msg)){ now = bd_data.toPlainString(); msg = "演算子"; //C-1 }else if("数値又は演算子".equals(msg)){ //D-1 if("/".equals(enzan) && bd_data.equals(er)){ System.out.println("0で割れません"); }else{ input = bd_data.toPlainString(); msg = "演算子"; } //C-1 }else{ System.out.println("エラー!演算子を入力してください"+ "\n"); } //A-2 }else{ if("+".equals(data) || "-".equals(data) || "*".equals(data) || "/".equals(data) || "=".equals(data)){ //msgが数値のとき if("数値".equals(msg)){ System.out.print("エラー!数値を入力してください"); //msgが数値又は演算子のとき }else if("数値又は演算子".equals(msg)){ //dataが=だったら if("=".equals(data)){ System.out.print("エラー!数値を入力してください"); }else{ enzan = data; msg = "数値又は演算子"; } //msgが演算子のとき }else{ //inputが空だったら if("".equals(input)){ if("=".equals(data)){ System.out.print("エラー!イコール以外を入力してください"); }else{ enzan = data; msg = "数値又は演算子"; } //inputに値が入っていたら }else{ BigDecimal bd1 = new BigDecimal(now); BigDecimal bd2 = new BigDecimal(input); //足し算 if("+".equals(enzan)){ BigDecimal ans1 = bd1.add(bd2); now = ans1.toPlainString(); input = ""; //引き算 }else if("-".equals(enzan)){ BigDecimal ans2 = bd1.subtract(bd2); now = ans2.toPlainString(); input = ""; //掛け算 }else if("*".equals(enzan)){ BigDecimal ans3 = bd1.multiply(bd2); now = ans3.toPlainString(); input = ""; //割り算(四捨五入・20桁まで表示) }else{ BigDecimal ans4 = bd1.divide(bd2,20,BigDecimal.ROUND_HALF_UP); ans4 = ans4.stripTrailingZeros(); now = ans4.toPlainString(); input = ""; } //dataが=だった場合 if("=".equals(data)){ enzan = ""; //=でない場合 }else{ enzan = data; msg = "数値又は演算子"; } }//inputに値が入っていたら }//msgが演算子のとき //A-3 }else if("c".equals(data)){ now = "0"; enzan = ""; input = ""; msg = "数値"; System.out.print("クリアしました"); //A-4 }else if("ce".equals(data)){ //B-3 if(input != ""){ input = ""; msg = "数値又は演算子"; //C-3 }else{ if(enzan != ""){ enzan = ""; msg = "演算子"; }else{ now = "0"; msg = "数値"; } } //A-5 }else if("e".equals(data)){ System.out.print("プログラムを終了します"); break; }else{ System.out.print("\n"+"エラー!対応していない入力値"); } }//A-2 }catch (Exception e) { // TODO 自動生成された catch ブロック System.out.print("予期せぬエラーの発生"); e.printStackTrace(); } }//無限ループ }//メソッド//判定メソッド**************************************
public static boolean hantei(String data){boolean brea = true; try{ Double.parseDouble(data); }catch (NumberFormatException e){ brea = false; } return brea; }//**************************************************
}
- 投稿日:2020-07-10T07:50:31+09:00
新興のSQLiteクライアント Reasonの開発をしています。
初めまして、hosokawatです。
SQLiteクライアント Reasonの開発をしています。ダウンロードはこちらから
次の投稿からデバッグ&宣伝を兼ねてReasonを使ったSQLite入門の記事を投稿していくことにしました。
その前置として、Reasonの紹介をします。Reasonとは?
SQLの作成、検証を効率よく行うことを目的としたSQLiteクライアントです。
ゴテゴテなGUIのツールを使うよりもエディタを使って「手で書く方が早いんだ!!」っていう人をターゲットにしています。百聞は一見に如かずということでスクリーンショットを見てみてください。
推しの機能
- クエリー自動生成機能
- テーブル名・カラム名・基本構文の自動補完
- 複数クエリー一括実行・結果の一括表示
- 多言語、Macos/Windows対応
- AceEditorの機能が使える
クエリー自動生成機能
テーブルを選択してのselect,insert,update,delete文作成と、
実行結果のセルから対象選択してのselect,update,deleteするためのクエリー作成機能があります。
テーブル名・カラム名・基本構文の自動補完
Reasonではテーブル名・カラム名・基本構文の自動補完をすることができます。
よくみてみてください。サブクエリーのカラム名も対応しています。複数SQL一括実行・結果の一括表示
Reasonでは複数のクエリーを一括で実行してまとめて結果を見ることができます。
多言語、Macos/Windows対応
日本語、英語で使うことができます。
そして、Macos/Windowsに対応しています。AceEditorの機能が使える
エディタのエンジンとしてAceEditorを組み込んでいます。
豊富な機能が搭載されていて、マルチカーソルや高度な検索を使うことができます。開発環境・言語・フレームワーク
Macbook pro 13インチでEclipseを使って、Java14とJavaFX14使って開発しています。
Java11以降の変化に対応したjavaFX開発のまとまったノウハウがなかなかネットにはなく苦労しました。
今回のプロダクトをリリースするにあたって全ての工程を経験できました。
いずれまとめることができたらなと思っています。開発期間
githubの草を確認してみたら3月の頭から開発をしていて124日開発しています!
いくつもの難題にぶち当たって刺激的な日々でした。
私の尊敬するエンジニアに言わせればこれは「楽しい困難!」です!まだまだ実装したい機能はたくさんあるので開発は続けていきます。
別のDBに対応するのか、さらに高度な機能を実装していくのか、は非常悩ましい選択肢です。
当初は4月5月には終わってると思っていたのですが、、しばらくのライフワークになると思います。まとめ
今回の記事を書きながらReasonを使ってなかなか良くできているなと自分のことながら思いました。
次回からReasonを使ったSQLite入門を始めます。ダウンロードはこちらから