2
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

【Kotlin】@JvmStaticと実行時間

Last updated at Posted at 2020-06-08

1. object について

object を理解するには、 Singleton というものを知っていなければいけません。
これは、「あるクラスのインスタンスを1つのみにする」という時に出てくるものです。
詳しく知りたい方はこちらがおすすめです。 デザインパターン「Singleton」

Kotlin で実装したい時は object を使うだけでいいのです。

object Singleton

2. @JvmStatic について

object に 何かしらの関数があったとしましょう。

object Singleton {
    fun nannka(){

    }
}

Kotlin であれば、Singleton.nannka() とアクセスすることができますが
Java から実行するときは Singleton.INSTANCE.nannka() としなければいけません。
nannka() というのはシングルトン内の関数であって、staticな関数ではありません。
その為、シングルトンのインスタンスを経由してアクセスする必要があります。
しかし、static fun というものはありません。
その時に登場するのが、@JvmStatic です。
staticにしたい関数にアノテーションをつけると、static関数になります。

object Singleton {
    @JvmStatic
    fun nannka(){

    }
}

static関数になれば、Javaでも Singleton.nannka() のようにアクセスすることができるようになります。

どうでもいい話なので読み飛ばしてもらって構いません。
Singleton.INSTANCE.nannka() とアクセスできるということは、
INSTANCE という変数が @JvmStatic で定義されているということになります。
以下のような感じになっているわけですね。(@JvmField の方が近いです。)

object Singleton {
    @JvmStatic
    val INSTANCE = this
    
    fun nannka(){

    }
}

ちなみに、これを実行しようとすると、例外が発生します

java.lang.ClassFormatError: Duplicate field name "INSTANCE" with signature "LSingleton;" in class file Singleton

3. 実行時間

@JvmStatic をつけるかつけないかでどれだけ実行時間に違いがあるのかを検証してみます。

検証1

10億回関数を実行します。関数内の処理は変数の加算処理1回だけです。

ソースコード

fun main(){
    val loop = 1000000000L
    val timeJvmStatic = block(loop) {
        CounterJvmStatic.count()
    }
    val timeSingleton = block(loop) {
        CounterSingleton.count()
    }
    println("%,d:   %,d   %,d".format(loop, timeJvmStatic, timeSingleton))
}

fun block(loop: Long, run: () -> Unit): Long {
    val begin = System.currentTimeMillis()
    for(i in 0 until loop) {
        run.invoke()
    }
    val end = System.currentTimeMillis()
    return end - begin
}

object CounterJvmStatic {
    var count = 0
    
    @JvmStatic
    fun count() {
        count ++
    }
}

object CounterSingleton {
    var count = 0
    
    fun count() {
        count ++
    }
}

実際に実行する

結果

1,000,000,000:   665   1,736
1,000,000,000:   651   1,717
1,000,000,000:   652   1,722

2倍くらい違いますね。しかし、10億回実行してこの数字なわけですから、ほとんど無視できるくらいでしょうか。

検証2

関数内の処理が重かった場合を考えてみましょう。
10万回関数を実行します。関数内の処理は変数の加算処理を1000回です。

ソースコード

fun main(){
    val loop = 1000000L
    val timeJvmStatic = block(loop) {
        CounterJvmStatic.count()
    }
    val timeSingleton = block(loop) {
        CounterSingleton.count()
    }
    println("%,d:   %,d   %,d".format(loop, timeJvmStatic, timeSingleton))
}

fun block(loop: Long, run: () -> Unit): Long {
    val begin = System.currentTimeMillis()
    for(i in 0 until loop) {
        run.invoke()
    }
    val end = System.currentTimeMillis()
    return end - begin
}

object CounterJvmStatic {
    var count = 0
    
    @JvmStatic
    fun count() {
        for(i in 0 until 1000) count ++
    }
}

object CounterSingleton {
    var count = 0
    
    fun count() {
        for(i in 0 until 1000) count ++
    }
}

実際に実行する

結果

1,000,000:   65   80
1,000,000:   55   57
1,000,000:   59   61

やはり、ほとんど変わりませんね。

4. まとめ

実行時間のためだけに @JvmStatic をつけるべきではないと思います。
Javaとの互換性のためのアノテーションなわけですから、Kotlinのみの開発で使うことはないでしょう。
実行時間が気になるほど、object にアクセスをするような状況なのであれば、一度変数で保持をする方が一般的だと思います。

object Singleton {
    fun nannka(){

    }
}

fun main(){
    val singeleton = Singleton
    singeleton.nannka()
}

しかし、ここで実行時間を気にするくらいであれば、他の部分で気にするべきなのでしょう。

余談ですが、object でクラスが定義されていれば、プログラムを読んでいる人からすると
「そのクラスはマルチプルである必要がない」と分かるので、理解しやすくなると思います。
積極的に object は使うべきではないでしょうか。私はそう思います。

2
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
2
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?