はじめに
Java8初期で知識が止まっている化石プログラマーが書籍『プロになるJava』を読んで、得た知見をメモ書き感覚であげていきます。
初歩的な内容も含まれてますが、温かい目で見守ってください。。
JShell
CUI上でJavaコードを実行できる。Java9より導入された。
参考:https://qiita.com/nowokay/items/e0b9c676567134e4a622
tabで補完、ドキュメント表示なども可能。
/vで型を確認できる
jshell> /v textBlock
| String textBlock = "改行あり\nの\n文字列です\n"
テキストブロック
Java15から。
"""
で囲った文字列は複数行の文字列として宣言できる。
jshell> var textBlock = """
...> 改行あり
...> の
...> 文字列です
...> """
textBlock ==> "改行あり\nの\n文字列です\n"
また、テキストブロック内ではエスケープ\
が不要になる。
jshell> """
...> テキストブロック内では"エスケープ"が不要
...> """
$16 ==> "テキストブロック内では\"エスケープ\"が不要\n"
BigDecimal
雑に言うと、10進数を扱うための型。
jshell> var ten = BigDecimal.valueOf(10)
ten ==> 10
jshell> var twenty = BigDecimal.valueOf(20)
twenty ==> 20
jshell> ten.subtract(twenty)
$34 ==> -10
# 足し算
jshell> ten.add(twenty)
$33 ==> 30
# 引き算
jshell> ten.subtract(twenty)
$37 ==> -10
# 掛け算
jshell> ten.multiply(twenty)
$35 ==> 200
# 割り算
jshell> ten.divide(twenty)
$36 ==> 0.5
0, 1, 10は定数が用意されている。
jshell> BigDecimal.ZERO
$40 ==> 0
jshell> BigDecimal.ONE
$38 ==> 1
jshell> BigDecimal.TEN
$39 ==> 10
BigDecimalが有用なケースの一つとして、小数計算がある。
有名な話だが、コンピュータは10進数の小数同士の計算が苦手。例えば0.1+0.2なんかは誤差が出てしまう。
jshell> 0.1+0.2
$24 ==> 0.30000000000000004
これは0.1も0.2も10進数→2進数に変換した際に循環小数となってしまうためだ。
参考:http://www.it-license.com/cardinal_number/DecimalToBinary.html
BIgDecimalではこの誤差問題を以下のように回避できる。
jshell> var b1 = new BigDecimal("0.1")
b1 ==> 0.1
jshell> var b2 = new BigDecimal("0.2")
b2 ==> 0.2
jshell> b1.add(b2)
$27 ==> 0.3
BigDecimal(String val)コンストラクタを用いて、"0.1"
"0.2"
を扱うことが可能になる。2進数がどうとかは関係なく、コンストラクタ引数に指定した文字列をそのまま数値として扱えるのがミソ。
なお、valueOfでもOK
jshell> var b3 = BigDecimal.valueOf(0.1)
b3 ==> 0.1
jshell> var b4 = BigDecimal.valueOf(0.2)
b4 ==> 0.2
jshell> b3.add(b4)
$45 ==> 0.3
switch
switch文の書き方がJava14から変更されている。
変更点は、それぞれのcaseにbreakが不要になったのと、ラムダっぽく->
を使える点である。
var num = 1;
// Java14〜
switch (num) {
case 0 -> System.out.println("ZERO");
case 1 -> System.out.println("ONE");
case 2 -> System.out.println("TWO");
default -> System.out.println("OTHER");
}
// 〜Java13
switch (num) {
case 0:
System.out.println("ZERO");
break;
case 1:
System.out.println("ONE");
break;
case 2:
System.out.println("TWO");
break;
default:
System.out.println("OTHER");
break;
}
switchに値を返させることも可能。
var num = 1;
var numStr = switch (num) {
case 0 -> "ZERO";
case 1 -> "ONE";
case 2 -> "TWO";
default -> "OTHER";
};
System.out.println(numStr); //ONE
The yield Keyword
record
Java16より、recordを使うことで型の違うデータをまとめて扱うことができる。
jshell> record Person(String name, int age) {}
...> var p1 = new Person("一郎", 10);
...>
| 次を作成しました: レコード Person
p1 ==> Person[name=一郎, age=10]
jshell> p1.name()
$5 ==> "一郎"
jshell> p1.age()
$6 ==> 10
jshell> p1.name().length()
$7 ==> 2
プロパティへのアクセスはname()
age()
で可能。
Listにもつめれるので、Streamで色々処理できそうな予感。
jshell> var p2 = new Person("二郎", 20);
...> var p3 = new Person("三郎", 30);
p2 ==> Person[name=二郎, age=20]
p3 ==> Person[name=三郎, age=30]
jshell> var persons = List.of(p1, p2, p3);
...>
persons ==> [Person[name=一郎, age=10], Person[name=二郎, age=20], Person[name=三郎, age=30]]
recordの中にメソッドを定義することも可能。
record Person(String name, int age) {
public String getUpper(){
return this.name.toUpperCase();
}
}
var p1 = new Person("ichiro", 10);
System.out.println(p1.getUpper()); //ICHIRO
もちろん外部クラスとして定義することも可能。
public record ManRecord(String name, int age) {
// メソッドなど・・・
}
DTOクラスとかは今後Recordに置き換わっていくのかな・・・?
と思ったけど、継承ができないので、基底クラスで共通化なんかしてると移行は難しそうな予感。
継承を使わないようなDTO設計が求められるのか?
参考:https://irof.hateblo.jp/entry/2021/09/23/235642
Java17~は既存のDTOクラスは「Recordに置換しますか?」とweak warningを出すらしく、やはりrecordへの移行を促していそう。
メソッド参照の使い所
メソッド参照自体はJava8からあるのですが、IDEに補完してもらわないとなかなか気づけないことが多いワタクシ。メソッド参照を使える条件を再確認すると、以下のような感じ。
引数 -> インスタンス.メソッド(引数)
↓
クラス::メソッド
受け取った引数をstaticメソッドに渡している
→引数を省略して、クラス名とメソッド名だけ書く
※staticメソッドの場合も大体同じ。
<例1>
x -> someInstance.doSomething(x)
↓
SomeInstance::doSomething
<例2>
s -> System.out.println(s)
↓
System.out::println
Pattern Matching of instanceof
Java 16
Sealed Classes
Java 17
参考