Kotlin
singleton

KotlinのSingletonについて

注意:objectなどkotlinのsingletonを乱用しないようにしましょう。

少なくともAndroidでは参照がないとGarbage Collectionで回収される可能性があるのでApp全体のグローバル的にデータの保持とかする時は注意して使いましょう。

https://discuss.kotlinlang.org/t/singleton-with-object-declarations-gets-garbage-collected/4786/10

ActivityA->ActivityBで行った時にActivityAが参照しているobjectシングルToneクラスは回収されて
ActivityB->ActivityAで再び初期化が走る可能性があります。当然保持して使おうとしているデータも消えるはず。
AndroidではちゃんとonSaveInstanceStateとかを対応した方がいいです。
Javaの時代もそうですがstatic変数/クラス参照も乱用するのはよくないことでしたね。

KotlinのSingletonを調べて見た。

Kotlin documentにも書いているが
https://kotlinlang.org/docs/reference/object-declarations.html
object declarations are initialized lazily, when accessed for the first time
これはKotlin bytecodeでのjava codeを見るとstatic initializeで初期化されることが分かる

   static {
      new Singleton();
   }

なので

結論

1.基本的objectを使った方がいい。(Thread safe)

//kotlin bytecodeで生成されたjava codeを見ると分かるが
//objectはSingleTone(static)+lazily(static initialize)にinstanceを作成する
object Singleton{}

例:

//AndroidのPreferencesでSharedpreferenceを初期化して使うときに便利
object PreferencesHelper{
  private lateinit var prefs: Sharedpreference
  fun setUp(context: Context){
    prefs = PreferenceManager.getDefaultSharedPreferences(context)
  }

    var userId: String
        get() = prefs.getString("userId","")
        set(value) { prefs.edit().putString("userId", value).apply() }
}

2.lazy方式でSingleToneをやる方式は推奨できない,ただスレッドセーフを求めていなければLazyThreadSafetyMode.NONEで使ってもいい。

理由はlazyのdefault mode SYNCHRONIZEDが実際はjavaのSingleTone方式:DoubleCheck方式なのでoverheadがある。また初期化パラメタを1.と同じく別途設定する必要がある

class LazyThreadSafeDoubleCheck private constructor() {
    companion object {
        //lazy modeディフォルトはLazyThreadSafetyMode.SYNCHRONIZEDである
        //スレッドセーフを求めていなければlazy(LazyThreadSafetyMode.NONE)とパラメタをづける

        val instance by lazy(LazyThreadSafetyMode.NONE) {
            LazyInit()
        }
    }
}

3.ただparameterも含めで厳密に一回だけ初期したい場合はかきの方法がいいだろう。Thread safeではないのでこれをベースにもうちょっと処理が必要。

https://stackoverflow.com/a/40437274

class TasksLocalDataSource private constructor(context: Context) : TasksDataSource {
    private val mDbHelper = TasksDbHelper(context)

    companion object {
        private var instance : TasksLocalDataSource? = null

        fun  getInstance(context: Context): TasksLocalDataSource {
            if (instance == null)
                instance = TasksLocalDataSource(context)

            return instance!!
        }
    }
}