object與companion object
Prerequisites:
以下的內容比較難明白,需要配合對映的Java程式碼才能明白。
object 類別
這裡的object,指的是就只有一個物件,不管被建立幾次,只會被建立一次。
Singleton的概念。
語法
object 類別名 {
}
使用方法
類別名.方法()
類別名.屬性名
建立object物件
1
2
3
4
5
6
7
8
9
object Single {
val name:String = "Single"
fun method1() {
println("method1")
}
fun method2() {
println("method2")
}
}
測試
1
2
3
4
5
6
7
8
fun main() {
Single.method1()
Single.method2()
println(Single.name)
println(Single.hashCode())
println(Single.hashCode())
println(Single.hashCode())
}
method1
method2
Single
1450495309
1450495309
1450495309
hashCode如同物件身份證,代表印出三次Single物件,hashCode都相同,代表同一個物件。
Kotlin程式碼
1
2
3
4
5
6
object Single {
val name: String = "Single"
fun method1() {
println("method1")
}
}
對映的Java程式碼
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
public final class Single {
// 唯一的單例實例
public static final Single INSTANCE;
// 屬性對應
private final String name = "Single";
// 初始化區塊
static {
INSTANCE = new Single();
}
// 私有建構子,防止外部建立實例
private Single() { }
// Kotlin 的 val name 對應成 getter
public String getName() {
return name;
}
// Kotlin 的 method1 對應成普通實例方法
public void method1() {
System.out.println("method1");
}
}
companion object
這邊不建議解釋為object物件,而是類別的靜態屬性與靜態方法的放置區塊。
把類別的靜態屬性與靜態方法,全放在companion object {}大括號之中。
companion object {
類別靜態屬性
類別靜態方法
}
Kotlin 寫法。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
class Test {
init {
println("建立物件")
}
companion object {
init {
println("類別載入")
}
fun staticMethod1() {
println("靜態方法")
}
}
}
對映Java的寫法。
1
2
3
4
5
6
7
8
9
10
11
12
13
public class Test {
{
System.out.println("建立物件");
}
static {
System.out.println("類別載入");
}
public static void staticMethod1() {
System.out.println("靜態方法");
}
}
由上方可以發現,static {} 靜態匿名區塊,變成companion object {}中的init {},證明這個init是「類別載入」的靜態匿名區塊。
1
2
3
4
5
6
7
class Test {
companion object {
init {
println("類別載入時執行")
}
}
}
而以下這個init是「物件」建立的時候,才會呼叫。
1
2
3
4
5
class Test {
init {
println("建立物件")
}
}
也就是說kotlin沒有static,但類別的靜態屬性、靜態方法一行放在companion object {} 中。
class 類別 {
companion object {
靜態屬性
靜態方法
}
}
測試
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
class Test {
init {
println("建立物件")
}
companion object {
init {
println("載入類別")
}
// 靜態屬性
val info: String = "info"
// 靜態方法
fun load() {
println("載入資訊...")
}
}
}
fun main() {
Test.load()
println(Test.info)
}
載入類別
載入資訊...
info
注意!以上的執行結果,完全沒執行「建立物件」這行。
只能透過類別名,才能呼叫companion object中的靜態屬性與靜態方法。
類別名.靜態方法
類別名.靜態屬性
Kotlin 沒有 static {}。
但是 Kotlin 有兩種替代方式:
| Java 的靜態概念 | Kotlin 對應方式 |
|---|---|
| static {} 區塊 | 在 companion object 裡的 init |
| static 方法 | 在 companion object 裡定義函式 |
| static 欄位 | 在 companion object 裡定義變數 |
以下會編譯錯誤,不能透過物件去呼叫靜態屬性與靜態方法。
1
2
3
4
5
6
fun main() {
val obj = Test()
// 以下編譯錯誤
obj.info
obj.load()
}
透過類別名,才能呼叫靜態方法與靜態屬性。
1
2
3
4
fun main() {
Test.load()
println(Test.info)
}
載入類別
載入資訊...
info
只有建立物件的時候,才會輸出「建立物件」。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
class Test {
init {
println("建立物件")
}
companion object {
init {
println("載入類別")
}
// 靜態屬性
val info: String = "info"
// 靜態方法
fun load() {
println("載入資訊...")
}
}
}
fun main() {
val obj = Test()
}
載入類別
建立物件
| 功能/階段 | Java | Kotlin | 執行時機 |
|---|---|---|---|
| 實例初始化(每次 new) | {}(匿名區塊) | init {} | 每次建立物件時 |
| 類別載入時(一次) | static {} | companion object { init { } } | 第一次使用類別成員時 |
| 靜態方法 | static void …() | companion object { fun …() } | 直接用類別呼叫 |
| 建構子 | 類別名() | constructor()或主建構子 | 每次建立物件時 |
companion object 委托by
以下程式碼,ConsoleLogger實作 interface Logger。
Service的隱藏靜態內部類別(companion)也要實作 interface Logger。
但是怎麼實作,全交由ConsoleLogger實作。
kotlin 程式碼
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
interface Logger {
fun log(msg: String)
}
object ConsoleLogger : Logger {
override fun log(msg: String) = println("Log: $msg")
}
class Service {
// 隱藏的靜態內部類別
companion object : Logger by ConsoleLogger
}
fun main() {
Service.log("Start service") // Log: Start service
}
對映的Java程式碼
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
public interface Logger {
void log(String msg);
}
public final class ConsoleLogger implements Logger {
public static final ConsoleLogger INSTANCE;
static {
INSTANCE = new ConsoleLogger();
}
private ConsoleLogger() {}
@Override
public void log(String msg) {
System.out.println("Log: " + msg);
}
}
靜態內部類別的log方法,呼叫的是ConsoleLogger的log方法。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
public final class Service {
// Companion object 對應成靜態內部類別
public static final class Companion implements Logger {
// Kotlin 自動生成的單例
public static final Companion INSTANCE;
static {
INSTANCE = new Companion();
}
private Companion() {}
// 「by ConsoleLogger」表示所有 log() 呼叫都轉交給 ConsoleLogger.INSTANCE
@Override
public void log(String msg) {
ConsoleLogger.INSTANCE.log(msg);
}
}
}