【Kotlin練習問題】シールドクラス(sealed class)

Kotlin

シールドクラスとは

シールドクラスとは継承をより制御するための制限がなされたクラスです。シールドクラスの全てのサブクラスはコンパイル時に認知されます。シールドクラスがコンパイルされるモジュールの後で別のサブクラスが発生することはありません。つまりシールドクラスが宣言されたファイル内でしかそのシールドクラスは継承できないということです。

例えばサードパーティのクライアントがあなたのシールドクラスを別のファイル上のコードで継承することはできません。したがって、シールドクラスのインスタンスはそのクラスのコンパイル時に存在していたものに限ります。ですので、エラー発生時にも確認する場所が限られているので、効率的です。

シールドクラスは簡単にいうとEnumクラスのようなものですが、若干違いがあります。それらがEnumと比べた際のメリットにもっているのでポイントを追って説明していきます。

ポイント1:クラスやオブジェクト宣言を内包できる

その違いの一つはシールドクラスはクラスやオブジェクト宣言をクラス内に内包できるという点です。
シールドクラス内にそのシールドクラスを継承するクラスを宣言することができるため、より細かい状態を表現することができます。

例えばテレビの状態を表すEnumクラスがあったとします。ONとOFFという列挙型を保持しており、テレビがついているか、ついていないかを表現することができます。

しかしついていた場合、映されているチャンネルまで表すことはできません。

シールドクラスではクラスを内包することができるので、”テレビの状態を表すシールドクラス”の中で、それぞれON/OFFの状態を表すクラスを作成しシールドクラスを継承させます。

ONの場合にはチャンネルを引数にとってインスタンスを作ることで、ON時のチャンネルまで表現することができるのです。

ちなみに同ファイル内であれば、シールドクラス内に内包しなくても、それを継承したサブクラスの作成は可能です。

ポイント2:インスタンスの状態を変更できる

ポイント1でもさらっと述べましたが、Enumと決定的に違うのはインスタンスを作れるという点です。

Enumはシングルトンなので、中身の列挙型を変更することはできません。しかしシールドクラスはインスタンスを作成して使用することができるため、各インスタンスの状態を後から変更することもできます。

上の例に続いて述べれば、テレビのチャンネルをあとから変更するメソッドを用意することで、インスタンス生成時とは別のチャンネルを設定することができるのです。

when式でelseが必要なくなる

シールドクラスの一番のメリットはwhen式を利用するときに現れます。はじめに述べたように、シールドクラスの中身はenum同様、宣言時に書いたものに限られているので、全ての可能性はシールドクラスの中身に限られています。従って、通常のwhen式のようにelse文を追加して分岐の中でカバーしきれていない可能性について考慮する必要がありません。シールドクラスを利用したwhen式ではelseを省くことができるのです。

シールドクラスの定義方法

シールドクラスを定義するにはsealed修飾子をクラス名の前につけるだけです。

sealed class Expr
data class Const(val number: Double) : Expr()
data class Sum(val e1: Expr, val e2: Expr) : Expr()
object NotANumber : Expr()

問題

前回のスマートキャストの記事で利用したコード(下に載せてあります)のインターフェースをシールドクラスで置き換えてください。そうすることでwhen式のelseが必要なくなります。

前回のコード:

fun eval(expr: Expr): Int =
        when (expr) {
            is Num -> TODO()
            is Sum -> TODO()
        }

interface Expr
class Num(val value: Int) : Expr
class Sum(val left: Expr, val right: Expr) : Expr

答え

fun eval(expr: Expr): Int =
        when (expr) {
            is Num -> expr.value
            is Sum -> eval(expr.left) + eval(expr.right)
        }

sealed class Expr
class Num(val value: Int) : Expr()
class Sum(val left: Expr, val right: Expr) : Expr()
[解説]

interface Exprをsealed classとして宣言することで、when式内のelseが無くてもコンパイルエラーが起こらなくなっています。

これはevalの引数として渡されているexprが確実にExprを継承しているNumかSumのインスタンスであることが保証されているためです。

答え

その他の問題はこちらからどうぞ。

おすすめ書籍

Kotlinの文法をまず学びたい!という方には以下の書籍がおすすめです。Kotlinは日本語書籍がまだ豊富とは言えない状況ですが、細かく解説されており、Kotlin入門者のかたでもつまずくことなく学習できると思います。

実際にアプリを作りながら覚えていきたい!という方には以下もお勧めです。はじめに上の書籍で文法をさらっと学んでから取り組むのがお勧めです。

プロフィール

プロフィール
コードラボJP

大学卒業後SEに就職、現在は退職しフリーランスとして活動中。
『初心者でも挫折せずに一人でプログラミングを学べる』をモットーに、コードラボJPを開設
お問い合わせ等はcodelabsjp@gmail.comまで

コードラボJPをフォローする
タイトルとURLをコピーしました