Kotlin可見性修飾符

蒙哥的客廳發表於2020-11-30

Java為我們提供了public、protected、private以及default四個可見性修飾符,用來指定類、方法及屬性的可見性。Kotlin中的可見性修飾符與Java中的很類似,但也有不一樣的地方,主要有以下幾點:

  • Kotlin與Java的預設修飾符不同,Kotlin中是public,而Java中是default;
  • Kotlin中有一個獨特的修飾符internal;
  • Kotlin可以在一個檔案內單獨宣告方法及常量,同樣支援可見性修飾符;
  • Java中除了內部類可以用private修飾以外,其他類都不允許private修飾,而Kotlin可以;
  • Kotlin和Java中的protected的訪問範圍不同,Java中是包、類及子類可訪問,而Kotlin只允許類及子類。

預設修飾符

很多時候,我們在實現類或者方法的時候都會省略它的修飾符。當然,在Java中我們很自然地會給類加上public修飾符,因為大多數類都可能需要在全域性訪問。而Java的預設修飾符是default,它只允許包內訪問。雖然通常IDE會自動幫我們加上public修飾符,但是這也讓人覺得它是一個多餘的宣告。所以,出於簡潔性的考慮,Kotlin將預設可見修飾符指定為了public。

前面我們說到了Java中預設修飾符是default,它的作用域是包內可訪問。那麼Kotlin中有類似的修飾符嗎?

Kotlin中的作用域

Kotlin中的作用域可以被稱作模組內訪問。那麼什麼是模組呢?模組可以看作一起編譯的Kotlin檔案組成的集合。那麼,Kotlin中為什麼要定義這種新的作用域呢?難道Java提供的包內訪問作用域不香嗎?Java的包內訪問作用域確實存在一些問題。舉個例子:假如我們在Java專案中定義了一個類,使用了預設修飾符,那麼這個類是包內可見,其他外部類將無法訪問它。接下來我們把這個專案打包成一個類庫,並提供給其他的專案使用。這時候,如果開發者想訪問之前定義的這個類,除了複製原始碼之外,還有一個方式。就是在程式中建立一個包,它的名字與該類所在的包的名字一致。通過這種方式,就可以直接訪問我們前面定義的類了。換句話說,我們之前定義的類就被洩露了,這很有可能導致問題。示例程式碼如下:

package com.dripower

/** 第三方類庫程式碼 */
class TestDefault {
  ...
}
package com.dripower

/** 在我們的專案中建立com.dripower包 */
class Test {
  TestDefault td = new TestDefault()
}

Kotlin並沒有採用這種包內可見的作用域,而是使用了模組內可見的作用域。模組內可見指的是該類只對一起編譯的其他Kotlin檔案可見。由於開發工程與第三方類庫不屬於同一個模組,因此就解決了Java中存在的問題。這就是Kotlin中internal修飾符的作用。

在Java程式中,我們很少見到用private修飾的類,因為Java中的類或方法沒有單獨屬於某個檔案的概念。對於某個檔案來說,它裡面的類要麼是public,要麼是包私有,而沒有隻屬於這個檔案的概念。若要用private修飾,那麼這個只能是其他類的內部類。而Kotlin中則可以用private給單獨的類修飾,它的作用域就是當前這個Kotlin檔案。比如:

package com.dripower.car

class BMWCar(val name: String) {
  private val bMWEngine = Engine("BMW")

  fun getEngine(): String{
    return bMWEngine.engineType()
  }
}

private class Engine(val type: String) {
  fun engineType(): String {
    return "theenginetypeis$type"
  }
}

Kotlin中的protected修飾符

除了private修飾符的差別,Kotlin中的protected修飾符也與Java有所不同。Java中的protected修飾的內容作用域是包內、類及子類可訪問,而在Kotlin中,由於沒有包作用域的概念,所以protected修飾符在Kotlin中的作用域只有類及子類。我們對上面的程式碼稍加修改:

package com.dripower.car

class BMWCar(val name: String) {
  private val bMWEngine = BZEngine("BMW")

  fun getEngine(): String {
    return bMWEngine.engineType() // 編譯錯誤,engineType只對類及子類可見
  }
}

private open class Engine(val type: String) {
  protected open fun engineType(): String {
    return "theenginetypeis$type"
  }
}

private class BZEngine(type:String): Engine(type) {
  override fun engineType(): String {
    return super.engineType()
  }
}

我們可以發現,同一個包下的其他類不能訪問protected修飾的內容,而在子類中則可以訪問。

總結一下,可見性修飾符在Kotlin與Java中大致相似,但也有自己的很多特殊之處。這些可見性修飾符比較如下所示。

修飾符含義與Java的比較
public(預設)Kotlin中的預設修飾符與Java中的public效果相同
protected類及子類可見含義一致,但作用域除了類及子類之外,包內也可見
private類內修飾只有本類可見,類外修飾檔案內可見只有類內可見
internal模組內可見

相關文章