當我們需要使用 Singleton Pattern , 例如某個類別,只需要實例化一次 , 例如我們要接Api , 或存取一些數據 ,這時候我們就可以 聲明 object 產生唯一的實例。
object 聲明的 Object 跟 一般 Object 不同的地方 , 在於首次訪問 (access) 這個 object 聲明的 class , Classloader 只會載入一次 static 的 init 區塊,所以達到 Singleton 的效果。 因為object不能包含 constructor, 所以也不能被其他人建立(new)。
object Square {
private var width = 0
fun setwidth (parameter:Int) {
width = parameter
}
fun getwidth ():Int {
return width
}
}
反編譯之後的結果 , Static Initializer 初始化 這個 class 的靜態實例 ,也是這類的唯一的實例
public final class Square {
private static int width;
public static final Square INSTANCE;
static {
Square square = new Square();
}
public final void setwidth(int parameter) {
width = parameter;
}
public final int getwidth() {
return width;
}
}
object Square {
private var width = 0
fun setwidth (parameter:Int) {
width = parameter
}
fun getwidth ():Int {
return width
}
}
聲明和一般 class 的 聲明 雷同,需要 class Name ,可以包含 property ,function等。但是,不允許它們具有構造函數 Constructor , 所以也不能被其他人建立(new)。
與一般class 一樣可以繼承於其他 class
open class MouseAdapter {
open fun mouseClicked(e: MouseEvent) {
}
open fun mouseEntered(e: MouseEvent) {
}
}
object DefaultListener : MouseAdapter() {
override fun mouseClicked(e: MouseEvent) { ... }
override fun mouseEntered(e: MouseEvent) { ... }
}
不能放在local (即直接放在function 內部),但可以放在到其他 Object expressions 或 非 inner class 中 。
我們可以這樣寫:
class outer (){
object inner {
}
}
object outer (){
object inner {
}
}
我們不可以這樣寫:
inner class outer {
object inner {
}
}
fun addInstant (){
object InstantFactory {
}
}
當有些 Object 需要 extend superType , 但又只會用到一次 , 最常見的例子是 OnClickListener
每個 view 要實作 listener 一定都不一樣 , 不可能為了要實作一個 listener , 就寫一個新的 class
這樣不符合效益 , 這時候就會派上用場
btn.setOnClickListener (object: View.OnClickListener {
override fun onClick(v: View?) {
permission ()
}
})
我們可以這樣聲明 , 在 java 中我們會 new 一個 匿名類別 的 object
但我們還需要用一個變數指向他 , 否則 它無法使用
val shape1 = object {
private var width = 0
fun setwidth (parameter:Int) {
width = parameter
}
fun getwidth ():Int {
return width
}
}
上面我們 只是 實例一個 Object , 如果需要有任何的 supertype (繼承) , 或 implete 其他 interface
我們可以寫成下面這樣,跟一般 的 class 一樣
要 繼承 的 class
open class Square (){
private var width = 0
fun setwidth (parameter:Int) {
width = parameter
}
fun getwidth ():Int {
return width
}
}
我們可以宣告一個 匿名 class 所產生的 object 繼承 上面的 類別 (class)
fun main (){
val square = object:Square () {}
square.setwidth(500)
println(square.getwidth())
}
讓我們來看看官方文件怎麼說
Note that anonymous objects can be used as types only in local and private declarations. If you use an anonymous object as a return type of a public function or the type of a public property, the actual type of that function or property will be the declared supertype of the anonymous object, or Any if you didn't declare any supertype. Members added in the anonymous object will not be accessible.
意思是 匿名類別 所 產生 的 object 如果是被 public 變數 指向 或者 為 public 函數 所返回的內容,則該函數返回的型別或變數的型別 會是這個 object 所繼承的 supertype,或者如果 未繼承任何 supertype,則為Any 。這樣自然無法取用這個object 內的成員變數 或 成員函數。只能有在 local (同一個 function 範圍內)和 private 聲明 (加上private keyword) 中 , 才會知道他明確型別。
像下面的例子
class Example4{
// 1.
// public property
val publicObj = object{
val x = 1
}
// 2.
// private property
private val privateObj = object{
val x = 2
}
// 3.
// Private function, so the return type is
// the anonymous object type
private fun privateFunction() = object {
val x: String = "x"
}
// 4.
// Public function, so the return type is Any
fun publicFunction() = object {
val x: String = "x"
}
fun showcase(){
// 5.
// local
val scopedObj = object{
val x = 3
}
println(privateFunction().x ) // Works fine
println(publicFunction().x ) // ERROR : unresolved reference: x
println(publicObj.x) // ERROR : unresolved reference: x
println(privateObj.x) // Works fine
println(scopedObj.x) // Works fine
}
}
上面 2 和 4 並不在一個 function 內 , 又是 public , 型別會是Any ,並無法調用
Companion Objects 和 Object declarations 都是屬於 Singleton ,也就是說這個 class 只會有唯一的實例,也不能被其他人建立(new), 他是 Object declarations 的特例, 跟 Object declarations 不同的地方在於 ,Company Objects伴隨一個 class 並會在這個 class 被初始化時跟著被實例,限制只能 一個 class 只能有一個 Companion Objects , 而 Object declarations 並無此限制
class A {
companion object {}
object A{}
object B{}
}
Companion Objects 的名稱是可選的,可以省略 如果沒有給他類名 , 預設類名會是 Companion, 但不管有沒有
給他類名 , 如果外部如果要指向(reference) 一個 class 中的 companion object , 會是透過這個 Class 做指向(reference)
class MyClass1 {
companion object Named { }
}
val x = MyClass1 // reference to the companion object of MyClass1
class MyClass2 {
companion object { }
}
val y = MyClass2 // reference to the companion object of MyClass2
Company Object 和 Object declarations 相同 ,可以包含property ,function 等。如果我們要調用這個 companion object 內 成員函數 , 會像下面這樣
class MyClass1 {
companion object Named {
fun getName () {}
}
}
val name= MyClass1.getName()
interface Factory {
fun create()
}
class MyClass {
companion object : Factory {
override fun create()
}
}
val f: Factory = MyClass
像這種 companion object 或object 聲明這種靜態的寫法,不宜太常使用,因為只要一被實例會跟整個應用程序的生命期一樣長,如果一個其他的物件 (Object)已經沒有用處了,但是單例這種靜態的寫法還持有它的引用(reference),那麼在整個應用程序的生命週期它都不能正常被回收,從而導致內存洩露。