Interface in Kotlin and when to use it

Kotlin allows Interface to have code which means a class can implement an Interface, and inherit the behavior from it.


1*PG7q_78CFDUWF4Q8z247Lw.png 46.99 KB

After using Kotlin in Android development for a while, I’ve just realized the benefit of Interface in Kotlin. In Java 6, Interface can only be used to describe the behaviors, but not implement them.

Fortunately, Kotlin allows Interface to have code which means a class can implement an Interface, and inherit the behavior from it.

To begin with, if you come from Java, you might have an interface like this.

interface FlyingVehicle {
  fun startEngine()
}

Let’s have Aeroplane and Helicopter implement the Interface like this.

class AeroPlane : FlyingVehicle {
  val engine: Engine = Engine()
  override fun startEngine() = engine.start()
}

class Helicopter : FlyingVehicle {
  val engine: Engine = Engine()
  override fun startEngine() = engine.start()
}

Nothing wrong with them right? However, as Kotlin allows us to have code in the Interface, we can define a property to be overridden and implement functions in the Interface like this

interface FlyingVehicle {
  val engine: Engine
  fun startEngine() = engine.start()
}

Then we just have to override the property in our classes like the following.

class AeroPlane : FlyingVehicle {
  override val engine: Engine = Engine()
}

class Helicopter : FlyingVehicle {
  override val engine: Engine = Engine()
}

When we want to start the engine of our Aeroplane or Helicopter, we can directly call the function like this!

val aeroPlane = AeroPlane()
val helicopter = Helicopter()

aeroPlane.startEngine()
helicopter.startEngine()


You may wonder how could we use this in action (more than just start the engine of aeroplane and helicopter)

I’ll take the example derived from part of the book called Kotlin for Android Developer by Antonio Leiva. It’s a really good book for every Android Developer who wants to start using Kotlin.

Basically, we are going to create an interface that helps us
  • Set the toolbar title
  • Handle the click on the setting menu


interface SettingToolbarManager {
  val toolbar: Toolbar
  
  var toolbarTitle: String
    get() = toolbar.title.toString()
    set(value) {
      toolbar.title = value
    }
  
  fun setOnMenuClickListener(onSettingClicked: () -> Unit) {
    toolbar.inflateMenu(R.menu.menu_setting)
    toolbar.setOnMenuItemClickListener {
      when(it.itemId) {
        R.id.action_setting -> onSettingClicked()
        else -> {}
      }
    }
  }
  
}

Then in your MainActivity or other activities that need the setting menu, we can make them implement our interface.


class MainActivity : AppCompatActivity(), SettingToolbarManager {
  
  override val toolbar by lazy { findViewById<Toolbar>(R.id.toolbar) }
  
  override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    setContentView(R.layout.main_activity)
    
    toolbarTitle = "My Main Activity"
    setOnMenuClickListener {
      //Do what you want here
    }
  }
  
}

As you can see that the interface helps us separate some common code from activities and it makes your code look a bit cleaner. Moreover, we can reuse our interface in multiple places so that we don’t need to write the same code over and over again.


However, the interface is not always a suitable solution to simplify our code. Let’s look at the next example.

Let’s create an interface that helps us simplify the process of creating Toast

interface ToastManager {
   val context: Context
 
   fun shortToast(message: String) {
       Toast.makeText(context, message, Toast.LENGTH_SHORT).show()
   }
  
   fun longToast(message: String) {
       Toast.makeText(context, message, Toast.LENGTH_LONG).show()
   }
}

An activity that implements the interface might look like this.

class ToastActivity : AppCompatActivity(), ToastManager {
   override val context = this
 
   override fun onCreate(savedInstanceState: Bundle?) {
      super.onCreate(savedInstanceState)
      shortToast("This is short toast")
   }
  
}


Everything seems to be fine with the above example. The ToastManager is doing its job well, but in this case, we have an easier solution. We can use Kotlin Extension!!

Instead of creating the interface like what we have done, we could write extensions functions.

fun Activity.shortToast(text: String) {
    Toast.makeText(this, text, Toast.LENGTH_SHORT).show()
}

fun Activity.longToast(text: String) {
    Toast.makeText(this, text, Toast.LENGTH_LONG).show()
}


As you can see in the example below that without implementing an interface and overriding context, we can access the functions longToast because our ToastActivity has extended AppCompatActivity which consider being Activity instance.

class ToastActivity : AppCompatActivity() {
 
   override fun onCreate(savedInstanceState: Bundle?) {
      super.onCreate(savedInstanceState)
      longToast("This is long toast")
   }
  
}


Discussion & Conclusion


Interface
vs Extension

In my opinion, if the functionality you want to achieve is common to every instance of the particular Class, using Extension is a way to go. However, when you want behavior that is more specific, Interface is considered to be more appropriate like SettingToolbarManager in the above example.

This is just my own thought. Please suggest if you don’t agree. :)

Anyway Interface is a great way to simplify your code and avoid repeating yourself.

Source


Like 141 likes
Ben
Mobile Developer at OOZOU in Bangkok, Thailand
Share:

Join the conversation

This will be shown public
All comments are moderated

Get our stories delivered

From us to your inbox weekly.