インラインクラスの導入
クラスやインターフェースに関する変更点の最後に紹介するのは、インラインクラスです。
インラインクラスとは
インラインクラスは、バージョン1.5で新たに導入された仕組みであり、英語での正式名称は、Inline Value Classesとvalueが入ります。そのため、第2回で紹介したインライン関数や前回紹介したインラインプロパティとは、少し仕組みが違います。
とはいえ、何かが埋め込まれるために「インライン」という名称が使われており、その埋め込まれるものが値=valueであることから、先の英語の名称となっています。
インラインクラスは、ただひとつの読み取り専用の値を保持するためだけのクラスです。そのため、保持している値に対して別名をつけるようなイメージであり、そのような場合に有効です。
このような仕組みから、クラス構文は、例えばリスト10のようになります。このクラスは、ホワイトレディカクテルの金額のみを保持するためのクラスです。そして、その金額=Int型の値にWhiteLadyPriceという名称を与え、以降は、WhiteLadyPrice型として扱われるようになります。
@JvmInline // (1) value class WhiteLadyPrice(val price: Int) // (2)
インラインクラスの特徴としては、リスト10の(2)のように、クラス宣言にvalueキーワードを付与します。そして、ただひとつの読み取り専用の値を保持するために、valプロパティをひとつだけ宣言し、コンストラクタの引数とします。このことから、インスタンス生成時にその値を受け取り、それを読み取り専用として保持するクラスとなります。
なお、このインラインクラスをJVM上で利用する場合は(1)の@JvmInlineアノテーションが必要となります。といっても、ほとんどのKotlinコードはJVMでの動作を前提としているので、実質このアノテーションは必須と言えます。
インラインクラスに記述できるもの
インラインクラスは、ある値に対して別名とその名称のデータ型を付与するような仕組みであり、型エイリアスのように思えるかもしれません。
しかし、インラインクラスには、valプロパティ以外に以下のメンバが定義でき、これが型エイリアスではなしえないインラインクラスの特徴です。
- initブロック
- セカンダリコンストラクタ
- 算出プロパティ
- 通常メソッド
これらをWhiteLadyPriceに追記したサンプルコードは、リスト11のようになります。
@JvmInline value class WhiteLadyPrice(val price: Int) { init { // 1 println("WhiteLadyPriceが${price}円で生成されました。") } constructor(price: Int, creator: String): this(price) { // 2 println("WhiteLadyPriceが${creator}さんによって${price}円で生成されました。") } val priceStr: String // 3 get() { return "${price}円" } fun showPrice() { // 4 println("WhiteLadyPriceの金額は${priceStr}です。") } }
このうち、2のセカンダリコンストラクタが定義できるようになったのは、バージョン1.9からで、比較的新しい仕組みです。
インラインクラスはインターフェースの実装が可能
インラインクラスは、インターフェースを実装することも可能です。例えば、リスト12のインターフェースCocktailPriceがあるとします。
interface CocktailPrice { fun showPrice() }
このCocktailPriceインターフェースを実装したインラインクラスは、リスト13のようにshowPrice()をオーバーライドする必要があります。
@JvmInline value class XYZPrice(val price: Int) : CocktailPrice { override fun showPrice() { println("XYZPriceの金額は${price}円です。") } }
インラインクラスの委譲も可能
このインターフェースを実装したインラインクラスを作成する際、インターフェースの実装に委譲の仕組みを利用することができるように、バージョン1.7で変更されています。Kotlinには、プロパティ以外にクラスそのものに対しても委譲を適用できる仕組みがあります。その際も、プロパティ同様にbyキーワードを利用し、例えばリスト14のコードとなります。
@JvmInline value class BalalaikaPrice(val deligateCocktailPrice : CocktailPrice) : CocktailPrice by deligateCocktailPrice // (1) val xyzPrice = XYZPrice(1250) // (2) val balalaikaPrice = BalalaikaPrice(xyzPrice) // (3)
リスト14のポイントは、(1)でインラインクラスを定義する際、そのプロパティをCocktailPrice型としておき、そのプロパティをそのままbyの次に記述している点です。
このようなクラスを利用する場合(3)のコードになります。(2)で事前にリスト13のXYZPriceインスタンスを生成しておき、それをそのまま引数にBalalaikaPriceを生成します。すると、委譲の仕組みが働き、BalalaikaPriceの内容は、XYZPriceのインスタンスと同じになります。
まとめ
Kotlinのバージョン2.0までに導入された新機能をテーマごとに紹介する本連載の第4回は、いかがでしたでしょうか。今回は、クラスとインターフェースに関するアップデートというテーマの後編として、委譲プロパティとインラインクラスを紹介しました。さて、この連載も次回でいったんの区切りとなります。第5回はコルーチンを紹介します。