Topic 1: Understanding OOPs Concepts from Scratch
The first topic of our Learn android from basic to advance series
Hello devs, I'm excited to share with you the first blog post in our Learn Android from Basic to Advanced series. Today, we'll be diving into the world of object-oriented programming concepts - trust me, they're super helpful when it comes to writing clean, secure, and organized code. So, let's get started!
Let's start with the oops concept this is the fundamental concept in Android development
OOPS Concept
Class
Object
Inheritance
Encapsulation
Polymorphism
Abstraction
Class
Class is a blueprint of our application. In Android, we create classes to define the structure and behaviour of various components such as activity, fragment and custom view classes.
class Person(val name: String, var age: Int) {
fun speak() {
println("Hello, my name is $name and I am $age years old.")
}
}
In this example, Person
is a class with properties name
and age
, and a method speak()
.
Object
So in Object Oriented Programming, everything is treated as an object. It means that UI elements like buttons text fields and even the app are represented as objects with properties and behaviours.
class Person(val name: String, var age: Int) {
fun speak() {
println("Hello, my name is $name and I am $age years old.")
}
}
fun main() {
val person1 = Person("John", 30)
val person2 = Person("Alice", 25)
person1.speak()
person2.speak()
}
We create two Person
objects (person1
and person2
) and call the speak()
method on each object.
Inheritance
Inheritance is like passing down traits in a family. We create two classes one is a superclass or parent class and the other one is a derived class or subclass and the subclass inherits the properties and behaviour of the superclass.
open class Animal(val name: String) {
open fun makeSound() {
println("Animal $name makes a sound.")
}
}
class Dog(name: String) : Animal(name) {
override fun makeSound() {
println("Dog $name says: Woof!")
}
}
fun main() {
val dog = Dog("Buddy")
dog.makeSound()
}
In this example, Animal
is a superclass with a property name
and a method makeSound()
. Dog
is a subclass of Animal
, inheriting the name
property and overriding the makeSound()
method.
Encapsulation
Encapsulation is a concept of bundling data and methods in a single unit or a class. For encapsulations, we use access modifiers. Using this access modifier we can control who can access or use our data and methods throughout the project. There are four access modifiers that we use :
Access Modifier | Uses |
Default | The default modifier allows accessibility in the same package but it is not allowed to be used in other packages. |
Public | A public modifier allows accessibility from anywhere in our code |
Private | A private modifier allows accessibility within the same class |
Protected | Protected modifier allows accessibility within the same class and subclasses |
// Class demonstrating encapsulation and access modifiers
class Employee(private val name: String, var age: Int) {
private var salary: Double = 0.0
protected var department: String = "HR"
var employeeID: Int = 0 // No access modifier, making it package-private
fun displayInfo() {
println("Name: $name, Age: $age, Salary: $salary, Department: $department, EmployeeID: $employeeID")
}
// Public method to set salary
fun setSalary(amount: Double) {
if (amount >= 0) {
salary = amount
} else {
println("Invalid salary amount.")
}
}
}
// Subclass inheriting from Employee class
class Manager(name: String, age: Int) : Employee(name, age) {
fun changeDepartment(newDepartment: String) {
department = newDepartment
}
}
fun main() {
val employee = Employee("John", 30)
employee.setSalary(50000.0)
employee.displayInfo()
val manager = Manager("Alice", 35)
manager.changeDepartment("Finance")
manager.employeeID = 1001
manager.displayInfo()
// println(employee.salary) // Error: 'salary' is private and cannot be accessed from outside the class
// println(employee.department) // Error: 'department' has protected visibility and cannot be accessed from outside the class hierarchy
}
In this example code we define a class Employee
that represents an employee in an organization. The class encapsulates the employee's name
, age
, salary
, department
, and employeeID
. These properties are accessed and modified through methods to ensure proper encapsulation and data integrity.
The
name
andage
properties are private and can only be accessed within theEmployee
class.The
salary
property is private and can only be accessed and modified within theEmployee
class.The
department
property is protected, allowing access within the class hierarchy.The
employeeID
property has no access modifier, making it package-private, meaning it can only be accessed within the same package.
Polymorphism
Polymorphism means one thing has many forms. That means we create one interface or abstract class and use this in many places.
There are two types of polymorphism :
Runtime Polymorphism
Compile Time Polymorphism
Runtime Polymorphism
When a method in a subclass has the same name and type signature as a method in its superclass then its call function overriding or runtime polymorphism. It is also called late binding or dynamic binding.
open class Animal {
open fun sound() {
println("Animal makes a sound")
}
}
class Dog : Animal() {
override fun sound() {
println("Dog barks")
}
}
class Cat : Animal() {
override fun sound() {
println("Cat meows")
}
}
fun main() {
val animal1: Animal = Dog()
val animal2: Animal = Cat()
animal1.sound() // Output: Dog barks
animal2.sound() // Output: Cat meows
}
In this example, both Dog
and Cat
classes override the sound()
method inherited from the Animal
class. At runtime, when calling the sound()
method on animal1
and animal2
, the overridden method in each respective subclass (Dog
and Cat
) gets executed.
Compile time polymorphism
A class having multiple methods by the same name but different parameters is known as method overloading or compile time polymorphism. It is also called early binding or static binding.
class Calculator {
fun add(a: Int, b: Int): Int {
return a + b
}
fun add(a: Double, b: Double): Double {
return a + b
}
}
fun main() {
val calculator = Calculator()
val sum1 = calculator.add(3, 4) // Invokes add(int, int) method
val sum2 = calculator.add(2.5, 3.5) // Invokes add(double, double) method
println("Sum1: $sum1") // Output: Sum1: 7
println("Sum2: $sum2") // Output: Sum2: 6.0
}
In this example, the Calculator
class has two add()
methods with different parameter types (Int
and Double
). At compile-time, the appropriate method to be invoked is determined based on the method signature and the arguments passed.
Abstraction
Abstraction means hiding the implementation details and showing only functionality. We use abstract class and interface for abstraction.
Abstract class
An abstract class is a special class that has both normal and abstract methods and variables. Abstract methods are the methods that have no code body. We can not create an object for an abstract class to use the abstract class we need to inherit from another class where we have to provide the implementation for that abstract method.
// Abstract class defining methods for a Shape
abstract class Shape {
abstract fun area(): Double
abstract fun perimeter(): Double
}
// Concrete implementation of Circle shape
class Circle(private val radius: Double) : Shape() {
override fun area(): Double {
return Math.PI * radius * radius
}
override fun perimeter(): Double {
return 2 * Math.PI * radius
}
}
// Concrete implementation of Rectangle shape
class Rectangle(private val width: Double, private val height: Double) : Shape() {
override fun area(): Double {
return width * height
}
override fun perimeter(): Double {
return 2 * (width + height)
}
}
fun main() {
val circle = Circle(5.0)
println("Circle area: ${circle.area()}")
println("Circle perimeter: ${circle.perimeter()}")
val rectangle = Rectangle(4.0, 6.0)
println("Rectangle area: ${rectangle.area()}")
println("Rectangle perimeter: ${rectangle.perimeter()}")
}
In this example, Shape
is an abstract class with abstract methods area()
and perimeter()
. Classes Circle
and Rectangle
extend the Shape
abstract class and provide their implementations for these methods.
Interface
Interface is one type of reference type. Interface is a collection of abstract methods. Interface is worked as multiple inheritance. Interface can extend other interfaces but cannot extend an abstract class. For the inherited interface we use Implementation but for interfaces that can inherit another interface, we use the Extends keyword.
// Interface defining methods for a Shape
interface Shape {
fun area(): Double
fun perimeter(): Double
}
// Concrete implementation of Circle shape
class Circle(private val radius: Double) : Shape {
override fun area(): Double {
return Math.PI * radius * radius
}
override fun perimeter(): Double {
return 2 * Math.PI * radius
}
}
// Concrete implementation of Rectangle shape
class Rectangle(private val width: Double, private val height: Double) : Shape {
override fun area(): Double {
return width * height
}
override fun perimeter(): Double {
return 2 * (width + height)
}
}
fun main() {
val circle = Circle(5.0)
println("Circle area: ${circle.area()}")
println("Circle perimeter: ${circle.perimeter()}")
val rectangle = Rectangle(4.0, 6.0)
println("Rectangle area: ${rectangle.area()}")
println("Rectangle perimeter: ${rectangle.perimeter()}")
}
In this example, the Shape
interface defines methods area()
and perimeter()
. Classes Circle
and Rectangle
implement the Shape
interface and provide their implementations for these methods.
Both examples demonstrate abstraction, allowing us to define a common interface or structure for different types of shapes while leaving the specific implementations to the concrete classes.
Difference between Abstract class and interface
Abstract class | Interface |
A class can extend only one abstract class | A class can extend multiple interfaces |
Abstract classes have both methods normal and abstract | Interface has only abstract methods as default |
The abstract class have variables | The interface cannot have any type of variable |
In the abstract class, we use any access modifier for the variables and for the method, we use public or protected | The interface is public by default, which means it does not use other modifiers |
We've wrapped up the blog post now. Just wanted to give you a heads-up that our next topic in this series is all about Lifecycles! Can't wait to catch up with you on the next blog, Devs.
Connect with Me:
Hey there! If you enjoyed reading this blog and found it informative, why not connect with me on LinkedIn? π You can also follow my Instagram page for more mobile development-related content. π²π¨βπ» Letβs stay connected, share knowledge and have some fun in the exciting world of app development! π