Kotlin Visibility and Scope - mariamaged/Java-Android-Kotlin GitHub Wiki
- Visibility: What is allowed to see these classes, functions, and properties?
- Scope: Where so these classes, functions, and properties actually exist?
Many programming languages have
visibility options
, to controlwhat objects
canreference functions and properties
fromother objects
.
- In both Java and Ruby, for example, you can have:
- public.
- private.
- protected. methods
- Kotlin has those too, though Kotlin adheres a bit closer to Java's definition of those terms than Ruby's.
- Kotlin also has an internal visibility option that is a little strange.
- In some languages, everything is public by default.
- Any object can reference public things.
- In Java, though, everything is
"package-private"
bydefault
: meaning that it is public to things in the same Java package, but private to things in other packages.
- In Java, though, everything is
-
Kotlin takes the
public
bydefault
route. - You will not see a
public
keyword used in typical Kotlin. - This is why code like this works.
- Both
Foo
andsomething
are public, and so code that lives outside of Foo can:- Reference Foo (for creating instances).
- something() (for calling that function on instances of Foo).
class Foo {
fun something() {
println("Hello, world")
}
}
val foo = Foo()
foo.something()
foo.something()
foo.something()
-
private
by contrast, limits access to instances of its class, and nothing else.
class Foo {
private fun something() {
println("Hello, world!")
}
}
val foo = Foo()
foo.something()
- This is the same as the previous snippet, except that
something()
is marked as private. - The
will not compile
, as something() is not visible to code outside of Foo:
- Private
properties and functions
can be referenced by other properties and functionswithin the same class
.
class Foo {
private fun something() {
println("Hello, world!")
}
fun somethingElse() {
something()
}
}
val foo = Foo()
foo.somethingElse()
- This works, because we are calling
somethingElse()
, which ispublic
(by default). - While code outside of Foo cannot call
something()
,somethingElse()
can callsomething()
, as both of them as part of Foo.
- The
protected
visibility modifier works a lot likeprivate
, but it also allows subclasses to access theproperty
orfunction
:
open class Foo {
protected fun something() {
println ("Hello, world!")
}
}
class Bar: Foo() {
fun somethingElse() {
something()
}
}
val notFoo = Bar()
notFoo.somethingElse()
- This works, because
somethingElse()
is public by default, and Bar can access the theprotected
function inside of Foo. - However, code outside of Foo and Bar cannot access that protected function, and so this fails:
open class Foo {
protected fun something() {
println("Hello, world!")
}
}
class Bar: Foo() {
fun somethingElse() {
something()
}
}
val notFoo = Bar()
notFoo.something()
So far, we have looked at how private and protected work with aspects of classes: (1)
properties
and (2)functions
.
- In case of
protected
, that is the only place you can use that particular visibility.-
protected
is defined as "accessible by this class and its subclasses", so it only makes sense in the context of classes. - It is also transitive.
-
- In case of
private
, though, is not just available for aspects of classes.-
Classes themselves can be
private
. -
Properties and functions defined
outside of classes
can also be marked asprivate
.
-
Classes themselves can be
- In these cases, the determination of what can and cannot access the private items is based on the file:
-
private
top-level things are visible to other things in the same Kotlin source file. -
private
top-level things are inaccessible to other things in other Kotlin files.
-
- Anything left as public (the default) is accessible by other files.
- Anything protected is accessible only by subclasses implemented in other files.
- Anything marked as private is inaccessible in other files.
The closest thing that Java has to this sort of
private
rule is its default "package-private" visibility.
- By default, any class, method, or field is visible to other code in the same Java package, as determined by the
package
statement.- Code in other packages cannot access that package-private code.
- Kotlin does not offer package-private visibility.
- While packages can be useful for code organization, and may be important when
interoperating with Java code
, kotlin does not use them to control visibility.
There is a fourth visibility option in Kotlin, called
internal
.
- For most simple projects, internal is useless and is equivalent to being public.
- Where
internal
comes into play is when your project is divided into a collection of some type of "modules".- For example, in an
Android project
, by default you have asingle module (app)
, but you can elect to define additional modules. - The precise definition of "module" depends entirely on
where the Kotlin code is being used
(e.g., an Android project) and is not defined by the language itself.
- For example, in an
- What is defined is what
internal
means:- Anything marked as internal is visible to code in the same module but is not visible to code in other modules.
- Visibility controls who can see what.
- Scope controls where things exist.
- This has an indirect impact on visibility: if something does not exist, it cannot be accessed by anything.
- This has an indirect impact on visibility: if something does not exist, it cannot be accessed by anything.
- However, scope more directly controls things like
garbage collection
, as scope helps to determine thelifetime of objects
.
- Top-level definitions in a Kotlin source file are global in scope.
-
Immutable globals - classes, val, etc. - will exist for the life of the
process
that is executing the Kotlin code. -
Mutable globals - such as var:
- Have references that will live forever.
- But the objects that those references reference might come and go as the value of that reference changes.
class Foo {
fun something() = println("Hello, world!")
}
val foo = Foo()
foo.something()
var bar = Foo()
bar.something()
bar = Foo()
bar.something()
- Both the
Foo class
and thefoo property
areglobal
andimmutable
. -
bar
is considered to be global. - The difference is that
bar
ismutable
. - So while anything that bar points to cannot be garbage collected, once bar is pointing to something else (e.g., another instance of Foo), whatever bar used to point to can be garbage collected.
- Properties defined inside of a class have
instance
scopes. - Each instance of the enclosing class will have its own set of instance properties,
independent of any other instance
of that same class.
class Foo(val count: Int)
val instanceOne = Foo(1)
val instanceTwo = Foo(2)
println(instanceOne.count)
println(instanceTwo.count)
Any
val
orvar
within something smaller than a class is some form of local variable.
A
val
orvar
directly inside of a function body is local to that function:
- Here, length is considered to be local to the
something()
function. - You would be unable to access
length
from other functions on Foo, let alone from outside Foo.
class Foo {
fun something(message: String) {
val length = message.length
println("'$message' has $length characters")
}
}
Foo().something()
A property defined within a block or lamda (i.e., chunk of code in {}) will be local to that block or lamda.
- Now, message is local not local to something(), but instead is local to the lamda expression supplied to
forEach()
. - You cannot access message from elsewhere within
something()
, let alone from other methods on Foo or from outside Foo.
class Foo {
fun something(messages: List<String>) {
messages.forEach{ message ->
val length = message.length
println("'$message' has $length charcters")
}
}
}
Foo().something(listOf("this", "is", "a", "test"))