SHOEISHA iD

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

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

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

Java 12~15でリリースされた機能を確認しよう! 新しいクラス・構文・式でコードが書きやすく

Java 12からJava 15までの変更点を確認しよう 第1回

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

Pattern Matching for instanceof - instanceofを使ったif文がより簡素に記述可能

 instanceofを使ったインターフェースの実装クラスごとに処理を分ける場合、これまでであれば、リスト9のようなコードを記述していました。

[リスト9] これまでの記述(src/main/java/Jep375.javaからの抜粋)
//  これまでの記述方法
if( obj instanceof Sample){
  ((Sample) obj).exec();    // (1) Sampleクラスへのキャスト
}

 instanceofで判定した後、指定したクラスにキャストしてから、その型固有の操作を行うわけです((1))。しかし、Java 15からは、リスト10のような記述が可能になりました。

[リスト10] instanceofパターンマッチングを使った例(src/main/java/jp/enbind/jdk15/Jep375.javaからの抜粋)
//(1) キャストも同時に行う記述
if( obj instanceof Sample sample){
  sample.exec();
}

//  (2)キャスト後のメソッドもif文の中で利用可能
if( obj instanceof Sample sample && sample.isVisible()){
  sample.exec();
}

 (1)のように、同時にキャスト済みのインスタンス変数(ここではsample)を定義できます。また、(2)のようにそのインスタンスメソッド(ここではisVisible)を条件式の中で利用することも可能です。

Records - 参照用データ管理のクラス定義が簡単

 Javaでは、これまでデータの固まりを扱う際にも、通常のクラスとして定義する必要がありました(例えば、C言語での構造体を知っている方であれば、イメージがつきやすいと思います)。

 このRecordsという機能はまさに、特別なメソッドを持たないデータへのアクセスのみができる構造体を管理するような用途を意図したものです。例えば、よくあるケースで座標(x,y)などの値を返す場合に、リスト11のようなクラスを作成する必要がありました。

[リスト11] Recordクラスを使わない場合(src/main/java/jp/enbind/jdk15/jep384/Point.javaからの抜粋)
public class Point {

    private final int x,y;

    public Point(final int x, final int y){
        this.x = x;
        this.y = y;
    }
    //  値を取得するためのメソッド
    public int x(){
        return this.x;
    }
    public int y(){
        return this.y;
    }
    //  Objectクラスでのメソッド
    public boolean equals(Object o) {
        //(省略)
    }
    public int hashCode() {
        return Objects.hash(x, y);
    }
    public String toString() {
        return String.format("Point[x=%d, y=%d]", x, y);
    }
}

 上と同じ意図のコードを、Recordクラスを用いて書き換えたものがリスト12です。

[リスト12] 基本的なRecordクラスのコード例(src/main/java/jp/enbind/jdk15/Jep384.javaからの抜粋)
// (1) recordクラスの定義
public record Point(int x, int y){}

//  (2) 利用する場合
Point point = new Point(100,100);

 Recordクラスは、「class」ではなく「record」を使って定義します((1))。Recordクラスでは、x()、y()とequals()、hashCode()、toString()などは自動生成されるのですべて省略できます。ただし、途中で値の変更はできないので、セッターはありません。

 Recordクラスで保持すべき値はfinal値として扱われるため、利用する場合には(2)のようにコンストラクタに渡します。もしもコンストラクタで値のチェックを行いたい場合には、リスト13のようにも表せます。

[リスト13] 設定する値をコンストラクタでチェックする場合(src/main/java/jp/enbind/jdk15/Jep384.javaからの抜粋)
public record Point(int x, int y){
    public Point{
        if(x < 0 || y < 0){
            throw new IllegalArgumentException("argument error");
        }
    }
}

Sealed Class - 継承クラスの作成を限定する

 Javaでは、既定でクラスは自由に継承できます。継承してほしくない場合にはfinal指定をすれば継承できませんが、今度はまったく継承ができなくなってしまいます。そこで、開発者の意向で指定したクラスだけ継承させたい、という場合に利用できるのがSealedクラスです。

 例えば以下は、図2のようなルールでSealedクラスを定義した例です。

図2:Sealedクラスの構造
図2:Sealedクラスの構造
[リスト14]sealedクラスを継承したクラスの例(src/main/java/jp/enbind/jdk15/jep360/*.javaからの抜粋)
//(1)Sealedクラスを定義
public sealed class Shape
    permits Circle,Polygon {}
//  自由に継承して良い場合
public non-sealed class Circle extends Shape{}
//  さらに継承できるクラスを指定する
public sealed class Polygon extends Shape
        permits Square {}
//  継承できないクラスとする
public final class Square extends Polygon{}

//  実際に利用する場合 (Main.javaからの抜粋)
public void case1(Shape shape){
    if(shape instanceof Polygon polygon){
        //  (省略)
    }
    else if(shape instanceof Circle circle){
        //  (省略)
    }
    else{
        //  (2)sealedを使うことで、ここにはたどり着かないようにできる
    }
}

 Sealedクラスでは、sealedによって継承(実装)を限定したものであることを宣言し、permitsによって実装できるクラスを指定します((1))。この例であれば、Circle、PolygonだけがShapeクラスを継承できます。

 Sealedクラスを継承した場合、派生クラスの側でも以下のキーワードで継承の可否を宣言しなければなりません。

  • sealed(さらに継承可能)
  • non-sealed、final(継承不可)

 そして、実際に利用する場合、Shapeクラスを継承したクラスは、PolygonまたはPolygonの継承クラス、もしくはCircleクラスしかないため、(2)のブロックには絶対にたどり着かないことが保証できます。これらの機能はライブラリとして提供する場合などで非常に有用で、意図しない継承がなくなるのでライブラリの安全性も高まります。

Sealedインターフェース

 インターフェースでもsealedキーワードを使って、実装可能なクラスを制限できます。例えば、図3のようなルールでSealedクラスを定義したものが、リスト15です。

図3:Sealedクラスの基本ルール
図3:Sealedクラスの基本ルール
[リスト15] recordクラスとsealedクラスを使った例(src/main/java/jp/enbind/jdk15/Jep360.javaからの抜粋)
//(1) sealedインターフェースの作成
public sealed interface Shape permits Circle,Square {}
//(2)permitsで指定したクラスしか実装できない
public record Circle(int center,int radius) implements Shape{}
public record Square(int width,int height) implements Shape{}

 (1)では、sealed/permitsによる宣言は、クラスの場合と同じです。(2)ではRecordクラスでSealedインターフェースを実装しました。Recordクラスはfinalクラスの扱いなのでnon-sealed、finalなどの宣言は不要です。このようにsealedとrecordを使うことで列挙型(Enum)のような使い方もできるようになります。

まとめ

 今回は、Javaコードを記述する開発者が気になる変更点を中心に説明しました。Javaは他の言語に比べて冗長性が高いため、Koltinを好む開発者も増えてきていています。Java自体もこれらの影響を受け、まだまだ言語としても進化しています。そして、これまでよりも簡素にコードを記述できるようになっています。

 次回は、今回紹介できなかったJDKのツールや内部構造等に関連する変更点を中心に紹介します。

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

  • 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/12905 2020/09/28 11:00

おすすめ

アクセスランキング

アクセスランキング

イベント

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

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

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

メールバックナンバー

アクセスランキング

アクセスランキング