2020/4 最近のJava記法
気づけば自分のJava知識が6ぐらいで止まっているので、知識のアップデートがてら最近のJava記法を紹介します。

バージョン
主要なJavaのバージョンとリリース年です。
- Java6: 2006年
 - Java8: 2014年
 - Java11: 2018年
 - Java17: 2021年
 
Java8, 11が採用される時代であることがわかると思います。
それではJava8, 11で追加された記法・構文についていくつか紹介します。
Java8
ラムダ式
ラムダ式は通常の関数と違い、関数内で変数のように関数処理を定義できます。
public static void main(String[] args){
    // 引数、戻り値なし
    Runnable hello = () -> { System.out.println("Hello!! こんにちわ!"); };
    hello.run(); // Hello!! こんにちわ!
    // Function<T, R> Tが引数、Rが戻り値
    Function<Integer, String> sharp = (i) -> { return "#"+ i; };
    String s = sharp.apply(99); // #99
    // Consumer<T> 引数あり、戻り値なし
    Consumer<Integer> at = (i) -> { System.out.println("@"+ i); };
    at.accept(77); // @77
}
柔軟で便利ですが、まとまった処理なら関数化するなど使い分けが大事そうです。
List/Map forEach
List, MapにforEach()メソッドが追加されました。
// List
for(String name: list){
    System.out.println(name);
}
// Map
for(Map.Entry<String, Integer> entry : items.entrySet()){
    System.out.println(entry.getValue());
}
↑従来、↓forEach()を使った場合。
// List
list.forEach((name) -> {
    System.out.println(name);
});
// Map
items.forEach((key, value)->{
    System.out.println(value);
})
何をループさせているのか、わかりやすくなりましたね。
ラムダ式の注意点になるのです。ラムダ式の外側の変数を参照できますが、変更はできないようです。
String appendix = "様";
list.forEach(name -> {
  System.out.println(name + appendix); // 参照可能
  // appendix = "ちゃん";  // 変更はNG
});
Optional
Optionalは、nullとなるかもしれないケースで役立つクラスです。
public hoge(){
    Integer value = this.count("foo");
    // nullチェックする
    if(value == null){
        value = -1;
    }
    System.out.println(value);
}
private Integer count(string key){
    return map.get(key);
}
↑従来、↓Optionalを使った場合。
public hoge(){
    Optional<Integer> optionalValue = this.count("foo");
    // nullなら-1
    Integer value = optionalValue.orElse(-1);
    System.out.println(value);
}
// 戻り値をOptionalで包む
private Optional<Integer> count(string key){
    return Optional.ofNullable(map.get(key));
}
闇雲に使うと混乱しそうですが、適切に使うと可読性の高いコードになりそうです。
StreamAPI
StreamAPIはループ処理をわかりやすくするもので、データを加工するのに便利です。
List<String> list1 = Arrays.asList("TANAKA", "YAMADA", "SATO");
// SATOを取り除く
List<String> list2 = new ArrayList<>();
for(String name: list1){
    if(name.equals("SATO")){
        continue;
    }
    list2.add(name);
}
// 挨拶文を作る
List<String> list3 = new ArrayList<>();
for(String name: list2){
    String message = "こんにちは、" + name + "さん。"
    list3.add(message);
}
↑従来、↓StreamAPIを使った場合。
List<String> list = Arrays.asList("TANAKA", "YAMADA", "SATO");
List<String> messages = list.stream()
                            .filter(name -> !name.equals("SATO"))
                            .map(name -> "こんにちは、" + name + "さん。")
                            .collect(Collectors.toList());
stream()でListをStream化filter()でSATOを取り除くmap()で挨拶文を作るcollect()でListに戻す
今まではfor文一つでしていたことが、何のループ処理なのか明示的になりわかりやすくなりました。
Java11
var
ローカル変数の型推論をしてくれます。
Hoge hoge = new Hoge();
List<User> users = List.of(new User("jon"), new User("ben"));
↑従来、↓varを使った場合。
var hoge = new Hoge();
var users = List.of(new User("jon"), new User("ben"));
変数の型を省略できるので可読性が上がります。
try-with-resource
冗長だったリソースのclose()処理を省略することができる構文です。
BufferedReader reader = null;
String line = null;
try{
    reader = new BufferedReader(new FileReader("test.txt"));
    line = br.readLine();
}catch(FileNotFoundException e){
    e.printStackTrace();
}catch(IOException e){
    e.printStackTrace();
}finally{
    if(reader != null){
        try{
            reader.close();
        }catch(IOException e){
            e.printStackTrace();
        }
    }
}
↑従来、↓try-with-resourceの使った場合。
var reader = new BufferedReader(new FileReader("test.txt"));
try(reader){
    line = br.readLine();
}catch(FileNotFoundException e){
    e.printStackTrace();
}catch(IOException e){
    e.printStackTrace();
}
try(reader)とリソースを指定することで、try終了時にclose()を自動でやってくれるので便利ですね。
List/Map of
immutable、つまり変更や追加ができないListやMapを簡単に生成できます。
var os = List.of("win", "mac", "linux");
var conties = Map.of(
  "jp", "日本",
  "us", "アメリカ");
後から自由に変更されたくないモードや定義値として活用すると、改修に強いコードになりそうです。
色々紹介しましたが、個人的にはStreamAPIを使いこなせるようになってみたいと思いました。 また、来年の2021年には、Java17が出るので引き続き知識のアップデートをしたいと思います。

