Interface and abstract class in Kotlin

Abstract classes and interfaces are two fundamental concepts in object-oriented programming (OOP) that are used to define contracts for classes that implement them. In Kotlin, both abstract classes and interfaces play an important role in building flexible and extensible software.

Abstract classes in Kotlin

An abstract class is a class that cannot be instantiated directly and is intended to be subclassed by other classes. An abstract class can have both abstract and concrete methods, and it can also have instance variables. The abstract methods declared in an abstract class do not have any implementation, and they must be implemented by any subclass that extends the abstract class.

An abstract class is declared using the abstract keyword, as shown in the following example:

abstract class Shape { 
    var color: String = "white" 
    abstract fun area(): Double 
}         

In this example, we have defined an abstract class called Shape that has a single property color and an abstract method area(). The area() method does not have an implementation, and it must be implemented by any subclass that extends the Shape class.

Advantages of abstract classes in Kotlin:

  • Abstract classes can provide a base implementation that can be reused by its subclasses.
  • Abstract classes can define abstract methods that enforce a contract for any subclass that extends the abstract class.
  • Abstract classes can have instance variables that are inherited by its subclasses.

Disadvantages of abstract classes in Kotlin:

  • Abstract classes cannot be instantiated directly, and they must be subclassed to be used.
  • A subclass can only extend one abstract class, which limits the flexibility of inheritance.

Suitable scenarios to use abstract classes in Kotlin:

  • When you want to define a base implementation that can be reused by its subclasses.
  • When you want to enforce a contract for any subclass that extends the abstract class.

Interfaces in Kotlin

An interface is a collection of abstract methods that define a contract for classes that implement them. In Kotlin, an interface can also declare default implementations for its methods. Unlike abstract classes, an interface cannot have instance variables and cannot provide any implementation details for its methods.

An interface is declared using the interface keyword, as shown in the following example:

interface Shape { 
    fun area(): Double 
    fun perimeter(): Double { 
        return 0.0 
    } 
}         

In this example, we have defined an interface called Shape that has two methods, area() and perimeter(). The area() method does not have an implementation, and it must be implemented by any class that implements the Shape interface. The perimeter() method has a default implementation that returns 0.0, and it can be overridden by any class that implements the Shape interface.

Advantages of interfaces in Kotlin:

  • Interfaces can define a contract for classes that implement them.
  • Interfaces can provide default implementations for their methods.
  • A class can implement multiple interfaces, which provides more flexibility than abstract classes.

Disadvantages of interfaces in Kotlin:

  • Interfaces can only declare methods and cannot provide any implementation details for their methods. <= This statement is not entirely true, interfaces cannot provide a complete implementation for their methods, they can provide default implementations that implementing classes can use as is or override with custom implementations.


Suitable scenarios to use interfaces in Kotlin:

  • When you want to define a contract for classes that implement the interface.
  • When you want to provide default implementations for the methods in the interface.
  • When you want to provide flexibility for a class to implement multiple interfaces.

Note: An abstract class can indeed have instance variable. These variables can be initialized in the constructor of the abstract class, and any subclass that extends the abstract class will inherit these instance variables.

On the other hand, an interface cannot have instance variables in the same way as an abstract class. However, Starting from Kotlin 1.5, interfaces can declare property accessors with backing fields, which effectively allow for the definition of instance variables in interfaces. This means that an interface can now have a property with a default value, and any implementing class can choose to override the property with a custom value.

Here is an example:

interface MyInterface { 
    val myVariable: String get() = "Default value" 
} 

class MyClass : MyInterface { 
    override val myVariable = "Custom value" 
} 

fun main() { 
    val myClass = MyClass() 
    println(myClass.myVariable) // Custom value 
}         

In this example, the interface MyInterface has a property accessor with a default implementation for myVariable. The class MyClass implements MyInterface and overrides myVariable with a custom value. When we create an instance of MyClass and call myVariable, we get the custom value instead of the default value.

So, while the statement is generally true, it is important to note that Kotlin interfaces can have instance variables through property accessors with backing fields.

Differences between abstract classes and interfaces in Kotlin

The main differences between abstract classes and interfaces in Kotlin are:

  • An abstract class can have instance variables, while an interface cannot have instance variables.<= this statement is true only until Kotlin 1.5.Even interfaces can also have instance variables
  • An abstract class can provide a base implementation for its methods, while an interface cannot provide any implementation details for its methods.<= this statement is also not entirely true as interfaces can have default implementation.
  • A class can only extend one abstract class, while it can implement multiple interfaces.
  • An abstract class can have both abstract and concrete methods, while an interface can only have abstract methods (with default implementations in Kotlin).
  • An abstract class can be used to define a hierarchy of related classes, while an interface can be used to define a set of unrelated classes that share a common behavior.

Choosing between abstract classes and interfaces in Kotlin

When choosing between abstract classes and interfaces in Kotlin, consider the following guidelines:

Use abstract classes when:

  • You want to define a base implementation that can be reused by its subclasses.
  • You want to enforce a contract for any subclass that extends the abstract class.
  • You want to have instance variables that are inherited by its subclasses.
  • You want to define a hierarchy of related classes.

Use interfaces when:

  • You want to define a contract for classes that implement the interface.
  • You want to provide default implementations for the methods in the interface.
  • You want to provide flexibility for a class to implement multiple interfaces.
  • You want to define a set of unrelated classes that share a common behavior.

// Abstract class example
abstract class Animal(val name: String) {
    
    // Concrete method
    fun introduction() {
        println("My name is $name")
    }
    
    // Abstract method, to be implemented by subclasses
    abstract fun makeSound()
}

// Subclass of Animal, which must implement makeSound()
class Dog(name: String) : Animal(name) {
    
    override fun makeSound() {
        println("Woof!")
    }
}

// Interface example
interface Shape {
    
    // Abstract method
    fun area(): Double
    
    // Default implementation
    fun perimeter(): Double {
        return 0.0
    }
}

// Class that implements Shape interface
class Rectangle(val length: Double, val width: Double) : Shape {
    
    // Implementation of area() method
    override fun area(): Double {
        return length * width
    }
    
    // Override of perimeter() method with custom implementation
    override fun perimeter(): Double {
        return 2 * (length + width)
    }
}

fun main() {
    val dog = Dog("Fido")
    dog.introduction() // My name is Fido
    dog.makeSound() // Woof!
    
    val rectangle = Rectangle(5.0, 3.0)
    println("Area of rectangle is ${rectangle.area()}") // Area of rectangle is 15.0
    println("Perimeter of rectangle is ${rectangle.perimeter()}") // Perimeter of rectangle is 16.0
}         

In the above example, we have an abstract class Animal which has a concrete method introduction() and an abstract method makeSound() which must be implemented by any subclass that extends Animal. We then have a subclass Dog which implements makeSound().

We also have an interface Shape which has an abstract method area() and a default implementation of perimeter(). We then have a class Rectangle which implements Shape and provides custom implementations for both area() and perimeter().

In the main() function, we create instances of Dog and Rectangle and call their respective methods.

Conclusion

Abstract classes and interfaces are essential concepts in Kotlin or any language that allow for building flexible and extensible software. Abstract classes provide a base implementation that can be reused by its subclasses, while interfaces define a contract for classes that implement them. When choosing between abstract classes and interfaces, consider the specific requirements of your project to determine which approach best suits your needs.


--------------------------------------------------------------------------

C++ Abstract class AND Interfaces class.

In C++, an abstract class is defined as a class that has at least one pure virtual function. A pure virtual function is a virtual function that is declared using the syntax "= 0" to indicate that it has no implementation in the abstract class and must be implemented in the derived classes that inherit from it.

Here is an example of an abstract class in C++:

class Shape {
public:
    virtual double getArea() const = 0; // pure virtual function
    virtual double getPerimeter() const = 0; // pure virtual function
};

class Rectangle : public Shape {
private:
    double width, height;
public:
    Rectangle(double w, double h) : width(w), height(h) {}
    double getArea() const override { return width * height; }
    double getPerimeter() const override { return 2 * (width + height); }
};

class Circle : public Shape {
private:
    double radius;
public:
    Circle(double r) : radius(r) {}
    double getArea() const override { return 3.14159 * radius * radius; }
    double getPerimeter() const override { return 2 * 3.14159 * radius; }
};

int main() {
    Shape* shape1 = new Rectangle(4, 5);
    Shape* shape2 = new Circle(2);
    std::cout << "Area of shape1: " << shape1->getArea() << std::endl;
    std::cout << "Perimeter of shape1: " << shape1->getPerimeter() << std::endl;
    std::cout << "Area of shape2: " << shape2->getArea() << std::endl;
    std::cout << "Perimeter of shape2: " << shape2->getPerimeter() << std::endl;
    delete shape1;
    delete shape2;
    return 0;
}        

In this example, Shape is an abstract class that defines the common interface for any shape class that inherits from it. It has two pure virtual functions, getArea and getPerimeter, that must be implemented in any derived class. Rectangle and Circle are concrete classes that inherit from Shape and implement their own versions of the getArea and getPerimeter functions.

An interface in C++ is defined using an abstract class with only pure virtual functions. Unlike Java or Kotlin, C++ does not have a specific keyword for defining an interface, so an interface is simply defined as an abstract class with only pure virtual functions.


Thanks for reading till end, please comment or suggest if you have any questions or suggestions.

prince S.

software Engineer

1 年

I liked how he paid extra attention on when to use which because with each update the gap in d/f is reducing among interfaces & Abstract classes

回复

要查看或添加评论,请登录

Amit Nadiger的更多文章

  • Rust modules

    Rust modules

    Referance : Modules - Rust By Example Rust uses a module system to organize and manage code across multiple files and…

  • List of C++ 17 additions

    List of C++ 17 additions

    1. std::variant and std::optional std::variant: A type-safe union that can hold one of several types, useful for…

  • List of C++ 14 additions

    List of C++ 14 additions

    1. Generic lambdas Lambdas can use auto parameters to accept any type.

    6 条评论
  • Passing imp DS(vec,map,set) to function

    Passing imp DS(vec,map,set) to function

    In Rust, we can pass imp data structures such as , , and to functions in different ways, depending on whether you want…

  • Atomics in C++

    Atomics in C++

    The C++11 standard introduced the library, providing a way to perform operations on shared data without explicit…

    1 条评论
  • List of C++ 11 additions

    List of C++ 11 additions

    Referance : C++11 - cppreference.com 1.

    2 条评论
  • std::lock, std::trylock in C++

    std::lock, std::trylock in C++

    std::lock - cppreference.com Concurrency and synchronization are essential aspects of modern software development.

    3 条评论
  • std::unique_lock,lock_guard, & scoped_lock

    std::unique_lock,lock_guard, & scoped_lock

    C++11 introduced several locking mechanisms to simplify thread synchronization and prevent race conditions. Among them,…

  • Understanding of virtual & final in C++ 11

    Understanding of virtual & final in C++ 11

    C++ provides powerful object-oriented programming features such as polymorphism through virtual functions and control…

  • Importance of Linux kernal in AOSP

    Importance of Linux kernal in AOSP

    The Linux kernel serves as the foundational layer of the Android Open Source Project (AOSP), acting as the bridge…

    1 条评论

社区洞察

其他会员也浏览了