TL;DR
-
Kotlin 1.5
以降 ->KClass::isValue
で判定できる1- ただし、
kotlin-reflect
が必要
- ただし、
-
Kotlin 1.4
以下 orkotlin-reflect
無し -> 正式な方法は提供されていない- 代替となりうる手段は有る
本文では代替となりうる手段の方を中心に解説します。
代替となりうる手段
以下2つのやり方を紹介します。
-
value class
特有の関数の名前から判定する-
Kotlin
のバージョンに関係なく動作しそう - エッジケースで誤判定の可能性有り
-
-
JvmInline
アノテーションを探す- 条件を
Kotlin 1.5
以上に限れるなら、エッジケースに気を配らずに書けるためこちらが良さそう -
Kotlin 1.4
以前で実際に動作するかは不明
- 条件を
kotlinx-metadata-jvm
を使うことでも判定できますが、外部ライブラリが必要になるためここでは一旦説明しません。
value class特有の関数の名前から判定する
value class
は特有の関数名を持つstatic
メソッドをコンパイル時に生成します。
代表的な例としては以下が有ります。
-
constructor-impl
2 -
box-impl
/unbox-impl
-
toString-impl
/hashCode-impl
よって、この名前を持つメソッドが含まれるかから判定することができます。
import kotlin.reflect.KClass
fun KClass<*>.myIsValue() = this.java.declaredMethods.any { it.name == "box-impl" }
ただし、エッジケースとして、Kotlin
上でbox-impl
という名前の関数を書くことはできるため、そのような関数が含まれる内容に対してこの判定関数を実行すると誤判定します。
仮に厳密さを求める場合、対処療法的になってしまいますが、以下のような条件を加えることが考えられます。
- 複数の関数名が全て含まれる
- 対象関数が
static
-
Class<*>.declaredFields
のサイズが1
補足
この判定方式は下記を参考にさせて頂きました。
物凄く細かい話ですが、手元で確認した所、対象関数の内declaredMethods
で取得できるのはbox-impl
が最初だったので、例ではこれをそのまま使っています。
JvmInlineアノテーションで判定する
以下はKotlin 1.5
以上のみで可能な方法です。
Kotlin 1.5
以上では、value class
には必ずJvmInline
アノテーションを付与する必要が有ります。
@JvmInline
value class ValueClass(val value: Int)
@JvmInline
@Metadata( /* 省略 */ )
public final class ValueClass {
よって、これを探すことで確認が可能です。
import kotlin.reflect.KClass
fun KClass<*>.myIsValue() = this.java.annotations.any { it is JvmInline }
ただし、このアノテーションは@SinceKotlin("1.5")
となっており、それ以前の環境からは普通に参照することができません。
クラス名(kotlin.jvm.JvmInline
)から比較を行う方法も有るかもしれませんが、Kotlin 1.4
以前の内容が絡む際に動くかは未知数です。
-
これは
static
メソッドであり、コンストラクタではありません。 ↩