オブジェクト指向で大切になるInterfaceの考え方やオブジェクトの再利用性を学ぶために「Java言語で学ぶデザインパターン入門」について学び、Javaとついでにkotlinで書いてみることにしました。
今回はSingletonについてまとめます。
※また、コメント欄にレビューをいただきました @sdkei さんありがとうございます。
レビューを元に修正しました内容を反映させましたので、その点も踏まえて書いていこうと思います。
##Singletonとは
singletonとは要素を一個しか持たない集合を指す言葉で、下記役割を果たすパターンとして定義されています。
- 指定したクラスのインスタンスが絶対に一個しかないことを保証したい
- インスタンスが一個しか存在しないことをプログラム上で表現したい
##Singletonクラス
このクラスではstaticフィールドとして定義されたsingleton
メンバにSingletonクラスのインスタンスで初期化していますが、クラスのロード時に一度だけ初期化が行われます。
また、privateで外部からのアクセスを防ぐことでインスタンスが一個しか生成されないことを保証しているとの事。
kotlinの場合、staticという定義はなく、代わりにcompanion object
を定義します。
また、companion object
を定義してデコンパイルするとCompanionオブジェクトを介したgetter/setterからアクセスすることになるのでconstか@JvmFieldを使用するとのこと。
しかし、constはプリミティブ型かString型のみなので非プリミティブ型は@JvmFieldを指定し、プリミティブ型を@JvmField指定すると2回初期値を代入するため使い分ける必要があるとのこと。
ただ、@JvmFieldはFieldに可視性を持たすために使用するため、今回のprivateでは使用できなかった。
追記:@sdkei さんレビューありがとうございます!
レビューをいただき、上記のような実装でなくてもKotlinにはオブジェクト宣言を使用してより容易に実装できるようになっているとのことです。
⇒Singleton2.kt
,SingletonSample2.kt
class Singleton {
private static Singleton singleton = new Singleton();
private Singleton() {
System.out.println("インスタンスを生成しました。");
}
public static Singleton getInstance() {
return singleton;
}
}
class Singleton {
init { println("インスタンスを生成しました。") }
companion object {
private val singleton = Singleton()
fun getInstance() = this.singleton
}
}
object Singleton {
init { println("インスタンスを生成しました。") }
}
##Mainクラス
public class SingletonSample {
public static void main(String[] args) {
System.out.println("Start.");
Singleton s1 = Singleton.getInstance();
Singleton s2 = Singleton.getInstance();
if (s1 == s2) {
System.out.println("s1とs2は同じインスタンスです。");
} else {
System.out.println("s1とs2は同じインスタンスではありません。");
}
System.out.println("End.");
}
}
fun main(args: Array<String>) {
println("Start.")
val s1 = Singleton.getInstance()
val s2 = Singleton.getInstance()
if (s1 == s2) println("s1とs2は同じインスタンスです。")
else println("s1とs2は同じインスタンスではありません。")
println("End.")
}
fun main(args: Array<String>) {
println("Start.")
val s1 = Singleton
val s2 = Singleton
if (s1 == s2) println("s1とs2は同じインスタンスです。")
else println("s1とs2は同じインスタンスではありません。")
println("End.")
}
Start.
インスタンスを生成しました。
s1とs2は同じインスタンスです。
End.
##所感
-
staticなフィールドとして定義された
singleton
メンバにSingletonクラスのロード時に一度だけ初期化を行い、privateで外部からのアクセスを防ぐことでインスタンスが一個しか生成されないことを保証していることを学んだ。 -
また、上記保証により、シリアルナンバーなどの一意である必要がある実装の際にメリットになることを学んだ。
-
Kotlinについては下記の点を学ぶことができた
- staticの代わりに
companion object {}
を定義する - companion内でプリミティブ型とString型は
const
, 非プリミティブ型は@JvmField
を指定 - privateの場合はそのまま使用
- オブジェクト宣言により容易にSingletonパターンを使用できること
##参考
下記を参考にさせて頂き、大変読みやすく、理解しやすかったです。
Kotlin で static なメンバーをどう書くべきなのか?
【Kotlin】【Java】Kotlin Javaの比較メモ
Calling Kotlin from Java
Kotlinでデザインパターン Singleton編
オブジェクト式と宣言