Kotlin OOPs
Kotlin OOPs
Kotlin Class
Kotlin class is similar to Java class, a class is a blueprint for the objects which
have common properties. Kotlin classes are declared using keyword class.
Kotlin class has a class header which specifies its type parameters, constructor
etc. and the class body which is surrounded by curly braces.
class className{ // class header
// property
// member function
}
In above example, class className is an empty constructor. It is generated by
compiler automatically but if we want to provide a constructor, we need to write
a constructor keyword followed by class name as:
class className constructor(){ // class header
// property
// member function
}
var acc_no: Int = 0
var name: String? = null
var amount: Float = 0f
fun deposit() {
//deposite code
}
fun withdraw() {
// withdraw code
}
fun checkBalance() {
//balance check code
}
}
The account class has three properties acc_no, name, amount and three member
functions deposit(), withdraw(),checkBalance().
Kotlin Object
Object is real time entity or may be a logical entity which has state and
behavior. It has the characteristics:
Object is used to access the properties and member function of a class. Kotlin
allows to create multiple object of a class.
Create an object
Kotlin object is created in two steps, the first is to create reference and then
create an object.
var obj1 = className()
var obj1 = className()
var obj2 = className()
obj.deopsit()
obj.name = Ajay
Let's create an example, which access the class property and member function
using . operator.
class Account {
var acc_no: Int = 0
var name: String = ""
var amount: Float = 0.toFloat()
fun insert(ac: Int,n: String, am: Float ) {
acc_no=ac
name=n
amount=am
println("Account no: ${acc_no} holder :${name} amount :${amount}")
}
fun deposit() {
//deposite code
}
fun withdraw() {
// withdraw code
}
fun checkBalance() {
//balance check code
}
}
fun main(args: Array<String>){
Account()
var acc= Account()
acc.insert(832345,"Ankit",1000f) //accessing member function
println("${acc.name}") //accessing class property
}
Output:
Ankit
class outerClass{
//outer class code
class nestedClass{
//nested class code
}
}
class outerClass{
private var name: String = "Ashu"
class nestedClass{
var description: String = "code inside nested class"
private var id: Int = 101
fun foo(){
// print("name is ${name}") // cannot access the outer class member
println("Id is ${id}")
}
}
}
fun main(args: Array<String>){
// nested class must be initialize
println(outerClass.nestedClass().description) // accessing property
var obj = outerClass.nestedClass() // object creation
obj.foo() // access member function
}
Output:
Id is 101
Inner class is a class which is created inside another class with keyword inner.
In other words, we can say that a nested class which is marked as "inner" is
called inner class.
class outerClass{
//outer class code
inner class innerClass{
//nested class code
}
}
The advantage of inner class over nested class is that, it is able to access
members of outer class even it is private. Inner class keeps a reference to an
object of outer class.
class outerClass{
private var name: String = "Ashu"
inner class innerClass{
var description: String = "code inside inner class"
private var id: Int = 101
fun foo(){
println("name is ${name}") // access the outer class member even private
println("Id is ${id}")
}
}
}
fun main(args: Array<String>){
println(outerClass().innerClass().description) // accessing property
var obj = outerClass().innerClass() // object creation
obj.foo() // access member function
}
Output:
name is Ashu
Id is 101
Kotlin Constructor
In Kotlin, constructor is a block of code similar to method. Constructor is
declared with the same name as the class followed by parenthesis '()'.
Constructor is used to initialize the variables at the time of object creation.
1. Primary constructor
2. Secondary constructor
class myClass(valname: String,varid: Int) {
// class body
}
When the object of myClasss is created, it initializes name and id with "Ashu"
and "101" respectively.
class myClass(val name: String, var id: Int) {
}
fun main(args: Array<String>){
val myclass = myClass ("Ashu", 101)
println("Name = ${ myclass.name}")
println("Id = ${ myclass.id}")
}
Output:
Name = Ashu
Id = 101
The primary constructor does not contain any code. Initializer blocks are used to
initialization of code. This block is prefixed with init keyword. At the period of
instance initialization, the initialized blocks are executed in the same order as
they appear in class body.
class myClass(name: String, id: Int) {
val e_name: String
var e_id: Int
init{
e_name = name.capitalize()
e_id = id
println("Name = ${e_name}")
println("Id = ${e_id}")
}
}
fun main(args: Array<String>){
val myclass = myClass ("Ashu", 101)
}
Output:
Name = Ashu
Id = 101
class myClass{
constructor(id: Int){
//code
}
constructor(name: String, id: Int){
//code
}
}
Let's see an example of secondary constructor assigning the value while object
of class is created.
class myClass{
constructor(name: String, id: Int){
println("Name = ${name}")
println("Id = ${id}")
}
}
fun main(args: Array<String>){
val myclass = myClass ("Ashu", 101)
}
Output:
Name = Ashu
Id = 101
We can also use both primary as well as secondary constructor in a same class.
By using primary as well secondary constructor in same class, secondary
constructor needs to authorize to primary constructor. Authorization to another
constructor in same class is done using this() keyword.
AD
For example:
class myClass(password: String){
constructor(name: String, id: Int, password: String): this(password){
println("Name = ${name}")
println("Id = ${id}")
println("Password = ${password}")
}
}
fun main(args: Array<String>){
val myclass = myClass ("Ashu", 101, "mypassword")
}
Output:
Name = Ashu
Id = 101
Password = mypassword
class myClass{
constructor(name: String, id: Int): this(name,id, "mypassword"){
println("this executes next")
println("Name = ${name}")
println("Id = ${id}")
}
constructor(name: String, id: Int,pass: String){
println("this executes first")
println("Name = ${name}")
println("Id = ${id}")
println("Password = ${pass}")
}
}
fun main(args: Array<String>){
val myclass = myClass ("Ashu", 101)
}
Output:
AD
Name = Ashu
Id = 101
Password = mypassword
Name = Ashu
Id = 101
AD
In Kotlin, one derived class secondary constructor can call the base class
secondary constructor. This is done using super keyword, this is the concept of
inheritance.
open class Parent{
constructor(name: String, id: Int){
println("this executes first")
println("Name = ${name}")
println("Id = ${id}")
}
constructor(name: String, id: Int,pass: String){
println("this executes third")
println("Name = ${name}")
println("Id = ${id}")
println("Password = ${pass}")
}
}
class Child: Parent{
constructor(name: String, id: Int): super(name,id){
println("this executes second")
println("Name = ${name}")
println("Id = ${id}")
}
constructor(name: String, id: Int,pass: String):super(name,id,"password"){
println("this executes forth")
println("Name = ${name}")
println("Id = ${id}")
println("Password = ${pass}")
}
}
fun main(args: Array<String>){
val obj1 = Child("Ashu", 101)
val obj2 = Child("Ashu", 101,"mypassword")
}
Output:
Name = Ashu
Id = 101
Name = Ashu
Id = 101
Name = Ashu
Id = 101
Password = password
Name = Ashu
Id = 101
Password = mypassword
Kotlin Visibility Modifier
Visibility modifiers are the keywords which are used to restrict the use of class,
interface, methods, and property of Kotlin in the application. These modifiers
are used at multiple places such as class header or method body.
public
protected
internal
private
public modifier
public class Example{
}
class Demo{
}
public fun hello()
fun demo()
public val x = 5
val y = 10
All public declaration can be placed at top of the file. If a member of class is not
specified then it is by default public.
protected modifier
A protected modifier with class or interface allows visibility to its class or
subclass only. A protected declaration (when overridden) in its subclass is also
protected modifier unless it is explicitly changed.
open class Base{
protected val i = 0
}
class Derived : Base(){
fun getValue() : Int
{
return i
}
}
open class Base{
open protected val i = 5
}
class Another : Base(){
fun getValue() : Int
{
return i
}
override val i =10
}
internal modifier
internal class Example{
internal val x = 5
internal fun getValue(){
}
}
internal val y = 10
In above, all the fields are declared as internal which are accessible only inside
the module in which they are implemented.
AD
private modifier
private class Example {
private val x = 1
private valdoSomething() {
}
}
open class Base() {
var a = 1 // public by default
private var b = 2 // private to Base class
protected open val c = 3 // visible to the Base and the Derived class
internal val d = 4 // visible inside the same module
protected fun e() { } // visible to the Base and the Derived class
}
class Derived: Base() {
// a, c, d, and e() of the Base class are visible
// b is not visible
override val c = 9 // c is protected
}
fun main(args: Array<String>) {
val base = Base()
// base.a and base.d are visible
// base.b, base.c and base.e() are not visible
val derived = Derived()
// derived.c is not visible
}
Kotlin Inheritance
Inheritance is an important feature of object oriented programming language.
Inheritance allows to inherit the feature of existing class (or base or parent class)
to new class (or derived class or child class).
The main class is called super class (or parent class) and the class which inherits
the superclass is called subclass (or child class). The subclass contains features
of superclass as well as its own.
The concept of inheritance is allowed when two or more classes have same
properties. It allows code reusability. A derived class has only one base class but
may have multiple interfaces whereas a base class may have one or more
derived classes.
In Kotlin, the derived class inherits a base class using: operator in the class
header (after the derive class name or constructor)
open class Base(p: Int){
}
class Derived(p: Int) : Base(p){
}
open class Employee(name: String, age: Int, salary: Float) {
// code of employee
}
class Programmer(name: String, age: Int, salary: Float): Employee(name,age,sal
ary) {
// code of programmer
}
class Salesman(name: String, age: Int, salary: Float): Employee(name,age,salary
) {
// code of salesman
}
All Kotlin classes have a common superclass "Any". It is a default superclass for
a class with no supertypes explicitly specified.
class Example
For example:
open class Example{
// I can now be extended!
}
When we inherit a class to derive class, all the fields and functionalities are
inherited. We can use these fields and functionalities in derived class.
For example:
open class Base{
val x = 10
}
class Derived: Base() {
fun foo() {
println("x is equal to " + x)
}
}
fun main(args: Array<String>) {
val derived = Derived()
derived.foo()
}
Output:
x is equal to 10
open class Bird {
fun fly() {
println("flying...")
}
}
class Duck: Bird() {
fun swim() {
println("swimming...")
}
}
fun main(args: Array<String>) {
val duck = Duck()
duck.fly()
duck.swim()
}
Output:
flying...
swimming...
open class Employee(name: String, age: Int, salary: Float) {
init {
println("Name is $name.")
println("Age is $age")
println("Salary is $salary")
}
}
class Programmer(name: String, age: Int, salary: Float):Employee(name,age,sala
ry){
fun doProgram() {
println("programming is my passion.")
}
}
class Salesman(name: String, age: Int, salary: Float):Employee(name,age,salary)
{
fun fieldWork() {
println("travelling is my hobby.")
}
}
fun main(args: Array<String>){
val obj1 = Programmer("Ashu", 25, 40000f)
obj1.doProgram()
val obj2 = Salesman("Ajay", 24, 30000f)
obj2.fieldWork()
}
Output:
Name is Ashu.
Age is 25
Salary is 40000.0
programming is my passion.
Name is Ajay.
Age is 24
Salary is 30000.0
travelling is my hobby.
AD
If the base and derived class both having primary constructor in that case the
parameters are initialized in the primary constructor of base class. In above
example of inheritance, all classes contain three parameters "name", "age" and
"salary" and all these parameters are initialized in primary constructor of base
class.
When a base and derived class both contains different numbers of parameters in
their primary constructor then base class parameters are initialized form derived
class object.
AD
For example:
open class Employee(name: String,salary: Float) {
init {
println("Name is $name.")
println("Salary is $salary")
}
}
class Programmer(name: String, dept: String, salary: Float):Employee(name,sala
ry){
init {
println("Name $name of department $dept with salary $salary.")
}
fun doProgram() {
println("Programming is my passion.")
}
}
class Salesman(name: String, dept: String, salary: Float):Employee(name,salary)
{
init {
println("Name $name of department $dept with salary $salary.")
}
fun fieldWork() {
println("Travelling is my hobby.")
}
}
fun main(args: Array<String>){
val obj1 = Programmer("Ashu", "Development", 40000f)
obj1.doProgram()
println()
val obj2 = Salesman("Ajay", "Marketing", 30000f)
obj2.fieldWork()
}
Output:
Name is Ashu.
Salary is 40000.0
Programming is my passion.
Name is Ajay.
Salary is 30000.0
Name Ajay of department Marketing with salary 30000.0.
Travelling is my hobby.
When an object of derived class is created, it calls its superclass first and
executes init block of base class followed by its own.
If derived class does not contain any primary constructor then it is required to
call the base class secondary constructor from derived class
using super keyword.
For example,
AD
open class Patent {
constructor(name: String, id: Int) {
println("execute super constructor $name: $id")
}
}
class Child: Patent {
constructor(name: String, id: Int, dept: String): super(name, id) {
print("execute child class constructor with property $name, $id, $dept")
}
}
fun main(args: Array<String>) {
val child = Child("Ashu",101, "Developer")
}
Output:
In above example, when object of Child class is created, it calls its constructor
and initializes its parameters with values "Ashu", "101" and "Developer". At the
same time Child class constructor calling its supper class constructor using super
keyword with values of name and id. Due to the presence of super keyword
thebody of superclass constructor executes first and returns to Child class
constructor.
In other words, when subclass redefines or modifies the method of its superclass
into subclass, it is known as method overriding. Method overriding is only
possible in inheritance.
Parent class and its method or property which is to be overridden must be open
(non-final).
Method name of base class and derived class must have same.
open class Bird {
open fun fly() {
println("Bird is flying...")
}
}
class Parrot: Bird() {
}
class Duck: Bird() {
}
fun main(args: Array<String>) {
val p = Parrot()
p.fly()
val d = Duck()
d.fly()
}
Output:
Bird is flying...
Bird is flying...
open class Bird {
open fun fly() {
println("Bird is flying...")
}
}
class Parrot: Bird() {
override fun fly() {
println("Parrot is flying...")
}
}
class Duck: Bird() {
override fun fly() {
println("Duck is flying...")
}
}
fun main(args: Array<String>) {
val p = Parrot()
p.fly()
val d = Duck()
d.fly()
}
Output:
Parrot is flying...
Duck is flying...
open class Bird {
open var color = "Black"
open fun fly() {
println("Bird is flying...")
}
}
class Parrot: Bird() {
override var color = "Green"
override fun fly() {
println("Parrot is flying...")
}
}
class Duck: Bird() {
override var color = "White"
override fun fly() {
println("Duck is flying...")
}
}
fun main(args: Array<String>) {
val p = Parrot()
p.fly()
println(p.color)
val d = Duck()
d.fly()
println(d.color)
}
Output:
Parrot is flying...
Green
Duck is flying...
White
We can override the val property with var property in inheritance but vice-versa
is not true.
For example:
open class Bird {
open var color = "Black"
open fun fly() {
println("Bird is flying...")
}
}
class Parrot: Bird() {
override var color = "Green"
override fun fly() {
super.fly()
println("Parrot is flying...")
}
}
fun main(args: Array<String>) {
val p = Parrot()
p.fly()
println(p.color)
}
Output:
Bird is flying...
Parrot is flying...
Green
open class Bird {
open var color = "Black"
open fun fly() {
println("Bird is flying...")
}
}
interface Duck {
fun fly() {
println("Duck is flying...")
}
}
class Parrot: Bird(),Duck {
override var color = "Green"
override fun fly() {
super<Bird>.fly()
super<Duck>.fly()
println("Parrot is flying...")
}
}
fun main(args: Array<String>) {
val p = Parrot()
p.fly()
println(p.color)
}
Output:
Bird is flying...
Duck is flying...
Parrot is flying...
Kotlin Abstract class
A class which is declared with abstract keyword is known as abstract class. An
abstract class cannot be instantiated. Means, we cannot create object of abstract
class. The method and properties of abstract class are non-abstract unless they
are explicitly declared as abstract.
abstract class A {
var x = 0
abstract fun doSomething()
}
Abstract classes are partially defined classes, methods and properties which are
no implementation but must be implemented into derived class. If the derived
class does not implement the properties of base class then is also meant to be an
abstract class.
Abstract class or abstract function does not need to annotate with open keyword
as they are open by default. Abstract member function does not contain its body.
The member function cannot be declared as abstract if it contains in body in
abstract class.
abstract class Car{
abstract fun run()
}
class Honda: Car(){
override fun run(){
println("Honda is running safely..")
}
}
fun main(args: Array<String>){
val obj = Honda()
obj.run();
}
Output:
open class Car {
open fun run() {
println("Car is running..")
}
}
abstract class Honda : Car() {
override abstract fun run()
}
class City: Honda(){
override fun run() {
// TODO("not implemented") //To change body of created functions use File
| Settings | File Templates.
println("Honda City is running..")
}
}
fun main(args: Array<String>){
val car = Car()
car.run()
val city = City()
city.run()
}
Output:
Car is running..
abstract class Bank {
abstract fun simpleInterest(p: Int, r: Double, t: Int) :Double
}
class SBI : Bank() {
override fun simpleInterest(p: Int, r: Double, t: Int): Double{
return (p*r*t)/100
}
}
class PNB : Bank() {
override fun simpleInterest(p: Int, r: Double, t: Int): Double{
return (p*r*t)/100
}
}
fun main(args: Array<String>) {
var sbi: Bank = SBI()
val sbiint = sbi.simpleInterest(1000,5.0,3)
println("SBI interest is $sbiint")
var pnb: Bank = PNB()
val pnbint = pnb.simpleInterest(1000,4.5,3)
println("PNB interest is $pnbint")
}
Output:
Kotlin Interface
An interface is a blueprint of class.Kotlin interface is similar to Java 8. It
contains abstract method declarations as well as implementation of method.
Defining Interface
interface MyInterface {
val id: Int // abstract property
fun absMethod()// abstract method
fun doSomthing() {
// optional body
}
}
The methods which are only declared without their method body are abstract by
default.
Implementing Interfaces
interface MyInterface {
var id: Int // abstract property
fun absMethod():String // abstract method
fun doSomthing() {
println("MyInterface doing some work")
}
}
class InterfaceImp : MyInterface {
override var id: Int = 101
override fun absMethod(): String{
return "Implementing abstract method.."
}
}
fun main(args: Array<String>) {
val obj = InterfaceImp()
println("Calling overriding id value = ${obj.id}")
obj.doSomthing()
println(obj.absMethod())
}
Output:
AD
interface MyInterface1 {
fun doSomthing()
}
interface MyInterface2 {
fun absMethod()
}
class MyClass : MyInterface1, MyInterface2 {
override fun doSomthing() {
println("overriding doSomthing() of MyInterface1")
}
override fun absMethod() {
println("overriding absMethod() of MyInterface2")
}
}
fun main(args: Array<String>) {
val myClass = MyClass()
myClass.doSomthing()
myClass.absMethod()
}
Output:
interface MyInterface1 {
fun doSomthing(){
println("overriding doSomthing() of MyInterface1")
}
}
interface MyInterface2 {
fun doSomthing(){
println("overriding doSomthing() of MyInterface2")
}
}
class MyClass : MyInterface1, MyInterface2 {
}
fun main(args: Array<String>) {
val myClass = MyClass()
myClass.doSomthing()
}
Output:
Kotlin: Class 'MyClass' must override public open fun doSomthing(): Unit
defined in MyInterface1 because it
interface MyInterface1 {
fun doSomthing() {
println("MyInterface 1 doing some work")
}
fun absMethod()
}
interface MyInterface2 {
fun doSomthing(){
println("MyInterface 2 doing some work")
}
fun absMethod(name: String)
}
class MyClass : MyInterface1, MyInterface2 {
override fun doSomthing() {
super<MyInterface2>.doSomthing()
}
override fun absMethod() {
println("Implements absMethod() of MyInterface1")
}
override fun absMethod(n: String) {
println("Implements absMethod(name) of MyInterface2 name is $n")
}
}
fun main(args: Array<String>) {
val myClass = MyClass()
myClass.doSomthing()
myClass.absMethod()
myClass.absMethod("Ashu")
}
Output:
interface MyInterface1 {
fun doSomthing() {
println("MyInterface 1 doing some work")
}
fun absMethod()
}
interface MyInterface2 {
fun doSomthing() {
println("MyInterface 2 doing some work")
}
fun absMethod() {
println("MyInterface 2 absMethod")
}
}
class C : MyInterface1 {
override fun absMethod() {
println("MyInterface1 absMethod implementation")
}
}
class D : MyInterface1, MyInterface2 {
override fun doSomthing() {
super<MyInterface1>.doSomthing()
super<MyInterface2>.doSomthing()
}
override fun absMethod() {
super<MyInterface2>.absMethod()
}
}
fun main(args: Array<String>) {
val d = D()
val c = C()
d.doSomthing()
d.absMethod()
c.doSomthing()
c.absMethod()
}
Output:
MyInterface 2 absMethod
Data class is a simple class which is used to hold data/state and contains
standard functionality. A data keyword is used to declare a class as a data class.
data class User(val name: String, val age: Int)
Declaring a data class must contains at least one primary constructor with
property argument (val or var).
equals(): Boolean
hashCode(): Int
toString(): String
copy()
Due to presence of above functions internally in data class, the data class
eliminates the boilerplate code.
If we want to create a User entry in Java using data class, it require lots of
boilerplate code.
import java.util.Objects;
public class User {
private String name;
private int id;
private String email;
public User(String name, int id, String email) {
this.name = name;
this.id = id;
this.email = email;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public intgetId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (!(o instanceof User)) return false;
User user = (User) o;
return getId() == user.getId() &&
Objects.equals(getName(), user.getName()) &&
Objects.equals(getEmail(), user.getEmail());
}
@Override
public inthashCode() {
return Objects.hash(getName(), getId(), getEmail());
}
@Override
public String toString() {
return "User{" +
"name='" + name + '\'' +
", id=" + id +
", email='" + email + '\'' +
'}';
}
}
Calling the constructor of above Java data class using the object of User class as
class MyClass{
public static void main(String agrs[]){
User u = new User("Ashu",101,"mymail@mail.com");
System.out.println(u);
}
}
Output:
The above Java data class code is rewritten in Kotlin data code in single line as
data class User(var name: String, var id: Int, var email: String)
Calling the constructor of above Kotlin data class using the object of User class
as
fun main(agrs: Array<String>) {
val u = User("Ashu", 101, "mymail@mail.com")
println(u)
}
Output:
Before 1.1,data class may only implements interface. After that data classes may
extend other classes.
AD
Kotlin data class toString() methods
Kotlin data class only focuses on data rather than code implementation.
Let's see a simple program without data class. In this class, we are trying to print
the reference of Product class using its object.
class Product(varitem: String, var price: Int)
fun main(agrs: Array<String>) {
val p = Product("laptop", 25000)
println(p)
}
While printing the reference of Product class, it displays the hashCode() with
class name of Product. It does not print the data.
Output:
Product@266474c2
data class Product(varitem: String, var price: Int)
fun main(agrs: Array<String>) {
val p = Product("laptop", 25000)
println(p)
}
Output:
AD
Product(name=laptop, price=25000)
The equal() method is used to check other object is "equal to" current object.
While doing comparison between two or more hashCode(), equals() method
returns true if the hashCode() are equal, else it returns a false.
For example, let's see an example in which a normal class comparing the two
references of same class Product having same data.
class Product(varitem: String, var price: Int)
fun main(agrs: Array<String>) {
val p1 = Product("laptop", 25000)
val p2 = Product("laptop", 25000)
println(p1==p2)
println(p1.equals(p2))
}
Output:
false
false
data class Product(varitem: String, var price: Int)
fun main(agrs: Array<String>) {
val p1 = Product("laptop", 25000)
val p2 = Product("laptop", 25000)
println(p1==p2)
println(p1.equals(p2))
}
Output:
true
true
The data class provides a copy() method which is used to create a copy (or
colon) of object. Using copy() method, some or all properties of object can be
altered.
For example:
data class Product(var item: String, var price: Int)
fun main(agrs: Array<String>) {
val p1 = Product("laptop", 25000)
println("p1 object contain data : $p1")
val p2 = p1.copy()
println("p2 copied object contains default data of p1: $p2")
val p3 = p1.copy(price = 20000)
println("p3 contain altered data of p1 : $p3")
}
Output:
We can also assign the default arguments in primary constructor of data class.
These default values can be changed later on program if required.
For example:
data class Product(var item: String = "laptop", var price: Int = 25000)
fun main(agrs: Array<String>) {
val p1 = Product(price = 20000)
println(p1)
}
Output:
Product(item=laptop, price=20000)
Kotlin Sealed Class
Sealed class is a class which restricts the class hierarchy. A class can be declared
as sealed class using "sealed" keyword before the class name. It is used to
represent restricted class hierarchy.
Sealed class is used when the object have one of the types from limited set, but
cannot have any other type.
The constructors of sealed classes are private in default and cannot be allowed as
non-private.
sealed class MyClass
The subclasses of sealed classes must be declared in the same file in which
sealed class itself.
sealed class Shape{
class Circle(var radius: Float): Shape()
class Square(var length: Int): Shape()
class Rectangle(var length: Int, var breadth: Int): Shape()
object NotAShape : Shape()
}
Sealed class ensures the important of type-safety by restricting the set of types at
compile time only.
sealed class A{
class B : A()
{
class E : A() //this works.
}
class C : A()
init {
println("sealed class A")
}
}
class D : A() //this works
{
class F: A() //This won't work,because sealed class is defined in another scope.
}
sealed class MyClass
fun main(args: Array<String>)
{
var myClass = MyClass() //compiler error. sealed types cannot be instantiated.
}
Sealed classes are commonly used with when expression. As the sub classes of
sealed classes have their own types act as a case. Due to this, when
expression in sealed class covers all the cases and avoid to add else clause.
For example:
sealed class Shape{
class Circle(var radius: Float): Shape()
class Square(var length: Int): Shape()
class Rectangle(var length: Int, var breadth: Int): Shape()
// object NotAShape : Shape()
}
fun eval(e: Shape) =
when (e) {
is Shape.Circle ->println("Circle area is ${3.14*e.radius*e.radius}")
is Shape.Square ->println("Square area is ${e.length*e.length}")
is Shape.Rectangle ->println("Rectagle area is ${e.length*e.breadth}")
//else -> "else case is not require as all case is covered above"
// Shape.NotAShape ->Double.NaN
}
fun main(args: Array<String>) {
var circle = Shape.Circle(5.0f)
var square = Shape.Square(5)
var rectangle = Shape.Rectangle(4,5)
eval(circle)
eval(square)
eval(rectangle)
}
Output:
Square area is 25
Rectagle area is 20
fun <class_name>.<method_name>()
In general, we call all methods from outside the class which are already defined
inside the class.In below example, a Student class declares a method
is Passed() which is called from main() function by creating the object student
of Student class.
Suppose that we want to call a method (say isExcellent()) of Student class which
is not defined in class. In such situation, we create a function (isExcellent())
outside the Student class as Student.isExcellent() and call it from the main()
function. The declare Student.isExcellent() function is known as extension
function, where Student class is known as receiver type.
class Student{
fun isPassed(mark: Int): Boolean{
return mark>40
}
}
fun Student.isExcellent(mark: Int): Boolean{
return mark > 90
}
fun main(args: Array<String>){
val student = Student()
val passingStatus = student.isPassed(55)
println("student passing status is $passingStatus")
val excellentStatus = student.isExcellent(95)
println("student excellent status is $excellentStatus")
}
Output:
Let's see the real example of extension function. In this example, we are
swapping the elements of MutableList<> using swap() method. However,
MutableList<>class does not provide the swap() method internally which swap
the elements of it. For doing this we create an extension function for
MutableList<> with swap() function.
fun MutableList<Int>.swap(index1: Int, index2: Int):MutableList<Int> {
val tmp = this[index1] // 'this' represents to the list
this[index1] = this[index2]
this[index2] = tmp
return this
}
fun main(args: Array<String>) {
val list = mutableListOf(5,10,15)
println("before swapping the list :$list")
val result = list.swap(0, 2)
println("after swapping the list :$result")
}
Output:
The extension function can be defined as nullable receiver type. This nullable
extension function is called through object variable even the object value is null.
The nullability of object is checked using this == null inside the body.
Let's rewrite the above program using extension function as nullable receiver.
funMutableList<Int>?.swap(index1: Int, index2: Int): Any {
if (this == null) return "null"
else {
val tmp = this[index1] // 'this' represents to the list
this[index1] = this[index2]
this[index2] = tmp
return this
}
}
fun main(args: Array<String>) {
val list = mutableListOf(5,10,15)
println("before swapping the list :$list")
val result = list.swap(0, 2)
println("after swapping the list :$result")
}
Output:
class MyClass {
companion object {
fun create():String{
return "calls create method of companion object"
}
}
}
fun main(args: Array<String>){
val instance = MyClass.create()
}
Output:
AD
class MyClass {
companion object {
fun create(): String {
return "calling create method of companion object"
}
}
}
fun MyClass.Companion.helloWorld() {
println("executing extension of companion object")
}
fun main(args: Array<String>) {
MyClass.helloWorld() //extension function declared upon the companion object
}
Output:
Advantage of Generics
Type-safety: Generic allows to hold only single type of object. Generic does not
allow to store other object.
In this example, we create a Person class with primary constructor having single
parameter. Now, we want to pass the different type of data in object of Person
class (say Int type as Person(30) and String type as Person("40")). The primary
constructor of Person class accept Int type Person(30) and regrets String type
Person("40"). It generates a compile time error as type mismatch.
class Person (age:Int){
var age: Int = age
init {
this.age= age
println(age)
}
}
fun main(args: Array<String>){
var ageInt: Person = Person(30)
var ageString: Person = Person("30")// compile time error
}
To solve the above problem, we use a generic type class which is a user defined
class that accepts different type of parametersin single class.
Let's rewrite the above code using generic type. A class Person of type <T> is a
general type class that accepts both Int and String types of parameter.
In other words, the type parameter <T> is a place holder that will be replaced by
type argument. It will be replaced when the generic type is instantiated.
class Person<T>(age: T){
var age: T = age
init {
this.age= age
println(age)
}
}
fun main(args: Array<String>){
var ageInt: Person<Int> = Person<Int>(30)
var ageString: Person<String> = Person<String>("40")
}
Output:
30
40
In above example, when the object of Person class is created using type Int as
Person<Int>(30) and Person<String>("40"), it replaces the Person class of
type T with Int and String respectively.
class_or_interface<Type>
<Type>methodName(parameter: classType<Type>)
Let's see an example of generic method. In this example, we are accessing the
generic method of collection type (ArrayList). For doing this, we create two
different objectsof ArrayList class arrayListOf<String>("Ashu","Ajay") and
arrayListOf<Float>(10.5f,5.0f,25.5f) of String and Float types respectively.
When we call the generic method <T>printValue(list: ArrayList<T>) using
printValue(stringList), the type T of method <T>printValue(list:
ArrayList<T>)will be replaced by String type. Similarly, when we call the
generic method using printValue(floatList), the type T of method
<T>printValue(list: ArrayList<T>) will replace by Float type.
fun main(args: Array<String>){
val stringList: ArrayList<String> = arrayListOf<String>("Ashu","Ajay")
val s: String = stringList[0]
println("printing the string value of stringList: $s")
printValue(stringList)
val floatList: ArrayList<Float> = arrayListOf<Float>(10.5f,5.0f,25.5f)
printValue(floatList)
}
fun <T>printValue(list: ArrayList<T>){
for(element in list){
println(element)
}
}
Output:
Ashu
Ajay
10.5
5.0
25.5
val stringList: ArrayList<String> = arrayListOf<String>("Ashu","Ajay")
stringList.printValue()
val floatList: ArrayList<Float> = arrayListOf<Float>(10.5f,5.0f,25.5f)
floatList.printValue()
}
fun <T>ArrayList<T>.printValue(){
for(element in this){
println(element)
}
}
Output:
AD
Ashu
Ajay
10.5
5.0
25.5