SHOEISHA iD

※旧SEメンバーシップ会員の方は、同じ登録情報(メールアドレス&パスワード)でログインいただけます

CodeZine編集部では、現場で活躍するデベロッパーをスターにするためのカンファレンス「Developers Summit」や、エンジニアの生きざまをブーストするためのイベント「Developers Boost」など、さまざまなカンファレンスを企画・運営しています。

【最新Javaアップデート解説】変更点と過去バージョンからのおさらい

「Java21」で何ができる? 新機能とJava17以降の変更点を紹介

Java21での新しいAPI、変更点とJava17からのおさらい

  • X ポスト
  • このエントリーをはてなブックマークに追加

追加されたインターフェースとそのメソッド

 リスト1が追加された各インターフェースの概要コードです。主に、first/lastを名前の一部に持つメソッドが追加されています。各実装クラスにおいて同じことをしようとするとインデックス値を使って処理を行わなければならない場合がありましたが、今後はより直感的に操作できると思います。

 また、reversedメソッドが追加されたことで、順番を逆から参照することも容易になりました。ただし、reversedメソッドで得られる新たなコレクションは、元のコレクションインスタンスの参照となります。保持しているデータ自体は同じなので、通常の場合、変更や更新などを行うとそれらも反映されます。その点には注意してください(ただし、定義しているのはインターフェースであるため、すべての実装において保証されているものではありません)。

[リスト1]追加されたインターフェース
public interface SequencedCollection<E> extends Collection<E> {
    SequencedCollection<E> reversed();
    void addFirst(E e);
    void addLast(E e);
    E getFirst();
    E getLast();
    E removeFirst();
    E removeLast();
}
public interface SequencedSet<E> extends SequencedCollection<E>, Set<E> {
    SequencedSet<E> reversed();
}
public interface SequencedMap<K, V> extends Map<K, V> {
    SequencedMap<K, V> reversed();
    Map.Entry<K,V> firstEntry();
    Map.Entry<K,V> lastEntry();
    Map.Entry<K,V> pollFirstEntry();
    Map.Entry<K,V> pollLastEntry();
    V putFirst(K k, V v);
    V putLast(K k, V v);
    SequencedSet<K> sequencedKeySet();
    SequencedCollection<V> sequencedValues();
    SequencedSet<Map.Entry<K, V>> sequencedEntrySet();
}

レコードパターン(JEP440)とSwitchでのパターンマッチ(JEP441)

 Switchでのパターンマッチはこちらでも紹介しているJava17のPreview版からも続く改善です。それらが、レコードパターンとも関連して使えるようになり、正式版としてリリースされました。この変更ではリスト2のような書き方が可能になっています。

[リスト2
 レコードパターンの利用例(src/main/java/jp/enbind/jep440/Main.javaの抜粋)]
record Point(int x,int y){};

void sample1(){
    var p1 = new Point(100,120);
    // (1) Java16まで
    if(p1 instanceof Point p){
        System.out.println("x:" + p.x + ",y:" + p.y);
    }
    // (2) Java21から
    if(p1 instanceof Point(int x,int y)){
        System.out.println("x:" + x + ",y:" + y);
    }
}

 (1)の書き方はJava16から可能でした。そして、Java21からは(2)のようにrecord定義のなかの変数にまで直接アクセスできる宣言が可能になっています。また、record定義の変数自体が他のrecord定義であるようなネストされたrecord定義であってもリスト3のようにアクセス可能です。

[リスト3]ネストされたレコードパターンの利用例(src/main/java/jp/enbind/jep440/Main.javaの抜粋)
record Line(Point start,Point end){};
void sample2(){
    var p1 = new Point(0,0);
    var p2 = new Point(100,100);
    var line = new Line(p1,p2);
    // (1) ネストされた変数にもアクセス可能
    if(line instanceof Line(Point(int x,int y),Point p)){
        System.out.println("line x1:" + x + ",y1:" + y + "->" + "x2:" + p.x + ",y2:" + p.y);
    }
    // (2) varで置き換えることもできる
    if(line instanceof Line(var v1,var v2)){
        System.out.println("line x1:" + v1.x + ",y1:" + v1.y + "->" + "x2:" + v2.x + ",y2:" + v2.y);
    }
}

 ネストされたrecord定義は(1)のように記述することができます。そして、あえてネストさせない記述も混ぜることができるので、その後に利用する実装に応じて使い分けることが可能です。

 このようにインスタンスを展開していく必要がなくなるので、よりコードが読みやすくなると思います。ただし、あまりにも深いネスト表現を使うと逆に可視性が下がるので、ご注意ください。

 また、(2)のようにvar記述を使って型推論を利用した宣言も可能です。ただし、使わない変数についての定義をしなければならないことに多少の面倒さがまだ残ります。ここの改善は、無名パターンと無名変数(JEP443)を利用して可能です。これについては、改めて後述します。

 そして、このレコードパターンはSwitchでも利用できます。

[リスト4]Switchパターンでの利用例(src/main/java/jp/enbind/jep441/Main.javaの抜粋)
void sample1Switch(Object obj){
    switch (obj){
        case Point(int x,int y) -> {
            // (処理)
        }
        case Line(Point(int x,int y),Point e) -> {
            // (処理)
        }
        default -> {
            // (処理)
        }
    }
}

 このようにrecord定義とswitchを組み合わせて使うことでさまざまなパターン一致の表現がしやすくなりました。

文字列テンプレート(JEP430)-Preview

 文字列内に変数の値を挿入したいケースは多々あります。ここまでの例でもリスト5のように文字列と変数を連結して使っていました。

[リスト5]Javaでの変数の値を使った文字列の作成例
System.out.println("line x1:" + x + ",y1:" + y + "->" + "x2:" + p.x + ",y2:" + p.y);

 このような記述方法は直感的に読みにくいと思います。これまでもFormatterクラスを使うことで解決することはできました。しかし、他の言語であればもっと分かりやすい表現ができるようになっています。例えば、JavaScriptではリスト6のように書き換えることができます。

[リスト6]JavaScriptでの書換例
const str = `line x1:${x},y1:${y} -> x2:${p.x},y2:${p.y}`;

 このような書き方ができるようにするための変更が、この文字列テンプレートです。 

[リスト7]Javaでの変数の値を使った文字列の作成例(src/main/java/jp/enbind/jep430/Main.javaの抜粋)
import static java.lang.StringTemplate.STR;
// : (省略)
int x = 0;
int y = 100;
// (1) 基本的な例
String line = STR."x is \{x}, y is \{y}";
// x is 0, y is 100
var map = new HashMap<String,Integer>();
map.put("grape",5);
// (2) 応用例
String data = STR."""
    りんご : \{map.get("apple").intValue()}
    ばなな : \{map.get("banana").intValue()}
    いちご : \{map.containsKey("strawberry")?  map.get("strawberry").intValue() : "-"} // (3)
    """;
// りんご : 3
// ばなな : 6
// いちご : -

 ここで注目すべきはSTRです。この部分が文字列の変換を行う実装になっています。そして、(1)のようにバックスラッシュ(\)と{}を使って変数の値を差し込みます。また、(2)のように変数のインスタンスやメソッドも利用可能であり、(3)のようにすればnullの場合にも対応できます。

 そして、STRの部分の実装は変更が可能です。例えば、java.util.Formatterと同じような記述ができるテンプレート実装もあり、その利用例がリスト8です。

[リスト8]FMTテンプレート形式を使った文字列の作成例(src/main/java/jp/enbind/jep430/Main.javaの抜粋)
import static java.util.FormatProcessor.FMT;
// (省略)
var map = new HashMap<String,Double>();
String data = FMT."""
    -- 名称 -- : - 単価 -
    りんご     : %7.2f\{map.get("apple").doubleValue()}
    ばなな     : %7.2f\{map.get("banana").doubleValue()}
    いちご     : %7.2f{map.containsKey("strawberry")?  map.get("strawberry").doubleValue() : 0.0} // (1)
    """";
// -- 名称 -- : - 単価 -
// りんご     :  180.50
// ばなな     :  198.20
// いちご     :    0.00

 このフォーマットではバックスラッシュの前にString.formatやjava.util.Formatterで使えるフォーマット指定を行います。STRと同じようにnullの場合のような記述も可能ですが、その値に対するフォーマット処理がされるので(1)のようにフォーマットで対応できる型にしないといけません。

 また、このテンプレート形式は自分で自作することも可能です。そのため、例えば、SQLを構築するためのテンプレートやHTMLを構築するためのテンプレートを作ることも可能です。自作テンプレートの実装例はサンプルコード(src/main/java/jp/enbind/jep430/Main.java)にあるので、詳しく知りたい方はそちらを参照してください。

次のページ
無名パターンと無名変数(JEP443)-Preview

この記事は参考になりましたか?

  • X ポスト
  • このエントリーをはてなブックマークに追加
【最新Javaアップデート解説】変更点と過去バージョンからのおさらい連載記事一覧

もっと読む

この記事の著者

WINGSプロジェクト 小林 昌弘(コバヤシ マサヒロ)

WINGSプロジェクトについて>有限会社 WINGSプロジェクトが運営する、テクニカル執筆コミュニティ(代表 山田祥寛...

※プロフィールは、執筆時点、または直近の記事の寄稿時点での内容です

山田 祥寛(ヤマダ ヨシヒロ)

静岡県榛原町生まれ。一橋大学経済学部卒業後、NECにてシステム企画業務に携わるが、2003年4月に念願かなってフリーライターに転身。Microsoft MVP for Visual Studio and Development Technologies。執筆コミュニティ「WINGSプロジェクト」代表。主な著書に「独習シリーズ(Java・C#・Python・PHP・Ruby・JSP&サーブレットなど)」「速習シリーズ(ASP.NET Core・Vue.js・React・TypeScript・ECMAScript、Laravelなど)」「改訂3版JavaScript本格入門」「これからはじめるReact実践入門」「はじめてのAndroidアプリ開発 Kotlin編 」他、著書多数

※プロフィールは、執筆時点、または直近の記事の寄稿時点での内容です

この記事は参考になりましたか?

この記事をシェア

  • X ポスト
  • このエントリーをはてなブックマークに追加
CodeZine(コードジン)
https://codezine.jp/article/detail/18303 2023/09/21 11:00

おすすめ

アクセスランキング

アクセスランキング

イベント

CodeZine編集部では、現場で活躍するデベロッパーをスターにするためのカンファレンス「Developers Summit」や、エンジニアの生きざまをブーストするためのイベント「Developers Boost」など、さまざまなカンファレンスを企画・運営しています。

新規会員登録無料のご案内

  • ・全ての過去記事が閲覧できます
  • ・会員限定メルマガを受信できます

メールバックナンバー

アクセスランキング

アクセスランキング