1. 序章

プログラミングでは、型定数を区別する必要が常にありました。 以前は、静的変数を宣言することでこれを行っていました。 そのような方法は間違っていませんが、これを達成するためのより良い方法があります。 Kotlin言語では、これを実現する方法は複数あります。

このチュートリアルでは、このようなユースケースで最も一般的に使用される2つのクラスタイプであるsealedクラスenumクラスを見ていきます。

2. 封印されたクラス

封印されたクラスは、コンパイル時にのみ宣言できるサブクラスを持つクラスの階層を提供します。

2.1. 宣言

さまざまなオペレーティングシステムを表す封印されたクラスを作成することから始めましょう。

sealed class OsSealed {
    object Linux : OsSealed()
    object Windows : OsSealed()
    object Mac : OsSealed()
}

オペレーティングシステムを所有する会社を表すコンストラクターパラメーターを追加することもできます。

sealed class OsSealed(val company: String) {
    object Linux : OsSealed("Open-Source")
    object Windows : OsSealed("Microsoft")
    object Mac : OsSealed("Apple")
}

注:封印されたクラスは、親クラスファイルの外部から拡張することはできません。

2.2. 使用法

封印されたクラスを使用するよく知られた方法の1つは、when式を使用することです。

when (osSealed) {
    OsSealed.Linux -> println("${osSealed.company} - Linux Operating System")
    OsSealed.Mac -> println("${osSealed.company} - Mac Operating System")
    OsSealed.Windows -> println("${osSealed.company} - Windows Operating System")
}

2.3. 機能

sealedクラス内で関数を宣言するのは非常に簡単です。 すべてのサブクラスで関数を使用する必要がある場合は、メインの親クラスで宣言できますが、異なる機能を持つ特定の関数を使用する場合は、任意のオブジェクトまたはサブクラスの下に関数を作成することで宣言できます。

sealed class OsSealed(val releaseYear: Int = 0, val company: String = "") {
    constructor(company: String) : this(0, company)

    object Linux : OsSealed("Open-Source") {
        fun getText(value: Int): String {
            return "Linux by $company - value=$value"
        }
    }

    object Windows : OsSealed("Microsoft") {
        fun getNumber(value: String): Int {
            return value.length
        }
    }

    object Mac : OsSealed(2001, "Apple") {
        fun doSomething(): String {
            val s = "Mac by $company - released at $releaseYear"
            println(s)
            return s
        }
    }

    object Unknown : OsSealed()

    fun getTextParent(): String {
        return "Called from parent sealed class"
    }
}

親クラス内で宣言された関数は、キャストせずに直接呼び出すことができます。 一方、子オブジェクトまたはサブクラス内で宣言された関数は、それらを同等の型に明示的にキャストして呼び出す必要があります。

when修飾子内の不要なボイラープレートコードを削除するには、is演算子を使用することをお勧めします。 そうすることで、値をキャストする必要がなくなります osSealed のパラメータ OsSealed.Linux オブジェクトまたは他の定義されたオブジェクトまたはサブクラス:

assertEquals("Called from parent sealed class", osSealed.getTextParent())
when (osSealed) {
    is OsSealed.Linux -> assertEquals("Linux by Open-Source - value=1", osSealed.getText(1))
    is OsSealed.Mac -> assertEquals("Mac by Apple - released at 2001", osSealed.doSomething())
    is OsSealed.Windows -> assertEquals(5, osSealed.getNumber("Text!"))
    else -> assertTrue(osSealed is OsSealed.Unknown)
}

3. 列挙型クラス

列挙型クラスを使用して、各列挙型定数をその親に関連付けます。

3.1. 宣言

ここでは、さまざまなオペレーティングシステムの列挙型を作成しました。ご覧のとおり、これは列挙型を宣言するJavaの方法と非常によく似ています。

enum class OsEnum {
    Linux,
    Windows,
    Mac
}

列挙型クラスにコンストラクターパラメーターを追加することもできます。

enum class OsEnum(val company: String) {
    Linux("Open-Source"),
    Windows("Microsoft"),
    Mac("Apple")
}

3.2。使用法

封印されたクラスと同様に、 when式で列挙型を使用できます。これは、渡された列挙型定数に応じて、さまざまなシナリオを処理するために主に使用されます。 :

when (osEnum) {
    OsEnum.Linux -> println("${osEnum.company} - Linux Operating System")
    OsEnum.Mac -> println("${osEnum.company} - Mac Operating System")
    OsEnum.Windows -> println("${osEnum.company} - Windows Operating System")
}

3.3。機能

列挙型クラスで関数を宣言する方法はいくつかあります。 関数を抽象関数として宣言して各列挙型定数でオーバーライドするか、親の列挙型クラスで宣言してその関数を任意の列挙型定数で使用できます

enum class OsEnum(val releaseYear: Int = 0, val company: String = "") {
    Linux(0, "Open-Source") {
        override fun getText(value: Int): String {
            return "Linux by $company - value=$value"
        }
    },
    Windows(0, "Microsoft") {
        override fun getText(value: Int): String {
            return "Windows by $company - value=$value"
        }
    },
    Mac(2001, "Apple") {
        override fun getText(value: Int): String {
            return "Mac by $company - released at $releaseYear"
        }
    },
    Unknown {
        override fun getText(value: Int): String {
            return ""
        }
    };

    abstract fun getText(value: Int): String

    fun getTextParent(): String {
        return "Called from parent enum class"
    }
}

abstract 関数を使用したため、関数名に違いはありません。 したがって、渡された osEnum パラメーターを使用して、実装された abstract 関数と、親で宣言された関数にアクセスするだけで済みます。

assertEquals("Called from parent enum class", osEnum.getTextParent())
when (osEnum) {
    OsEnum.Linux -> assertEquals("Linux by Open-Source - value=1", osEnum.getText(1))
    OsEnum.Windows -> assertEquals("Windows by Microsoft - value=2", osEnum.getText(2))
    OsEnum.Mac -> assertEquals("Mac by Apple - released at 2001", osEnum.getText(3))
    else -> assertTrue(osEnum == OsEnum.Unknown)
}

4. 封印されたクラスと列挙型

それぞれについて詳しく説明したので、今度はそれらの違いを見てみましょう。

enum は、主に相互に関連する定数として使用されます。 それらは、いくつかの親関数とペアにすることもできます。

s ealed classesはenumに似ていますが、より多くのカスタマイズが可能です。 説明したように、これらは列挙型抽象クラスの組み合わせです。

既知の値を持たない、または実装する必要のないサブクラスを追加するとします。

封印されたクラスでは、必要に応じて複数のカスタムコンストラクターを追加するだけです。さらに、異なる名前、パラメーター、および戻り値タイプで複数の関数を定義できます。

ただし、列挙型クラスでは、各列挙型定数に異なる関数を定義することはできません。したがって、 Unknown enum 定数でも、実装する必要がありました。その定数に必要のないメソッドであり、LinuxおよびWindows enumを初期化するときにinteger値0を渡す必要もありました。 ]定数。

5. ユースケース

列挙型封印されたクラスは、一見似ているように見えるかもしれません。 また、 enum は、 enumクラスでの関数の宣言が少し制限されているため不要に思えるかもしれませんが、まだユースケースがあります。

たとえば、いくつかの定数を保持し、それらをほとんどまたはまったく機能なしで接続する必要がある場合はどうなりますか? 推奨されるオプションは、列挙型を使用することです。これは、シンプルでわかりやすく、機能するためです。

一方、関連する定数が必要な場合、それぞれが異なるデータを保持し、異なるロジックを実装する場合、最適なオプションは、封印されたクラスを使用することです。

6. 結論

この記事では、封印されたクラス列挙型、およびそれらの使用方法について説明しました。 さらに、列挙型封印されたクラス、およびそれらの機能を初期化および宣言する複数の方法について説明しました。 また、列挙型を使用する場合と封印されたクラスを使用する場合についても説明しました。 最後に、各クラスに最適なユースケースについて説明しました。

いつものように、ソースコードはGitHubから入手できます。