汎用
nullチェック
Objects.nonNull(obj);
StringUtils.isEmpty(str);
Listの空チェック
以下の2つはlist自体がnullであった場合にぬるぽとなる。
list.size() == 0;
list.isEmpty();
org.springframework.util.CollectionUtilsのisEmptyを使用すれば、list自体がnullの場合も、booleanで値をしっかり返却してくれる。
CollectionUtils.isEmpty(list);
型チェック
.getClass().getTypeName()
文字列の比較
StringUtils.equals(str1, str2) //左側にnullにならない値を設定する。importはlang3
intをStringに変換
Integer.toString(intValue);
StringをList<String>に変換
String str = "sample";
Arrays.asList(str);
構文基礎
forEach
list.forEach(s-> System.out.println(s))
Mapの場合
cookieParamMap.forEach((key, value)
System.out.println("key: + key + ", value: + value)
);
SonarQubeでコード解析をしている場合、ラムダ式を使用すると、SonarQube上で以下のようなバグとして検知される場合がある。よほど可読性やプログラミング効率に対する貢献がなければ、ラムダ式は使用しなくて良い。
A "NullPointerException" could be thrown; "get()" can return null.
Result of 'get()' is dereferenced.
Switch, Case文
var signal // signalには、blue, yellow, redのいずれかが入る
switch (signal) {
case "blue":
console.log("青です");
break;
case "yellow":
console.log("黄です");
break;
default:
console.log("赤です");
}
三項演算子
簡単なif文を1行で記述する。
構文
条件式 ? trueの場合の処理 : falseの場合の処理
例
int win = "777";
String result = win == "777" ? "当たり" : "はずれ";
system.out.println(result); // "当たり"
3点リーダ
以下のように型+3点リーダの形式で、引数が与えられることがある。
void fund(String... args) {
}
これは、可変長引数と呼ばれるもので、引数の数が不定のメソッドを定義したい場合に使用する。
void countMemberFunc(String className, String… members) {
system.out.println(className + count(members) + "名");
}
countMemberFunc("3年A組", "田中", "佐藤"); // 3年A組2名
countMemberFunc("3年B組", "鈴木", "山田", "小林"); // 3年A組3名
String… は、String[] args と同義。固定長配列のため、引数として渡された時点で長さが確定し、値の追加はできない
コロンが2つつくやつ
String::trim
メソッド参照というもの。ラムダ式で使用できる記法。例えば、以下の文字列を区切りで取り出すために、;のあとの空白を取り除きたい場合
var value = "paramA; paramB"
List<String> list = Arrays.asList(value.split(";")).stream()
.map(s->s.trim())
これを、Stringのtrimメソッドを通す、という意味で、メソッド参照の記法が使える。
List<String> list = Arrays.asList(value.split(";")).stream()
.map(String::trim)
可読性が低いので、おすすめはしない。
Appendix
乱数生成
UUIDで作成する
UUID.randomUUID().toString();
単純な数値で作成する場合
// 5桁の乱数を生成する例
Random random = new Random();
int num = 10000 + random.nextInt(90000);
SonarQubeなどでソースコードチェックを行っている場合、Randomは信頼されていないクラスとして検知される。 ダミーやスタブ実装時であれば問題ないが、本番で使用する場合はUUIDを使用する。
もしくはSucureRandomを使用する?
上位桁の0埋め
SecureRandom random = SecureRandom.getInstance("SHA1PRNG"); // SHA1PRNGはSHA1を使用したPRNG(乱数生成アルゴリズム)
random.nextInt(10000); // 10000以下の乱数を生成
int randomNumStr = String.format("%04d", randomNum); // 上位桁0埋め
ランダムなバイト値(1~9とa~f を使用した文字列)で作成
乱数生成の理想は、1~9とランダムなアルファベットを想像すると思うが、コンピューターの世界では2進数や16進数といったフォーマットしか無いため、a~zという範囲指定はアナログで実装する必要がある。
そのため、SecureRandomというライブラリを使用して実装できるこの方法が好まれる。
byte[] randomByte = new byte[length]; // length分のbyteを生成
SecureRandom random= SecureRandom.getInstance("SHA1PRNG"); // SecureRandom を使用するためのインスタンスを生成 (おまじないでOK
random.nextBytes(randomByte); // randomByteに、 長さが指定したlengthの、ランダムなbyte配列を格納…①
String randomStr = HexencodeHexString(randomByte); // byteを16進数で取得…②
_randomStr = randomStr.substring(randomStr.length() / 2); //…③
- ①1バイトは8ビットで、 10進数では-128~128の値
- ②1バイトは、16進数では0~ffで表現される ※上記では0埋めされ、 00~ffで出力される
- ③各バイトが00~ffに変換され、 桁数がlengthの2倍になるため、半分削除
値を指定桁でマスキング
認証情報や個人情報など、 マスキングしてログ出力したい。
以下は末尾4桁を残してxでマスキングする例。Stringのメソッド substring() でマスキング桁数を削除し、 apache.commons.lang3.StringUtilsのleftPad() で、削除した分『x』で桁埋めする。
leftPad()の第2引数は、xの数ではなく、桁埋めした後の桁数 (xの桁数+マスキングしない文字列の桁数)、つまりもともとの文字列の桁数を指定する点に注意する。
int replaceLength = personalInfo.length() - 4; // マスキングする桁数
if (replaceLength > 0) {
String replaceStr StringUtils.leftPad(personalInfo.substring(replaceLength), personalInfo.length(), "x");
}
ラムダ式を元に戻す
ラムダ式はデバッグできない。
なので、もとの式に戻したいことがあるが、元に戻すドキュメントはあまりないのでメモ。
ちなみに、{}をつけるだけ。
// 分解前
http.authorizeHttpRequests(auth ->
auth.antMatchers("/asset").anyRequest().authenticated()
);
// 分解後
http.authorizeHttpRequests(auth -> {
// ここに式を書けるようになる
auth.antMatchers("/asset").anyRequest().authenticated();
});
オブジェクトのコピー
Javaは基本的に参照渡しであるため、ただ変数に格納しなおしただけではオブジェクトは複製されない。
Object obj = new Object(); Object copyObj = Object(); // copyObjとobjは参照関係であり、片方を修正すればどちらも修正されてしまう
ディープコピー
オブジェクトをまるまる複製したい場合は、org.springframework.utilのSerializationUtils.clone()メソッドを使用する。
copiedHeader = SerializationUtils.clone(header);
比較的処理が重たいので、用途に応じて後述のシャローコピーも検討する。
apacheCommonsにも同名のパッケージ、メソッドが存在するので注意。特にこの2つのパッケージは第一引数と第二引数の仕様が真逆。
第一引数 : コピー元、第二引数: コピー先、となっているSpringの方がわかりやすい。
シャローコピー
コピー先とコピー元の型が異なる場合は、org.springframework.beans/BeanUtilsのcopyPropertiesBeanUtilsメソッドを使用する。
BeanUtils.copyProperties(コピー先OBJ, コピー元OBJ);
プロパティは、参照渡しでコピーされる。
複製というか、マッピングというような意味合いが強い。
MyBatisなどで作られるEntityを、DTOとして作り直す際などに便利。
リクエストレスポンスをJSON形式で出力
APIのログやデバッグでオブジェクトの中身が見たいときに、オブジェクトをJSON形式で出力したい。
com.fasterxml.jackson.databind.ObjectMapperのwriteValueAsString()メソッドを使用する。
sytem.out.println(new ObjectMapper().writeValueAsString(targetObj));
JSON形式の文字列からパラメータを取り出す
String jsonObj = "{\"title\": \"sample\"}"
try {
JsonNode rootNode = objectMapper.readTree(jsonObj.getResponseBodyAsString());
JsonNode nodeTitle = rootNode.path("title");
String title = nodeTitle.asText();
System.out.println(title); // sample
} catch (JsonProcessingException el) {
// TODO 自動生成された catch ブロック
throw e1;
}
URLからクエリパラメータを取得する
文字列としてURLを扱う際に、そのURLに含まれるパラメータを取得する方法。
String urlString = "https://abc.jp?param1=a¶m2=b";
URI uri = URI.create(urlString);
UriComponents locationUriComponents = UriComponentsBuilder.fromUri(uri).build();
MultiValueMap<String, String> queryParams = locationUriComponents.getQueryParams();
queryParams.getFirst("paramKeyName");
バッファーとバイナリ
バッファーとは、値を格納するための固定長のメモリストレージ。
変数と何が違うのかというと、変数は明確な型のある値を格納するもの。
一方のバッファーはバイナリ、つまり、コンピューターしか読めない生のデータを格納する入れ物として使用される。
これを知っていると、ストリームなどを扱う際の理解が早い。
JSONファイルからDTOを作成する
- APIのDTOを記載したJSONファイルを用意する(ここではinput.jsonとする)
- ファイル名はinput.jsonとする
- 以下のコマンドを実行
- Output.javaとして、配下のモデルとともに出力される
quicktype input.json --no-maps --no-date-times --acronym-style camel --array-type list -o output.java --just-types
インシデント
@Valueが動作しない
以下のように、@Valueフィールドをコンストラクタで読み込むと、値が設定されていない。
public class SampleService {
@Value("{app.key}")
private String appKey;
public SampleService(String appKey) {
this.appKey = appKey;
}
public sampleMethod() {
system.out.println(this.appKey); // null
}
}
Javaアプリケーションが起動すると、コンポーネントスキャンが行われ、@Component系のクラスがDIされると、続いて以下の順で後続のDIが行われる。
- コンストラクタインジェクション
- フィールドインジェクション
- セッターインジェクション
先ほどのソースコードを見ると、フィールド定義の@Valueの方が先に読み込まれそうだが、実際にはコンストラクタが先なので、値が設定されない。
この場合は、コンストラクタで@Valueを設定する。
public class SampleService {
private String appKey;
public SampleService(@Value("{app.key}") String appKey) { // コンストラクタで@Valueする
this.appKey = appKey;
}
public sampleMethod() {
system.out.println(this.appKey); // 取得できる
}
}
staticフィールドとして@Valueを定義できる?
実装する場合は、@Componentを付与して、読み込まれるタイミングを前倒しする。
物理的には可能だが、本来@Valueはインスタンスフィールドに対し使用するものであり、推奨はされない。
依存関係が特殊になるので、テスト等でも問題が起こる可能性がある。
@Component
public class SampleConst {
public static String APPKEY;
public SampleConst(@Value("{app.key}") String appKey) {
APPKEY = appKey;
}
}
コメント