Kotlin object keyword - mariamaged/Java-Android-Kotlin GitHub Wiki

Kotlin - object Keyword

Most likely, there have been developers over the years who have wondered why object-oriented programming languages do no have any sort of object keyword.

Singletons

  • One way to declare singletons in Kotlin is to use a top-level val declaration:
class Transmogrifier {
	fun transmogrify() {
		// TODO
	}
}

val QUASI_SINGLETON = Transmogifier()
  • In truth, though, we can have as many instances of Transmogrifier as we want.
  • QUASI_SINGLETON is merely a global instance of Transmogrifier.
  • We cannot prevent others from making their own Transmogrifier instances, as then we could not create the quasi-singleton.
     
  • For example, we cannot make the constructor be private, like this:
class Transmogrifier private constructor() {
	fun transmogrify() {
	}
}

val QUASI_SIGNLETON = Transmogrifier()
QUASI_SINGLETON.transmogrify()
  • This results in a compiler error.
     

enter image description here

  • To create a true singleton, all we need to do is:
    • Replace the class with object.
    • Call functions on that object based on its name, rather than creating some instance ourselves.
object Transmogrifier {
	fun transmogrify() {
		// TODO
	}
}

Transmogrifier.transmogrify()
  • An object declared this way:
    • Can have properties.
    • Can have functions.
    • Cannot have a constructor, which makes sense, since there will only be one instance.
    • However, if it extends classes, it can and must call the superclass constructors.
class Base {
	// TODO stuff here
}

object Transmogrifier: Base() {
	fun transmogrify() {
		// TODO
	}
}
Transmogrifier.tranmogrify()
  • As with singletons in any programming language, though, the ones that we declare via object will live for the life of our process.
    • So be careful that you are not creating a memory leak by having an object holding onto more and more stuff over time.

Companion Objects

Another use of the object keyword is for a companion of a class.

  • A companion is a singleton, associated with a class, whose functions somewhat fill the role that static methods do in Java.
    • In fact, if you set up those functions the right way, they can be called from Java as statics methods.

Declaring and Using a Companion Object

  • A companion object is simply:
    • An object.
    • Nested inside of a class.
       
  • Then, other parties can call the companion object's functions as if they were "static" functions on the enclosing class.
class Thingy {
	companion object {
		fun something() {
		// Do something
	}
  }
}

Thingy.something()

Um, Why Bother?

  • After all, we can have top-level functions in Kotlin:
fun doSomething() {
	// TODO
}
something()
  • However, companion objects have one key advantage:
    • They have access to private functions and properties of their enclosing class.
    • Even though count is private, doSomething() can still reference it, since doSomething() is part of the Thingy implementation.
class Thingy {
	private val count = 1 

	companion object {
		fun doSomething(thing: Thingy) {
			println(thing.count)
	}
	}
}

Thingy.doSomething(Thingy())
  • By contrast, a top-level function cannot reference private functions and properties so this fails with compile error:
class Thingy {
	private val count = 1
}

fun doSomething(thing: Thingy) {
	printl (thyingy.count)
}

Naming a Companion Object

Sometimes, you will see a companion object have a name, though probably not one named after after a companion.

  • This has limited impact on your Kotlin code - you still call functions on the class as before.
  • It does have an impact if you are going to try using the companion object functions from Java.
class Thingy {
	private val count = 1

	companion object AmyPond {
		fun doSomething(thing: Thingy) {
			println(thingy.count)
	  } 
   }
}

Thingy.doSomething(Thingy())

Nested Objects

A companion object is just a specialized case of nested objects, where we have "singletons" declared inside of a class.

Object Expressions

  • Frequently, we need to:
    • Pass an instance of an object to some function.
    • Where we need a tailored instance of the desired class.
    • With custom functionality.
       
  • In Java, this is where the "anonymous inner class" comes into play.
  • To find out when the UI element (a View) gets clicked, we can call setOnClickListener(), providing an implementation of an OnClickListener interface.
  • Rather than create a standalone class, though, we use anonymous inner class syntax to:
    • Create a class.
    • And a single instance of that class.
void clickyClickyClicky(View view) {
	view.setOnClickListener(new OnClickListener() {
		@Override
		public void onClick(View v) {
			// TODO something useful.
		}
	});
}
  • The Kotlin equivalent of this syntax is the "object expression".

Basic Declaration

  • The Kotlin equivalent of the View and OnClickListener from above might look like this.
  • Technically, in Android, OnClickListener is a nested interface inside of View.
interface OnClickListener {
	fun onClick(v: View)
}

class View {
	fun setOnClickListener(listener: OnClickListener) {
		// TODO something with this.
	}
}
  • When we want to call setOnClickListener(), we could create a standalone class that implements the OnClickListener interface.
  • Or, we could use an object expression instead.
    1. The object keyword indicates that we are creating a single object.
    2. The : onClickListener indicates that this object will implement the onClickListener interface.
fun clickyClickClicky(view: View) {
	view.setOnClickListener(object: OnClickListener {
		override fun onClick(v: View) {
			// TODO something useful.
	}
})
}