DEV Community

Cover image for Mastering Kotlin's Object-Oriented Programming 101: Unleashing the Full Potential of Your Code.
Daniel Brian
Daniel Brian

Posted on

Mastering Kotlin's Object-Oriented Programming 101: Unleashing the Full Potential of Your Code.

Hellooooo🥳️🥳️🥳️I am glad🤗 that you have joined me again in this Android development journey🚀. I am glad that you have decide to join me again on this exciting journey as we explore the world of kotlin for Android development.

🚀Kotlin is a programming language that's gaining popularity due to its ability to combine object-oriented and functional programming paradigms.
🚀In this article, we'll explore Kotlin's object-oriented programming features and how they can be used to build efficient software applications. Whether you're a seasoned developer or just starting out, come with us on this journey to discover why Kotlin is worth learning.

In this article, we are going to deep dive into one of the most important concepts of Kotlin:

1. Functions
2. Classes and Objects
3. Interfaces and Inheritance
4. Class Inheritance

1. Functions

  • Functions help in writing the code once and re-using it in other parts of your code.

kt functions

  • Functions are used to break the code into smaller, more understandable, and reusable parts.

Variable scope and local functions

  • Variables defined inside of a function are called local variables. They are visible only in their defined scope.
  • These variables can be used in the functions they are defined.
fun firstFunction () {
val name = "Google Office Zurich"
    println(name)
}
fun CarType () {
    val car = "Bently"
    firstFunction()
    println(car)
}
fun main () {
    CarType()
}

Enter fullscreen mode Exit fullscreen mode

The output of the above code after calling the function CarType() in the main function is Google Zurich Office and Bently. firstFunction cannot be used to access the type of car becaude it is mentioned outside its scope.

  • Local functions are defined inside other functions. They can only be used in their scope of the definition.
  • Top-level functions do not have such limitations they can be used everywhere even in functions defined earlier in a file.
  • Below is an example of a top-level function
fun calculateCircleArea(radius: Double): Double {
    return Math.PI * radius * radius
}
fun main () {
val area = calculateCircleArea(2.0)
println("The area of the circle is $area")
}
Enter fullscreen mode Exit fullscreen mode

The output of the above code is 12.566370614359172

Functions with Parameters and result

  • Parameters are named variables that is defined as part of a function. They are specified using parenthesis after the function name.
  • When you declare or write a function and you use as a parameter, when you want to call this function, you must supply the parameter with a value. The value supplied for this parameter is called Argument
fun calculateCircleArea(radius: Double): Double {
    return Math.PI * radius * radius
}
fun main () {
val area = calculateCircleArea(2.0)
println("The area of the circle is $area")
}
Enter fullscreen mode Exit fullscreen mode

In the above code sample the name radius: Double is the parameter of the function calculateCircleArea. The Argument supplied for the calculateCircleArea function is 2.0

  • An absolete function is a function that returns an integer value without a sign so this doesn't change positive values, but it returns all negative values into positive.
fun absolete(value : Int) :Int {
    if(value >= 0){
        return value
    }else{
        return -value
    }
}
fun main () {
    println(absolete(123)) // output 123 
    println(absolete(-1)) // output 1
    println(absolete(9)) // output 9
}
Enter fullscreen mode Exit fullscreen mode

Single expression functions

  • In Kotlin, there is a special syntax for single expressions functions. Instead of braces with the body, you can use the equality sign, which specifies what should be returned.
fun triangleArea(width : Double, height : Double) : Double = width * height/2
fun main () {
val area = triangleArea(12.0,56.0)
println(area)
}
Enter fullscreen mode Exit fullscreen mode

The ouput of the above code is 336.0

Recursion

  • These are functions that call themselves.
  • A factorial function is an example of a recursive function. To implement such a function we might define a variable called accumulator with an initial value of 1, which will be used to accumulate all the numbers. Then, for each number from 1 to the number you calculate the factorial, you will modify the value by multiplying it by the next number.
  • After that, all you need to do is to return the accumulator variable.
fun factorial(number : Int) : Int {
    var accumulator = 1
    for(i in 1..number){
       accumulator = accumulator * i 
    }
   return accumulator 
}
fun main () {
    println(factorial(5))
}
Enter fullscreen mode Exit fullscreen mode

Default and Named arguments

  • Default arguments that need not specify explicitly while calling a function. When a function is called without passing arguments the parameters become the default arguments.
fun student(name: String="Praveen", standard: String="IX" , roll_no: Int=11) {  
    println("Name of the student is: $name")
    println("Standard of the student is: $standard")
    println("Roll no of the student is: $roll_no")
}
fun main() {
    student()        
}
Enter fullscreen mode Exit fullscreen mode

The output of the above code is Praveen IX 11 since we have not passed any arguments in our student function in the main function.

  • Named arguments allows to specify arguments while calling a function. With named arguments, you can pass arguments to a function in any order, as long as you specify the name of each argument.
fun student( name: String="Praveen", standard: String="IX" , roll_no: Int=11 ) {
    println("Name of the student is: $name")
    println("Standard of the student is: $standard")
    println("Roll no of the student is: $roll_no")
}

fun main(args: Array<String>) {
    val name_of_student = "Gaurav"
    val standard_of_student = "VIII"
    val roll_no_of_student = 25
student(name=name_of_student,roll_no=roll_no_of_student)
}
Enter fullscreen mode Exit fullscreen mode

For this code to work we pass arguments with names as defined in function.

2. Classes and Objects

  • A class is like a template that defines what an object looks like and how it can be used.
  • It specifies how the objects should be created and which methods and properties it consists of.

Image kt obj

  • An object is an instance of a class. A type represents a category of objects eg an integer represents all integers.
  • To define a class in kotlin you use the keyword class and specify the name of the class.
class Car {
    var brand = ""
    var model = ""
    var year = 0
}
Enter fullscreen mode Exit fullscreen mode
  • You will realize that when we use curly braces to define a class we must initialize the objects, we cannot leave them open but when we use brackets we don't have to initialize the class we only specify the type.
  • When we use brackets to enclose the class we should use commas to separate different properties inside it.
class Car (
   var brand : String,
    var model : String,
    var year : Int
         )
Enter fullscreen mode Exit fullscreen mode

Creating an object

  • We can create an object of the Car class above called objCar, and then we can access the properties of objCar by using the dot syntax (.)
class Car (
   var brand : String,
    var model : String,
    var year : Int
         )
 fun main () {
     var objCar = Car(
     "Bently",
     "Mustang",
     2013
     )
      println( objCar.brand)
     println( objCar.model)
     println( objCar.year)
 }
Enter fullscreen mode Exit fullscreen mode
  • When we use the brackets to define the class we use the primary constructor property when specifying a value for a regular property.
  • When we use the curly braces we define the properties of the class not as parameters as shown below.
class Car {
    var brand : String = ""
    var model : String = ""
    var year : Int = 0
         }
 fun main () {
     var objCar = Car()

     objCar.brand = "Bently"
     objCar.model = "Mustang"
     objCar.year = 2013

     println( objCar.brand)
     println( objCar.model)
     println( objCar.year)
 }
Enter fullscreen mode Exit fullscreen mode
  • We can also specify the property of a class inside a class body. First, we use curly brackets as shown below.
class Car (
    var brand : String,
    var model : String
          )
{
    var ObjCar : String = "$brand($model)"
}
 fun main () {
     var CarType = Car( brand = "Bently", model = "Mustang")
     println(CarType.ObjCar)
            }
Enter fullscreen mode Exit fullscreen mode

The output of the above code will be Bently(Mustang)

Methods

  • A method is a function associated with a class, for example a defined class. They are defined inside the class body. In other words, they are defined inside the curly braces.
  • Methods have direct access to all the properties of a class as shown below
class Subject (val name : String)

{
    fun Teacher (course : String, grade : String) : Teaches {
        println("$name teaches $course well I got $grade grade")
        return Teaches(course,grade)
                             }
    fun TeachesWell (course : String, grade : String){
        println("Congratulations")
    //Methods can be used to call other methods    
        Teacher(course, grade)
               }
}

class Teaches (
    var course : String,
    var grade : String
              )

fun main () {
    val read = Subject ("Daniel")
    read.TeachesWell(course = "Physics", grade = "A")
}
Enter fullscreen mode Exit fullscreen mode
  • The output of the above code is

Congratulations
Daniel teaches Physics well I got A grade

  • In order to call a method, you need to have an object. For instance, when you have a class Dog with a method feed, to call this method outside the class, you need to first have an object of type Dog.
class Dog (var name: String)
{
    var hunger : Int = 62

    fun feed (){
        println("Feeding $name")
        hunger = 0
    }

}

fun main () {
    var dog = Dog ("Rex")
    dog.feed()
}
Enter fullscreen mode Exit fullscreen mode

The output of the above code is feeding Rex.

  • Inside the method, we use the nameand hunger properties from the object that was used during the call. In a way, It is similar to defining feed as a top-level function and parsing Dog as an argument.
  • In methods, you can access members implicitly. For instance in the below code use just name, not dog.name
class Dog (var name : String)
{
    var hunger : Int = 62
}

fun feed (dog : Dog){
     println("Feeding ${dog.name}")
     dog.hunger = 0
    }

fun main () {
    var dog = Dog ("Rex")
    feed(dog)
}
Enter fullscreen mode Exit fullscreen mode

The output of the above code is feeding Rex.

  • When you call methods, the object of their class is passed to their class body. This is called a receiver.
  • A receiver can be accessed using this keyword also known as reciever reference. So if you want to reference an object used to call a method inside this method, use this keyword.
class Dog(var name : String){
    var hunger = 62

    fun feed (){
      //Receiver
      var currentDog : Dog = this
        println("Feeding ${currentDog.name}")
        currentDog.hunger = 0
    }
}

fun main (){
    var dog = Dog("Rex")
    dog.feed()
}
Enter fullscreen mode Exit fullscreen mode
  • when you call methods or properties inside methods, such as when you call name, you are calling methods or properties in the receiver, so its like calling this.name
class Dog(var name : String){
    var hunger = 62

    fun feed (){
      //Receiver
      //var currentDog : Dog = this
        println("Feeding ${this.name}")
        this.hunger = 0
    }
}

fun main (){
    var dog = Dog("Rex")
    dog.feed()
}
Enter fullscreen mode Exit fullscreen mode
  • this keyword can be used to call methods or properties and sometimes it can be used explicitly.
  • One example might be when you have a method parameter and class property with the same name. Like in the below class, there is a property name and a method changeName with a parameter name. So if you see name inside the class, you will have a value of the parameter. To access the property use this.name
class User (var name : String) {
    fun changeName (name : String){
        println("Change name from ${this.name} to $name")
        this.name = name
    }
}

fun main () {
    val user = User("Alpha")
    user.changeName("Beta")
    println(user.name)
}
Enter fullscreen mode Exit fullscreen mode

3. Interfaces and Inheritance

  • An interface is a collection of abstract methods and properties that define a common contract for classes that implement the interface.

Image kt interface

  • Interfaces cannot be instantiated directly.

Creating interfaces

  • Interface is defined by the keyword Interface followed by the name of the interface and curly braces.
  • Functions and properties of the interface reside inside the curly braces.
interface Fruit {
  fun  BitterFruit()
  fun SweetFruit()
}
Enter fullscreen mode Exit fullscreen mode

Implementing Interfaces

  • Interfaces are implemented by classes or objects.
  • Classes or objects implementing the interface should be followed by a colon and the name of the interface which is to be implemented. eg
interface Fruit {

   fun BitterFruit()
   fun SweetFruit()

}

class TypeFruit: Fruit {

    override fun BitterFruit() {
        println("Lemon is a bitter fruit")
    }
    override fun SweetFruit() {
        println("Apple is sweet fruit ")
    }
}

fun main () {
    val fruit = TypeFruit ()

    fruit.BitterFruit()
    fruit.SweetFruit()
}
Enter fullscreen mode Exit fullscreen mode
  • We are using the keyword override in our functions because they were specified in the interface.

Default values and Default methods

  • Methods can have default values as parameters which are used when no value is passed during the function call.
interface Sum {
    // 10 and 20 are default values
    fun add( firstNum : Int = 10,secondNum : Int = 20)
    fun output () {
        println("This is an example of an addition of numbers")
    }
}
class Addition (): Sum {
    override fun add (firstNum : Int, secondNum : Int){
        val SumOfNumbers = firstNum + secondNum
        println("The sum of $firstNum and $secondNum is $SumOfNumbers")
    }
    override fun output () {
        super.output()
        println("Sum of two numbers")
    } 
}
fun main () {
    val addition = Addition()
    //We are overriding the default values with new arguments
    addition.add(30,30)
    addition.output()
}
Enter fullscreen mode Exit fullscreen mode

The output is 60 and not 30. In our main function, we have changed our default values to hold 30 in both firstNum and secondNum.

  • The super keyword is used to call the default implementation of print specified in the inheritance.

Inheritance properties in interface

  • Interfaces can contain properties that cannot be instantiated hence no backfield to hold their values. The fields in the interface are either left abstract or are provided an implementation.

Image inheritance
-Interfaces can also inherit others interfaces adding its own properties and methods in both the interfaces.

  • An interface can inherit more than interface.
  • The value of length and breadth are the properties of the interface dimensions.
interface Dimensions {
    val length : Double
    val breadth : Double
}
interface CalculateParameters : Dimensions {
    fun area ()
    fun perimeter ()
}
class Calculations () : CalculateParameters {
    override val length : Double
    get() = 40.0
    override val breadth : Double
    get() = 60.0

    override fun area () {
        println("The area is ${length * breadth}")
    }
    override fun perimeter () {
        println("The perimeter is ${2*(length + breadth)}")
    }
}
fun main () {
    var obj = Calculations ()
    // The area is 2400.0
    obj.area()
    // The perimeter is 200.0
    obj.perimeter()
}
Enter fullscreen mode Exit fullscreen mode
  • Kotlin has multiple interface implementations. A class can implement more than one interface provided that it has a definition for all the numbers of the interface.
  • A class can only inherit one class.
interface Dimensions {
    val length : Double
    val breadth : Double
}
interface CalculateParameters {
    fun area ()
    fun perimeter ()
}
class Calculations () : Dimensions, CalculateParameters {
    override val length : Double
    get() = 40.0
    override val breadth : Double
    get() = 60.0

    override fun area () {
        println("The area is ${length * breadth}")
    }
    override fun perimeter () {
        println("The perimeter is ${2*(length + breadth)}")
    }
}
fun main () {
    var obj = Calculations ()
    // The area is 2400.0
    obj.area()
    // The perimeter is 200.0
    obj.perimeter()
}
Enter fullscreen mode Exit fullscreen mode

4. Class Inheritance

  • During class inheritance

Image kt inh
1. One must define a class that extends from another class.
2. The class to be inherited must be open.
3. One must introduce the same modifications to the behavior.

  • When one class inherits from another, it takes all the previous class methods and properties. It becomes its subclass, which means that it can be used wherever its parent is expected.
open class Klass () {
    fun students () {
        println("There are 50 students in the class")
    }
    fun subject () {
        println("The class takes chemistry as a compulsory subject")
    }
   }
class Teacher () : Klass() {
    fun teaches () {
        println("The teacher teaches all the 50 students chemistry")
    }

}
fun main () {
    val teach = Teacher ()
    teach.students()
    teach.subject()
}
Enter fullscreen mode Exit fullscreen mode
  • class Klass is the super or the primary class while class Teacher is the subclass or the secondary class.

Visibility Modifiers in Classes

  • To make encapsulation possible you need to hide some properties and methods so that they can be used inside of a class but not outside of it. To do that you use the private visibility modifier before a function or a property you want to hide.
  • Private means that a particular property or a function can only be used in that class.
  • Protected means that a property can be used inside classes that are open or abstract.
  • Internal means that an element will be visible throughout the same module.

💫💫Thank you for taking the time to read my article, I appreciate your interest and support.😇😇🥳🥳
Thank you

Top comments (0)