Interface
An interface is a blueprint for a class. An interface defines a set of abstract methods (methods without a body) and possibly some constants (static final fields). It provides a way to declare the behavior that implementing classes should adhere to, without providing the implementation details.
In Kotlin, you define an interface using the interface
keyword:
interface MyInterface {
// Abstract method declaration
fun doSomething()
// Property declaration (can be abstract or have a default implementation)
val property: Int
// Default implementation for a method (optional)
fun defaultMethod() {
println("Default implementation of defaultMethod")
}
}
Let’s break down the syntax elements:
interface
: Keyword indicating the declaration of an interface.MyInterface
: Name of the interface.
Inside the interface body, you can declare abstract methods (methods without a body), properties (abstract or with a default implementation), and provide default implementations for methods.
To implement this interface in a class, you can use the : InterfaceName
syntax:
class MyClass : MyInterface {
override fun doSomething() {
println("Doing something")
}
override val property: Int
get() = 42
}
In this example, MyClass
implements MyInterface
and provides concrete implementations for the abstract method doSomething
and the property property
Here are some key points about Interface in Kotlin:
Abstract Methods:
Interfaces can contain abstract methods(methods without a body). Classes implementing the interface must provide concrete implementations for these methods.Properties:
Interfaces can declare properties, which can be either abstract or have default implementations. Properties declared in interfaces are abstract by default.Multiple Inheritance:
In many programming languages, including Kotlin and Java, a class can implement multiple interfaces. This enables a form of multiple inheritance.Default Implementations:
Starting from Kotlin 1.0, interfaces can provide default implementations for methods, allowing implementing classes to use the default implementation or override the method.Companion Objects:
An interface can have a companion object, allowing it to declare properties and functions that are associated with the interface itself rather than an instance of the implementing class.Functional Interfaces:
Kotlin interfaces can represent functional interfaces, which are interfaces with a single abstract method. These can be used for functional programming constructs like lambda expressions.Constants:
Interfaces can declare constants usingval
declarations in companion object, which can be accessed using the interface name.Type Hierarchy:
Interfaces can be used to define common behavior and establish a type hierarchy, facilitating polymorphism.
Example of Interface in Kotlin
Let’s consider a real-world example of using interfaces in Kotlin within the context of a simple media player application. We’ll define an interface MediaPlayer
that represents the common functionalities expected from various types of media players.
// Interface defining the MediaPlayer contract
interface MediaPlayer {
fun play()
fun pause()
fun stop()
fun skipToNext()
fun skipToPrevious()
}
// Class representing a basic audio player
class AudioPlayer : MediaPlayer {
override fun play() {
println("Audio player is playing.")
}
override fun pause() {
println("Audio player is paused.")
}
override fun stop() {
println("Audio player is stopped.")
}
override fun skipToNext() {
println("Skipping to the next track in the audio player.")
}
override fun skipToPrevious() {
println("Skipping to the previous track in the audio player.")
}
}
// Class representing a basic video player
class VideoPlayer : MediaPlayer {
override fun play() {
println("Video player is playing.")
}
override fun pause() {
println("Video player is paused.")
}
override fun stop() {
println("Video player is stopped.")
}
override fun skipToNext() {
println("Skipping to the next video in the video player.")
}
override fun skipToPrevious() {
println("Skipping to the previous video in the video player.")
}
}
fun main() {
// Creating instances of the AudioPlayer and VideoPlayer
val audioPlayer = AudioPlayer()
val videoPlayer = VideoPlayer()
// Using the MediaPlayer interface to control both players
audioPlayer.play()
audioPlayer.skipToNext()
audioPlayer.pause()
videoPlayer.play()
videoPlayer.skipToPrevious()
videoPlayer.stop()
}
here is the output of this program
//Output
Audio player is playing.
Skipping to the next track in the audio player.
Audio player is paused.
Video player is playing.
Skipping to the previous video in the video player.
Video player is stopped.
In this example, the MediaPlayer
interface declares common methods like play()
, pause()
, stop()
, skipToNext()
, and skipToPrevious()
. The AudioPlayer
and VideoPlayer
classes then implement this interface, providing concrete implementations for each method.
In a media player application, using the MediaPlayer
interface lets you control both AudioPlayer
and VideoPlayer
objects in a consistent way. This promotes code reuse and flexibility by ensuring a common set of functionalities for different types of media players.
Example of an interface That includes abstract methods, default methods, static methods, abstract properties, default properties, constants, and private methods:
interface ExampleInterface {
// Abstract method
fun abstractMethod(): String
// Default method
fun defaultMethod(): String {
return "Default implementation of defaultMethod"
}
// Static method (companion object)
companion object {
// Constants
const val CONSTANT_VALUE: String = "Constant value"
fun staticMethod(): String {
return "Static method implementation"
}
}
// Abstract property
val abstractProperty: String
// Default property to provide defalt value
val defaultProperty: String
get() = "Default implementation of defaultProperty"
// Private method
private fun privateMethod(): String {
return "Private method implementation"
}
// Public method that uses private method
fun publicMethodUsingPrivate(): String {
return privateMethod() + " in public method"
}
}
// Example of a class implementing the interface
class ExampleClass : ExampleInterface {
override val abstractProperty: String
get() = "Implementation of abstractProperty"
override fun abstractMethod(): String {
return "Implementation of abstractMethod"
}
}
fun main() {
val exampleObj = ExampleClass()
// Using methods and properties from the interface
println(exampleObj.abstractMethod())
println(exampleObj.defaultMethod())
println(ExampleInterface.staticMethod())
println(exampleObj.abstractProperty)
println(exampleObj.defaultProperty)
println(ExampleInterface.CONSTANT_VALUE)
println(exampleObj.publicMethodUsingPrivate())
}
here is the output of this program
//Output
Implementation of abstractMethod
Default implementation of defaultMethod
Static method implementation
Implementation of abstractProperty
Default implementation of defaultProperty
Constant value
Private method implementation in public method
Let me explain the program
- Static Method , Constants – This is a static method and constants defined in the companion object of the interface. It can be called using the interface name
- Abstract Property (
abstractProperty
): This property is declared without an initial value. Classes implementing the interface must provide their own implementation for this property. Properties declared in interfaces are abstract by default. - Default Property (
defaultProperty
): This property has a default implementation in the interface. If a class implementing the interface doesn’t provide its own implementation, it will use this default implementation. - Private Method (
privateMethod
): This is a private method within the interface, and it can only be accessed within the interface itself.
Implementing multiple interfaces
In kotlin, a class can implement multiple interfaces by separating the interface names with commas. Here’s a simple example:
// Interface 1
interface Interface1 {
fun method1()
}
// Interface 2
interface Interface2 {
fun method2()
}
// Class implementing both interfaces
class MyClass : Interface1, Interface2 {
override fun method1() {
println("Method 1 implementation")
}
override fun method2() {
println("Method 2 implementation")
}
}
fun main() {
val myObject = MyClass()
myObject.method1()
myObject.method2()
}
// Interface 1
interface Interface1 {
fun method1()
}
// Interface 2
interface Interface2 {
fun method2()
}
// Class implementing both interfaces
class MyClass : Interface1, Interface2 {
override fun method1() {
println("Method 1 implementation")
}
override fun method2() {
println("Method 2 implementation")
}
}
fun main() {
val myObject = MyClass()
myObject.method1()
myObject.method2()
}
/*
//Output
Method 1 implementation
Method 2 implementation
* */
Keep in mind that if both interfaces declare a method with the same name, the implementing class must provide a concrete implementation for that method, resolving the potential conflict. Let’s consider an example where two interfaces declare a method with the same name:
// Interface 1
interface Interface1 {
fun commonMethod()
}
// Interface 2
interface Interface2 {
fun commonMethod()
}
// Class implementing both interfaces
class MyClass : Interface1, Interface2 {
override fun commonMethod() {
println("Concrete implementation of commonMethod")
}
}
fun main() {
val myObject = MyClass()
myObject.commonMethod()
}
/*
Output:
Concrete implementation of commonMethod
* */
Use cases of interfaces in Android
- Callback Mechanisms: Use interfaces for callbacks, allowing one component to notify another about events, like asynchronous task completion.
- Achieving Multiple Inheritance: In languages without direct multiple inheritance, interfaces help a class inherit from multiple sources.
- Enforcing Design Patterns: Observer and Factory Patterns: Interfaces are crucial for implementing Observer and Factory design patterns, defining contracts for object interaction and creation.
- Fragment Communication: Interfaces define communication contracts between fragments and their hosting activities, allowing decoupled interactions.
- Event Handling (e.g., Clicks): Interfaces define contracts for event handling, like item clicks in RecyclerViews, promoting consistency.
- Adapter Communication: Interfaces facilitate communication between adapters and hosting components, like handling item clicks in RecyclerViews.
- Dependency Injection: Interfaces provide contracts for dependency injection, promoting flexibility and testability in component interactions.
- Observer Pattern: Interfaces are key in implementing the Observer Pattern, where an object maintains a list of its dependents (observers) that are notified of state changes. The observers implement a common interface defining the update method.
- Retrofit Service: Interfaces define contracts for Retrofit services, specifying API endpoints and serving as blueprints for HTTP requests.
To learn more about Kotlin interfaces, check out the official documentation at: Kotlinlang