Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Java 8 から 21 へのバージョンアップでどう変わる?:実アプリケーションで学ぶ実装のポイント / 20231111 Key Implementation Points from Java 8 to 21

mackey0225
November 11, 2023

Java 8 から 21 へのバージョンアップでどう変わる?:実アプリケーションで学ぶ実装のポイント / 20231111 Key Implementation Points from Java 8 to 21

2023年 11月 11日 に開かれた JJUG CCC 2023 Fall での発表資料です。

コーディング・実装に焦点において、
Java 8 から 21 でどのような変更があるか、また、どの様に取り入れるかについてまとめております。

発表時間枠(20分)の都合上、ポイントを絞った資料になっております。

mackey0225

November 11, 2023
Tweet

More Decks by mackey0225

Other Decks in Programming

Transcript

  1. Java 8 から 21 へのバージョンアップでどう変わる?
    実アプリケーションで学ぶ実装のポイント
    2023-11-11 JJUG CCC Fall 2023
    BABY JOB 株式会社 浅野 正貴(@mackey0225)

    View full-size slide

  2. Java 8 から 21 へのバージョンアップでどう変わる?
    目次
    ❏ 自己紹介
    ❏ 前提
    ❏ 取り上げる機能について
    ❏ 各機能について実装(Before / After)
    ❏ まとめ
    2

    View full-size slide

  3. Java 8 から 21 へのバージョンアップでどう変わる?
    自己紹介
    名前:浅野 正貴
    所属:BABY JOB 株式会社(2022-06 入社)
    役割:Javaエンジニア
    X: @mackey0225 / GitHub: @mackey0225
    3

    View full-size slide

  4. 本題の前に

    View full-size slide

  5. Java 8 から 21 へのバージョンアップでどう変わる? 5
    前提
    ● プロダクト
    ○ Java 8 (Amazon Corretto) + Spring Boot 2.7
    ■ Corretto は当初 Elastic Beanstalk を使っていた名残
    ● 背景
    ○ Java 8 のサポート期間は無問題󰢏
    ○ Spring Boot 2.7 の LTS の期限が間近
    ■ Spring Boot 3系だと、Java 8 では対象外󰢃
    ■ こっちがモチベーション🔥

    View full-size slide

  6. Java 8 から 21 へのバージョンアップでどう変わる? 6
    今日話すこと
    ● Java 8 → 21 で実装がどの様に変わるのか
    ○ 焦点は 実装 に関する部分
    ■ 可読性や保守性、DDDの観点で良さそうなもの
    ■ JEP でいうと specification / language を中心
    ■ Garbage Collection や Virtual Thread などは対象外
    ● 目新しいことはあまりないかも。。。
    ○ 「何番煎じだろうが私はまだ煎じていない!」
    ○ とはいえ、何か持って帰ってもらえるようにしているつもり󰢛

    View full-size slide

  7. Java 8 から 21 へのバージョンアップでどう変わる?
    今日話すこと
    ● 取り上げる機能
    ○ Optional
    ○ Switch 式
    ○ Records
    本当は String Template とかも取り上げたかったけど、
    時間的に限界でした。。。󰢛
    7

    View full-size slide

  8. ここから、本題

    View full-size slide

  9. Java 8 から 21 へのバージョンアップでどう変わる?
    Optional
    9
    【概要】
    ● Optional 自体は Java 8 で追加
    ● Java 9, 10, 11 にてメソッドが追加
    ○ Java 9
    ■ ifPresentOrElse / or / stream
    ○ Java 10
    ■ orElseThrow
    ○ Java 11
    ■ isEmpty

    View full-size slide

  10. Java 8 から 21 へのバージョンアップでどう変わる?
    Optional - ifPresentOrElse【Before】
    10
    var op = repository.findById(id);
    if (op.isPresent()) {
    anotherRepository.save(AnotherEntity.createOnPresent(op.get()));
    } else {
    anotherRepository.save(AnotherEntity.createOnAbsence());
    }
    処理イメージとしては、
    リポジトリから Optional で返ってきたときに処理が分かれる場合:
    - エンティティが取得できる
    - そのエンティティをベースに別エンティティを生成し、永続化
    - エンティティが取得できない
    - 別のメソッドで別エンティティを生成し、永続化

    View full-size slide

  11. Java 8 から 21 へのバージョンアップでどう変わる?
    Optional - ifPresentOrElse【After】
    11
    var op = repository.findById(id);
    op.ifPresentOrElse(
    e -> anotherRepository.save(AnotherEntity.createOnPresent(e)),
    () -> anotherRepository.save(AnotherEntity.createOnAbsence())
    );
    if文がなくなりスッキリとしつつも、処理分岐感が薄れるので、
    チームや組織の方針に合わせて使い分け・統一するのが良い

    View full-size slide

  12. Java 8 から 21 へのバージョンアップでどう変わる?
    Switch 式
    12
    【概要】
    ● Java 12,13 にて Preview
    ○ JEP 325: Switch Expressions (Preview)
    ○ JEP 354: Switch Expressions (Second Preview)
    ● Java 14 にて正式追加
    ○ JEP 361: Switch Expressions

    View full-size slide

  13. Java 8 から 21 へのバージョンアップでどう変わる?
    Switch 式
    13
    【簡単な機能・良いとこ】
    ● 「文」だけではなく「式」として利用可能
    ○ つまり、値を返す
    ● Arrow label でスッキリした見た目にできる
    ○ 詳細は後述のコードで説明
    ● 網羅性チェックもある
    ○ 保守開発時の考慮漏れも防げる

    View full-size slide

  14. Java 8 から 21 へのバージョンアップでどう変わる? 14
    Destination destination;
    switch (destinationType) {
    case NURSERY:
    case CORPORATION:
    destination = new Destination(nurseryAddress);
    break;
    case ANOTHER:
    destination = new Destination(anotherAddress);
    break;
    default:
    throw new UnsupportedOperationException("未サポートの届け先種類 ")
    }
    Switch 式【Before】
    Enum(destinationType)の値によって、destination に代入する値を変える。

    View full-size slide

  15. Java 8 から 21 へのバージョンアップでどう変わる? 15
    Destination destination;
    switch (destinationType) {
    case NURSERY:
    case CORPORATION:
    destination = new Destination(nurseryAddress);
    break;
    case ANOTHER:
    destination = new Destination(anotherAddress);
    break;
    default:
    throw new UnsupportedOperationException("未サポートの届け先種類 ")
    }
    Switch 式【Before】
    󰢃 switch 内で代入するために、直前で宣言している

    View full-size slide

  16. Java 8 から 21 へのバージョンアップでどう変わる? 16
    Destination destination;
    switch (destinationType) {
    case NURSERY:
    case CORPORATION:
    destination = new Destination(nurseryAddress);
    break;
    case ANOTHER:
    destination = new Destination(anotherAddress);
    break;
    default:
    throw new UnsupportedOperationException("未サポートの届け先種類 ")
    }
    Switch 式【Before】
    󰢃 条件ごとに case の行を定義しないといけない

    View full-size slide

  17. Java 8 から 21 へのバージョンアップでどう変わる? 17
    Destination destination;
    switch (destinationType) {
    case NURSERY:
    case CORPORATION:
    destination = new Destination(nurseryAddress);
    break;
    case ANOTHER:
    destination = new Destination(anotherAddress);
    break;
    default:
    throw new UnsupportedOperationException("未サポートの届け先種類 ")
    }
    Switch 式【Before】
    󰢃 case ごとに break 文の記載

    View full-size slide

  18. Java 8 から 21 へのバージョンアップでどう変わる? 18
    Destination destination = switch (destinationType) {
    case NURSERY, CORPORATION -> new Destination(nurseryAddress);
    case ANOTHER -> new Destination(anotherAddress);
    default -> throw new UnsupportedOperationException("未サポートの届け先種類 ");
    };
    Switch 式【After】

    View full-size slide

  19. Java 8 から 21 へのバージョンアップでどう変わる? 19
    Destination destination = switch (destinationType) {
    case NURSERY, CORPORATION -> new Destination(nurseryAddress);
    case ANOTHER -> new Destination(anotherAddress);
    default -> throw new UnsupportedOperationException("未サポートの届け先種類 ");
    };
    Switch 式【After】
    󰢏 宣言時に値を設定できるので、
    場合によっては final 付加してイミュータブルにできる

    View full-size slide

  20. Java 8 から 21 へのバージョンアップでどう変わる? 20
    Destination destination = switch (destinationType) {
    case NURSERY, CORPORATION -> new Destination(nurseryAddress);
    case ANOTHER -> new Destination(anotherAddress);
    default -> throw new UnsupportedOperationException("未サポートの届け先種類 ");
    };
    Switch 式【After】
    󰢏 case も1行にまとめられる

    View full-size slide

  21. Java 8 から 21 へのバージョンアップでどう変わる? 21
    Destination destination = switch (destinationType) {
    case NURSERY, CORPORATION -> new Destination(nurseryAddress);
    case ANOTHER -> new Destination(anotherAddress);
    default -> throw new UnsupportedOperationException("未サポートの届け先種類 ");
    };
    Switch 式【After】
    󰢏 冗長な break 文が排除できた

    View full-size slide

  22. Java 8 から 21 へのバージョンアップでどう変わる? 22
    Destination destination = switch (destinationType) {
    case NURSERY, CORPORATION -> new Destination(nurseryAddress);
    case ANOTHER -> new Destination(anotherAddress);
    default -> throw new UnsupportedOperationException("未サポートの届け先種類 ");
    };
    Switch 式【After】
    参考までに、Arrow labels ではなく、yield も登場
    cf.https://openjdk.org/jeps/354#Yielding-a-value
    (適材適所が前提ですが、個人的には、Arrow labels 使いがちです)

    View full-size slide

  23. Java 8 から 21 へのバージョンアップでどう変わる? 23
    ・・・おや!? switch のようすが・・・!

    View full-size slide

  24. Java 8 から 21 へのバージョンアップでどう変わる?
    Pattern Matching for switch
    24
    【概要】
    ● Java 17〜20 にて Preview
    ○ JEP 406: Pattern Matching for switch (Preview)
    ○ JEP 420: Pattern Matching for switch (Second Preview)
    ○ JEP 427: Pattern Matching for switch (Third Preview)
    ○ JEP 433: Pattern Matching for switch (Fourth Preview)
    ● Java 21 にて正式追加
    ○ JEP 441: Pattern Matching for switch
    何回、Bボタン 押されてんねん!

    View full-size slide

  25. Java 8 から 21 へのバージョンアップでどう変わる? 25
    Pattern Matching for switch【Before】
    if (item.getType() == null) {
    return item.getName();
    }
    return switch (item.getType()) {
    case DIAPER, BABY_WIPES -> ....
    case APRON -> ....
    default -> throw new UnsupportedOperationException("未サポートの商品種別 ")
    };

    View full-size slide

  26. Java 8 から 21 へのバージョンアップでどう変わる? 26
    if (item.getType() == null) {
    return item.getName();
    }
    return switch (item.getType()) {
    case DIAPER, BABY_WIPES -> ....
    case APRON -> ....
    default -> throw new UnsupportedOperationException("未サポートの商品種別 ")
    };
    Pattern Matching for switch【Before】
    󰢃 NPE を回避するために、直前でチェックしている

    View full-size slide

  27. Java 8 から 21 へのバージョンアップでどう変わる? 27
    Pattern Matching for switch【After】
    return switch (item.getType()) {
    case null -> item.getName()
    case DIAPER, BABY_WIPES -> ....
    case APRON -> ....
    default -> throw new UnsupportedOperationException("未サポートの商品種別 ")
    };

    View full-size slide

  28. Java 8 から 21 へのバージョンアップでどう変わる? 28
    return switch (item.getType()) {
    case null -> item.getName()
    case DIAPER, BABY_WIPES -> ....
    case APRON -> ....
    default -> throw new UnsupportedOperationException("未サポートの商品種別 ")
    };
    Pattern Matching for switch【After】
    󰢏 NULL もラベルとして使えるので、
    NPE を回避するための処理が不要。

    View full-size slide

  29. Java 8 から 21 へのバージョンアップでどう変わる? 29
    Pattern Matching for switch【After】
    ただし、この例だと、列挙値で NULL が来るかもという設計がそもそも怪しいかも。。。🤨
    return switch (item.getType()) {
    case null -> item.getName()
    case DIAPER, BABY_WIPES -> ....
    case APRON -> ....
    default -> throw new UnsupportedOperationException("未サポートの商品種別 ")
    };
    使えるから飛びつくのではなく、別の箇所や設計を見直すことは常日頃から重要!

    View full-size slide

  30. Java 8 から 21 へのバージョンアップでどう変わる?
    Records
    30
    【概要】
    ● Java 14,15 にて Preview
    ○ JEP 359: Records (Preview)
    ○ JEP 384: Records (Second Preview)
    ● Java 16 にて正式追加
    ○ JEP 395: Records

    View full-size slide

  31. Java 8 から 21 へのバージョンアップでどう変わる?
    Records
    31
    【簡単な機能・良いとこ】
    ● 手軽にイミュータブルなクラスが作成できる
    ○ 記述量が少なく利用できる
    ○ コンストラクタや equals などの定義が不要
    ■ 独自の仕様・実装をオーバーライドも可能
    ○ フィールドは常に private final が付与

    View full-size slide

  32. Java 8 から 21 へのバージョンアップでどう変わる?
    Records - イメージ
    32
    class Point {
    private final int x;
    private final int y;
    Point(int x, int y) {
    this.x = x;
    this.y = y;
    }
    int x() { return x; }
    int y() { return y; }
    public boolean equals(Object o) {
    if (!(o instanceof Point)) return false;
    Point other = (Point) o;
    return other.x == x && other.y == y;
    }
    public int hashCode() {
    return Objects.hash(x, y);
    }
    public String toString() {
    return String.format("Point[x=%d, y=%d]", x, y);
    }
    }
    record Point(int x, int y) { }
    少ない記述量でイミュータブルなクラスがで
    きる。
    ※参照のアクセサメソッドは"get"始まりで
    はない

    View full-size slide

  33. Java 8 から 21 へのバージョンアップでどう変わる?
    Records - Value Object、DTO
    33
    イミュータブルである性質を使って、DDDの文脈で
    ● Value Object
    ● DTO
    を定義するのに活かせる。
    ※図表は、松岡幸一郎(@little_hand_s).ドメイン駆動設計 モデリング/実装ガ
    イド.p.79 図 7.1 より
    https://booth.pm/ja/items/1835632

    View full-size slide

  34. Java 8 から 21 へのバージョンアップでどう変わる? 34
    Records - 例:分数(from JEP 395)
    record Rational(int num, int denom) {
    public Rational {
    int gcd = gcd(num, denom);
    num /= gcd;
    denom /= gcd;
    }
    private int gcd(int i, int j) { 最大公約数を求める }
    ... 続く

    View full-size slide

  35. Java 8 から 21 へのバージョンアップでどう変わる? 35
    record Rational(int num, int denom) {
    public Rational {
    int gcd = gcd(num, denom);
    num /= gcd;
    denom /= gcd;
    }
    private int gcd(int i, int j) { 最大公約数を求める }
    ... 続く
    Records - 例:分数(from JEP 395)
    コンストラクタを上書き。
    入力値をそのままではなく、通分して生成する。
    これで、分数としてのルール(=ドメイン知識)を保てる。

    View full-size slide

  36. Java 8 から 21 へのバージョンアップでどう変わる? 36
    お分かり頂けただろうか。。。

    View full-size slide

  37. Java 8 から 21 へのバージョンアップでどう変わる? 37
    record Rational(int num, int denom) {
    public Rational {
    int gcd = gcd(num, denom);
    num /= gcd;
    denom /= gcd;
    }
    private int gcd(int i, int j) { 最大公約数を求める }
    ... 続く
    Records - 例:分数(from JEP 395)
    // 普通のコンストラクタ
    public Rational(int num, int denom) {
    int gcd = gcd(num, denom);
    num /= gcd;
    denom /= gcd;
    this.num = num;
    this.denom = denom;
    }
    󰢏 コンストラクタも簡略化できる!
    ただし、レコードのヘッダーと一致する場合に限る。

    View full-size slide

  38. Java 8 から 21 へのバージョンアップでどう変わる? 38
    ... 続き
    public Rational add(Rational r) {
    return new Rational(
    this.num * r.denom + r.num * this.denom,
    this.denom * r.denom
    );
    }
    public Rational multiply(Rational r) {
    return new Rational(
    this.num * r.num,
    this.denom * r.denom
    );
    }
    Records - 例:分数(from JEP 395)

    View full-size slide

  39. Java 8 から 21 へのバージョンアップでどう変わる? 39
    ... 続き
    public Rational add(Rational r) {
    return new Rational(
    this.num * r.denom + r.num * this.denom,
    this.denom * r.denom
    );
    }
    public Rational multiply(Rational r) {
    return new Rational(
    this.num * r.num,
    this.denom * r.denom
    );
    }
    足し算の際に新しいインスタンスを生成する。
    分数のルールを呼び出し側に漏れ出さないように定義できる。
    ドメインルールの流出を防げる
    Records - 例:分数(from JEP 395)

    View full-size slide

  40. さいごに

    View full-size slide

  41. Java 8 から 21 へのバージョンアップでどう変わる?
    まとめ
    41
    ● 進化を取り入れて、サービス利用者の方々に還元できるかも。。。?
    ○ 冗長なコードや記述量が減ったり、
    ○ 可読性が良くなったり、
    ○ 保守性が高くなったり、
    ● とはいえ、
    ○ サービス開発においては組織での意思決定・取捨選択は必要
    ○ コーディングルールの策定やバージョンアップ戦略も必要
    ● なので、日頃からの情報のアップデートは必要!

    View full-size slide

  42. ご清聴いただきありがとうございました。

    View full-size slide

  43. Java 8 から 21 へのバージョンアップでどう変わる?
    補足 - IntelliJ IDEA の コードインスペクション機能
    43
    インスペクション内に「Java 言語レベルの移行支援」がある
    プロジェクト内で適用できそうな箇所をサジェストしてくれる

    View full-size slide