Kotlin のシールドクラス
このチュートリアルでは、Kotlinの封印クラスについて見ていきます。それらは何であり、どのように使用されるのでしょうか?以下でこれらのすべてのことに取り組みます。
Kotlinのシールドクラス
Kotlinの密封クラスの実装
Kotlinでのシールドクラスの実装方法は次のようになります。
sealed class A{
class B : A()
class C : A()
}
封印されたクラスを特定するためには、修飾子sealedを追加する必要があります。封印されたクラスはインスタンス化できませんので、暗黙的に抽象です。以下の方法は動作しません。
fun main(args: Array<String>)
{
var a = A() //compiler error. Class A cannot be instantiated.
}
デフォルトでは、シールドクラスのコンストラクターは非公開です。シールドクラスのすべてのサブクラスは、同じファイル内で宣言する必要があります。コンパイル時にのみ型のセットを制限することで、シールドクラスは型の安全性を確保する上で重要です。
sealed class A{
class B : A()
{
class E : A() //this works.
}
class C : A()
init {
println("sealed class A")
}
}
class D : A() //this works
{
class F: A() //This won't work. Since sealed class is defined in another scope.
}
コンストラクタを持つシールドクラスを作成する。
sealed class A(var name: String){
class B : A("B")
class C : A("C")
}
class D : A("D")
fun main(args: Array<String>) {
var b = A.B()
var d = D()
}
シールされたクラスにデータクラスとオブジェクトを追加しています。
fun main(args: Array<String>) {
val e = A.E("Anupam")
println(e) //prints E(name=Anupam)
var d = A.D
d.name() //prints Object D
}
sealed class A{
class B : A()
class C : A()
object D : A()
{
fun name()
{
println("Object D")
}
}
data class E(var name: String) : A()
}
列挙型とシールドクラスの違い
Kotlinでは、シールドクラスはステロイド入りの列挙クラスと言えます。シールドクラスを使用すると、異なる型のインスタンスを作成できますが、列挙型はすべての列挙定数に同じ型を使用する制約があります。このようなことは列挙クラスでは不可能です。
enum class Months(string: String){
January("Jan"), February(2),
}
Enumクラスでは、すべての定数に対して単一の型のみが許可されます。ここで、シールドクラスが登場し、複数のインスタンスを許可します。
sealed class Months {
class January(var shortHand: String) : Months()
class February(var number: Int) : Months()
class March(var shortHand: String, var number: Int) : Months()
}
シールドクラスのこの機能をプロジェクトでどのように使用できますか?ニュースフィードのようなアプリケーションでは、以下に示すように、ステータス、画像、ビデオ投稿の3つの異なるクラスタイプを作成することができます。
sealed class Post
{
class Status(var text: String) : Post()
class Image(var url: String, var caption: String) : Post()
class Video(var url: String, var timeDuration: Int, var encoding: String): Post()
}
これはEnumクラスでは不可能です。
封印されたクラスとwhen文
以下の例は、sealedクラスはよくwhen文と一緒に使われます。各サブクラスとその型がケースとして機能するためです。さらに、sealedクラスは型を制限することがわかっています。したがって、when文のelse部分は簡単に削除できます。次の例は、同じことを示しています。
sealed class Shape{
class Circle(var radius: Float): Shape()
class Square(var length: Int): Shape()
class Rectangle(var length: Int, var breadth: Int): Shape()
}
fun eval(e: Shape) =
when (e) {
is Shape.Circle -> println("Circle area is ${3.14*e.radius*e.radius}")
is Shape.Square -> println("Square area is ${e.length*e.length}")
is Shape.Rectangle -> println("Rectagle area is ${e.length*e.breadth}")
}
以下のように、私たちのメイン関数内でeval関数を実行しましょう。
fun main(args: Array<String>) {
var circle = Shape.Circle(4.5f)
var square = Shape.Square(4)
var rectangle = Shape.Rectangle(4,5)
eval(circle)
eval(square)
eval(rectangle)
//eval(x) //compile-time error.
}
//Following is printed on the console:
//Circle area is 63.585
//Square area is 16
//Rectangle area is 20
注意:is修飾子は、クラスが以下のタイプであるかどうかをチェックします。is修飾子はクラスに対してのみ必要であり、以下に示すようなKotlinオブジェクトでは必要ありません。
sealed class Shape{
class Circle(var radius: Float): Shape()
class Square(var length: Int): Shape()
object Rectangle: Shape()
{
var length: Int = 0
var breadth : Int = 0
}
}
fun eval(e: Shape) =
when (e) {
is Shape.Circle -> println("Circle area is ${3.14*e.radius*e.radius}")
is Shape.Square -> println("Square area is ${e.length*e.length}")
Shape.Rectangle -> println("Rectangle area is ${Shape.Rectangle.length*Shape.Rectangle.breadth}")
}
これによって、Kotlinのsealed classチュートリアルは終了です。参考文献:Kotlinドキュメント