LoginSignup
1
0

More than 3 years have passed since last update.

初めてのkotlinを触る - Sealed Classes(1)

Posted at

目的

概要

  • Sealed Classesの基本
  • Sealed Classesでの状態管理

内容

Sealed Classesの基礎

目的

They are, in a sense, an extension of enum classes: the set of values for an enum
type is also restricted, but each enum constant exists only as a single instance,
whereas a subclass of a sealed class can have multiple instances which can contain state.
  • Sealed Classesは、Enum Classesの拡張になる
  • Enum Classesがの値の集合で制限されていますが、各列挙型定数は単一のインスタンスとして存在する
  • Sealed Classesのサブクラスは、複数インスタンスであり、特定な状態を持っている
A sealed class is abstract by itself, it cannot be instantiated directly and can have abstract members.
  • Sealed Classesは抽象クラスであり、直接的にインスタンス化できなくて、抽象メンバーを持っている
  • Sealed Classesには、valueを含むことができる
To declare a sealed class, you put the sealed modifier before the name of the class. A sealed class can have subclasses, but all of them must be declared in the same file as the sealed class itself.
Note that classes which extend subclasses of a sealed class (indirect inheritors) can be placed anywhere, not necessarily in the same file.
  • Sealed Classesを宣言するには、クラスの名前の前に修飾子(sealed)を付ける。
  • Sealed Classesはサブクラスを持つことができるが、それらのすべては継承されるクラス自体と同じファイルで宣言されなければならない
  • Sealed Classesの継承したサブクラスを拡張するクラス(間接継承)は、同じファイル内ではなく、どこにでも配置できる

定義と利用

方法1

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

fun eval(expr: Expr): Double = when(expr) {
    is Const -> expr.number
    is Sum -> eval(expr.e1) + eval(expr.e2)
    NotANumber -> Double.NaN
}

fun main() {
    println(eval(Sum(Const(12.3), Const(12.4)))) // 24.700000000000003
}

方法2

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

fun eval(expr: Expr): Double = when(expr) {
    is Expr.Const -> expr.number
    is Expr.Sum -> eval(expr.e1) + eval(expr.e2)
    Expr.NotANumber -> Double.NaN
}

fun main() {
    println(eval(Expr.Sum(Expr.Const(12.3), Expr.Const(12.4))))
}
  • 表現できるものの範囲が広がる
  • enum定数に宣言後に値を持たせることはできない

メリット

  • 継承のことを制限できる
  • サブクラスの範囲を固定できる
fun eval(expr: Expr): Double = when(expr) {
    is Expr.Const -> expr.number
    is Expr.Sum -> eval(expr.e1) + eval(expr.e2)
}
  • whenに全てのケースを含んでいない場合は、エラーが出力される
'when' expression must be exhaustive, add necessary 'NotANumber' branch or 'else' branch instead

Sealed Classesでの状態管理



enum class State {
    IDLE, PAUSED, PLAYING
}

sealed class PlayerCmd {
    class Play(val url: String, val position: Long = 0): PlayerCmd()
    class Seek(val position: Long): PlayerCmd()
    object Pause: PlayerCmd()
    object Resume: PlayerCmd()
    object Stop: PlayerCmd()
}

class Player {
    private var state: State = State.IDLE

    private fun sendCmd(cmd: PlayerCmd) {
        when (cmd) {
            is PlayerCmd.Play -> {
                println("\nPlay ${cmd.url} from ${cmd.position}ms")
                state = State.PLAYING
                doPlay(cmd.url, cmd.position)
            }
            is PlayerCmd.Resume -> {
                println("\nResume. ")
                state = State.PLAYING
                doResume()
            }
            is PlayerCmd.Pause -> {
                println("\nPause. ")
                state = State.PAUSED
                doPause()
            }
            is PlayerCmd.Stop -> {
                println("\nStop.")
                state = State.IDLE
                doStop()
            }
            is PlayerCmd.Seek -> {
                println("\nSeek to ${cmd.position}ms, state: $state")
            }
        }
    }

    private fun doPlay(url: String, position: Long) {
        //todo
    }

    private fun doResume(){
        //todo
    }

    private fun doPause() {
        //todo
    }

    private fun doStop() {
        //todo
    }

    fun play(url: String, position: Long = 0) {
        sendCmd(PlayerCmd.Play(url, position))
    }

    fun resume() {
        sendCmd(PlayerCmd.Resume)
    }

    fun pause() {
        sendCmd(PlayerCmd.Pause)
    }

    fun stop() {
        sendCmd(PlayerCmd.Stop)
    }

    fun seekTo(position: Long) {
        sendCmd(PlayerCmd.Seek(position))
    }
}

fun main() {
    val player: Player = Player()
    player.play("http://ws.stream.qqmusic.qq.com/C2000012Ppbd3hjGOK.m4a") // Play http://ws.stream.qqmusic.qq.com/C2000012Ppbd3hjGOK.m4a from 0ms
    player.pause() // Pause.
    player.resume() // Resume.
    player.seekTo(30000) // Seek to 30000ms, state: PLAYING
    player.stop() // Stop.
}

javaで実現

まとめ

  • kotlinのSealed Classが簡単ではないか
  • 次はSealed Classで例外処理を紹介する
1
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
0