2020/4 最近のJava記法

佐々木誠
大阪

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

バージョン

主要なJavaのバージョンとリリース年です。

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());

今までは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が出るので引き続き知識のアップデートをしたいと思います。

こんな記事も読まれています