public și internal în Kotlin

public (implicit) face o declarație vizibilă oriunde; internal o face vizibilă doar în modulul în care este scrisă.

Sintaxă
public class A            // vizibil peste tot (implicit, nu trebuie scris)
internal class B          // vizibil doar în același modul
internal fun ajutor() { }

Ce face public și internal

Kotlin are patru niveluri de vizibilitate: public, internal, protected și private. Dacă nu scrii nimic, declarația este public: vizibilă din orice cod care are acces la pachetul ei. De aceea public se scrie rar, de obicei doar în bibliotecile care activează modul -Xexplicit-api, unde orice API public trebuie marcat explicit.

internal înseamnă „vizibil în tot modulul”, dar nu în afara lui. Un modul este un set de fișiere compilate împreună: un modul Gradle sau Maven, un proiect din IntelliJ, sau fișierele date unei singure comenzi kotlinc. Așa o bibliotecă își poate folosi clasele ajutătoare în toate pachetele ei, fără ca utilizatorii bibliotecii să le vadă.

Java nu are „modul” în același sens: pe JVM, membrii internal devin public în bytecode, cu numele modificat ca să fie greu de apelat din Java. Pentru private și protected, vezi pagina dedicată.

Exemple

Exemplul 1: Declarații public și internal în același modul

Programul este un singur modul, deci main vede și clasa internal. O altă bibliotecă sau aplicație care ar folosi acest cod ar vedea doar Calculator și calculeazaTva.

// public implicit: vizibil pentru oricine folosește modulul
class Calculator {
    fun calculeazaTva(pret: Double) = Rotunjire.laBani(pret * CotaTva.STANDARD)
}

// internal: doar pentru codul din acest modul
internal object CotaTva {
    const val STANDARD = 0.21
}

internal object Rotunjire {
    fun laBani(x: Double) = Math.round(x * 100) / 100.0
}

fun main() {
    val c = Calculator()
    println("TVA pentru 99,99 lei: ${c.calculeazaTva(99.99)} lei")
    println("Cota folosită: ${CotaTva.STANDARD}")
}

Rezultat

TVA pentru 99,99 lei: 21.0 lei
Cota folosită: 0.21

Exemplul 2: Eroare din alt modul (nu rulează aici)

Două module: facturare (pachetul ro.firma.facturare, cu class Calculator publică și internal object CotaTva), compilat separat cu kotlinc -module-name facturare -d fact.jar, și modulul app de mai jos, compilat cu kotlinc app/Main.kt -cp fact.jar -module-name app. Calculator se vede, CotaTva nu.

// modulul :app
import ro.firma.facturare.Calculator
import ro.firma.facturare.CotaTva

fun main() {
    println(Calculator().calculeazaTva(100.0))
    println(CotaTva.STANDARD)
}

Rezultat la compilare

app/Main.kt:2:27: error: cannot access 'object CotaTva : Any': it is internal in file.
import ro.firma.facturare.CotaTva
                          ^^^^^^^
app/Main.kt:6:13: error: cannot access 'object CotaTva : Any': it is internal in file.
    println(CotaTva.STANDARD)
            ^^^^^^^

Greșeli frecvente

  • Credința că internal înseamnă „în același pachet”: în Kotlin înseamnă „în același modul”. Kotlin nu are vizibilitate de pachet ca Java.
  • Expunerea unui tip internal printr-o funcție public (fun cota(): CotaTva): eroare 'public' function exposes its 'internal' return type 'CotaTva'.
  • Scrierea lui public peste tot din obișnuința din Java: este redundant, pentru că este valoarea implicită.

Comenzi înrudite

  • private și protected: private ascunde o declarație în clasa sau fișierul ei; protected o face vizibilă și în subclase.
  • package și import: package stabilește numele pachetului unui fișier; import aduce în fișier clase și funcții din alte pachete.
  • class: class declară o clasă: un tip nou care grupează date (proprietăți) și comportament (metode).
  • kotlinc: Compilatorul Kotlin din linia de comandă: transformă fișierele .kt într-un program care rulează pe JVM.