PHONE APPLI Engineer blog

エンジニアブログ

Kotlin 紹介 - 定数

はじめに

弊社プロダクト「PHONE APPLI PEOPLE」では、サーバサイドに Kotlin を採用しています。
今回は、 Kotlin の定数について、自身の復習をかねて紹介します。

【目次】
・ 前提条件
コンパイル時定数
・ 通常の定数 (非コンパイル時定数)
・ 遅延初期化定数
enum 定数
・ object 定数
・ 終わりに

前提条件

「定数」は、大きな意味だと 不変の値 ですが、クラス機能のある言語だと 静的アクセスが可能なこと も条件に含まれることがよくあると思います。
※ 静的アクセス可能 : クラスのインスタンスを作らなくても使用できる領域に定義したもの
  Kotlin だと (トップレベル, objectcompanion object) いずれかに定義したプロパティや enumobject
14_01a.png

この記事で扱う定数としては、下記とします。
・ 静的アクセス可能な val プロパティ (変更不可プロパティ)
・ enum
・ object

定数の定義場所による分類は、複雑になるためしません。
Kotlin on JVM の話になります。

コンパイル時定数

14_02.png

特徴

コンパイル時に値が決まる
・ 使用できる値は、プリミティブ型と String 型のみ
アノテーションのパラメータにできる
バイトコードでは定数の使用箇所にインライン展開される

定数の中で一番強いです。
ただ、コンパイル時に値が決まっておく必要があるので、 new の必要な参照型は使えません。(String リテラルだけ例外です)
関数を通す必要がある場合 (= コンパイル時に値が決まらない) も使えません。

Spring Boot 等のアノテーションを多用するフレームワーク・ライブラリを使っていると、アノテーションのパラメータにできることは割と重要です。

バイトコードでインライン展開されることによる問題 (定数の値が変更された場合に使用箇所が再コンパイル対象にならない) は、 Gradle + Kotlin 環境では遭遇したことはないです。
Kotlin コンパイラがよしなにやってくれてると思われます。

通常の定数 (非コンパイル時定数)

14_03.png

コンパイル時に値が決まらない定数は、これになります。
アノテーションのパラメータにはできません。

@JvmField を付けている理由は、 getter 関数が作られることを防ぐためです。
(public プロパティに getter 関数が作られるのは Kotlin の仕様)
定数の getter 関数があることによるコード上での弊害は無いですが、定数は基本フィールドに定義する派なので付けるようにしてます。

個人的に @JvmField 機能も const のように構文の方がよかったのでは、と思ってます。

遅延初期化定数

14_04.png

Kotlin の Delegated Properties の lazy 機能を使った定数定義です。
Delegated Properties は getter 関数必須のため、 @JvmField 指定はできません。
プログラム内で定数が初めて使用された時に、値が作成されます。
逆に言えば、使用されるまで、値作成のための計算処理は行われず、値に必要なメモリも確保されません。

使用用途としては次になります。
・ プログラムの起動を速めたい場合
・ 共通モジュール (ライブラリ) において、未使用状態であれば値を作ってほしくない場合

enum 定数

14_05.png

クラスのインスタンスをまとめて定義できて、かつ、アノテーションのパラメータにできる優れものです。
まとめて定義するものなので、定数グループが主な使用用途になります。

各種 java ライブラリでシリアライズ・デシリアライズ機能も充実しています。
そのため、 API のリクエスト・レスポンスで値が決まっているもの (いわゆるコード) の型としても採用しやすいです。

object 定数

14_06.png

Object declarations はシングルトンを作る機能で、定数のように扱えます。
単体だと enum の 1 個版みたいなものです。
ただし、アノテーションのパラメータにはできません。

Sealed Classes と組み合わせることで、カスタマイズ性の高い enum のようなことができます。
object を定数として扱う場合は、これが大半の使用用途になると思います。
enum だと書き辛い/書けない場合に sealed class + object + xxx を選択する感じです。

終わりに

Kotlin で定数と認識してるものについて、説明してみました。
Java と比べて、遅延初期化定数と object 定数は Kotlin 独自のものになりますが、 Java で実現できないこともないと思います。
(ただ、実装はたぶん面倒...)

定数に限った話ではないですが、使用用途を意識して書くことで、いいコードに繋がると思います。


 

書いた人: プロダクトデベロップメント部 藤本泰輔