Kotlin
Kotlin
Constructor:
Primary Constructor
Second Constructors
1. Primary Constructor
We use it when you want to initialize properties of the class directly and when no
additional setup logic is needed.
The primary constructor is defined in the class header, directly after the class name.
It contains constructor parameters.
2. Secondary Constructor
We use it when we need additional ways to initialize the class or perform extra setup
logic.
Secondary constructors are defined using the “constructor” keyword.
They provide additional ways to initialize the class or perform additional setup logic
Secondary constructors can't initialize properties directly; they must use the primary
constructor to do so.
Class.
Data class:
A data class is a simple class that is used to hold data and contains standard
functionality. A data keyword is used to declare a class as a data class.
Data class cannot be abstract, inner, open, or sealed.
Declaring a data class we have to use “val” or “var” inside the parameters.
The data class should have at least one parameter.
The normal class doesn’t have “toString” methods but the data class has “toString”
methods.
Data class object works with data.
We cannot extend or subclass on data class.
Sealed class:
Sealed classes are used when we have a fixed, restricted, set of sub-class and we
want to ensure exhaustive handling of those sub-class.
The subclass of the sealed classes must be declared in the same file in which the
sealed class itself is declared.
The sub-class can be a data class, regular class, object class, or even another
sealed class or sealed interface.
A sealed class is an abstract class which cannot be instantiated.
By default, the constructor of a sealed class is private and we cannot make it non-
private.
Inner class:
The nested class in Kotlin does not have access to the outer class instance. To
avoid this problem, we normally use an inner class.
Open Class:
The open keyword with the class means the class is open for the extension meaning
that we can create a subclass of that open class.
Interfaces:
The interface can contain both normal methods and abstract methods.
The interface only contains abstract properties.
The interface is not a class.
We cannot create an instance of an interface similar to an abstract class.
Abstract Class:
When we need to define a common set of rules for multiple classes, we can make
a common abstract parent class and we can derive child classes using this
common base class.
Null Safety:
Safe call [ ?. ]
Safe call with Let [?. let{ } ]
Not null assertion operation [ !!]
Elvis operator [?] is used for the null message.
Safe call? Only call when the value is non-null. Else it prints null.
2. Run
It works similarly to “Let”, but It’s used when we need to access properties and
functions of the object using “this” instead of an explicit reference to the object.
Return: Lambda result
Context object: this
3. With
It allows us to execute multiple operations on an object without having to call the
object’s name repeatedly.
Return: Lambda result.
Context object: this.
4. Apply
It is used for initializing the properties of an object or performing setup
operations on an object. It results from the object itself after applying the
changes.
Return: Context object.
Context object: this.
5. Also
It is used for performing additional operations on an object an object without
changing it. It returns the object itself after applying the additional operations.
Return: context object.
Context object: It.
List
Immutable list: list
Mutable collections (List): ArrayList, arrayListOf, MutableListOf
Set:
The set has a unique value and cannot be duplicated. A set cannot be accessed by
index.
Immutable set: setOf
Mutable set: mutableSetOf, hashSetOf
Map:
In a map, first we have a key and then a value.
Immutable map: mapOf
Mutable map: HashMap, hasMapOf, mutableMapOf.
Functions
1. Higher-Order Functions
The high order function is a function that accepts functions as parameters.
Can return a function.
Or can do both.
2. Lambda Functions
Lambda Expression {parameters: Type -> body}
Example {s: Int -> print(s)}
val myLamda: (Int) -> Unit = {s : Int -> print(s)}
val myLamda: (Int, Int) -> Int = {x, y -> x + y}
It’s just a function with no name.
Difference between “open” VS “final” VS “public” keyword.
open keyword: “open” allows for inheritance and overriding.
final keyword: “final” prevents inheritance and overriding.
public: The public keyword is used to define the visibility of a class, property,
or function. When a class, property, or function is marked as public, it is
accessible from any other class or file in the same module.
var (Mutable Variables): Variables declared with var are mutable, meaning
their values can be changed or reassigned after they are initialized.
Singleton:
-> A Singleton is a design pattern that ensures a class has only one instance and
provides a global point of access to that instance.
-> In Kotlin, creating a Singleton is straightforward using the object keyword. The
object keyword creates a singleton instance of a class at the time of its
initialization.
Advantages of Singleton:
Global Access: Provides a single point of access to the instance, making it easy
to access from anywhere in the application.
Memory Efficiency: Since only one instance is created, it saves memory by
preventing multiple instances of the same class.
Initialization Control: The Singleton pattern allows for lazy initialization,
meaning the instance is created only when it's needed for the first time.
Thread Safety: Singleton instances can be made thread-safe, ensuring that
only one thread can access the instance at a time.
Disadvantages of Singleton:
Global State: Singletons can introduce a global state, which can make the
application's behavior harder to understand and debug. Memory
Efficiency: Since only one instance is created, it saves memory by
preventing multiple instances of the same class.
Testing Complexity: Singletons can be difficult to test in isolation, as they
often depend on the global state.
Tight Coupling: Code that depends on a Singleton becomes tightly
coupled to it, making it harder to change or replace the Singleton in the
future.
Overuse: Singleton patterns can be overused, leading to an unnecessarily
complex and tightly coupled codebase.
-> Yes, Kotlin supports the use of primitive data types such as Int, Float, Double, Boolean,
Char, and others, just like Java. However, Kotlin also provides a rich set of wrapper
classes for these primitive types, allowing them to behave like objects when necessary.
These wrapper classes are automatically used when needed, so you can use primitive
types directly without needing to explicitly use their wrapper classes in most cases.
When we use the lateinit keyword in Kotlin. Why do we use it and why do we use it?
-> In kotlin, the late “lateinit” keyword is used to declare non-null variables that will be
initialized later. This is particularly useful when we can't initialize a variable upon its
declaration due to dependency injection or other reasons, but we are sure that the
variable will be initialized before it is used.
* Here are some scenarios where you might use lateinit instead of initializing directly:
1. Lateinit
Used for properties that will be initialized later and will always have a
non-null value once initialized.
Only applicable to var properties of non-nullable types (e.g., var
myProperty: SomeType).
The initialization must be done before accessing the property; otherwise,
a LateinitPropertyAccessException will be thrown.
Cannot be used with nullable types.
2. Lazy
Used for properties whose values are computed only when accessed for
the first time and will not change afterward.
Applicable to both val and var properties.
The initialization is thread-safe by default, meaning it will only be
executed once, even in a multi-threaded environment.
Can be used with nullable types.
Differences:
Mutability: lateinit is used for mutable properties (var), whereas lazy can be
used for both mutable and immutable properties (val).
Initialization Time: With lateinit, you need to ensure that the property is
initialized before accessing it, while with lazy, the initialization is deferred
until the property is accessed for the first time.
Thread Safety: lazy properties are by default thread-safe, ensuring that the
initialization is executed only once, even in a multi-threaded environment.
lateinit properties do not have built-in thread safety; you need to ensure
proper initialization yourself if working in a multi-threaded environment.
What is the infix function in Kotlin? Why do we use the infix function in the Kotlin
program?
In Kotlin, an inline function is a function modifier that suggests to the compiler that the
function should be inlined at the call site. Inlining means that the function's code is
copied directly into the calling code during compilation, eliminating the overhead of
function calls.
-> Reduced Function Call Overhead: When working with higher-order functions or
lambdas, using inline functions can avoid the overhead of creating function objects
and invoking them, leading to more efficient code.
> In Kotlin, Pair and Triple are standard library classes used to represent a fixed-
size collection of two or three elements, respectively. They are used when
you need to combine two or three values together into a single object. They
provide a convenient way to return multiple values from a function, represent
key-value pairs, or simply group related values together.
Pair Class:
Represents a pair of two values.
Defined in the Kotlin standard library as kotlin.Pair.
The first and second values are accessed using the first and second
properties, respectively.
Triple Class:
Represents a group of three values.
Defined in the Kotlin standard library as kotlin.Triple.
The first, second, and third values are accessed using the first, second,
and third properties, respectively.
Use Case:
1. Returning Multiple Values: Instead of creating a custom data class or using
arrays, you can use Pair or Triple to return multiple values from a function.
2. Key-Value Pairs: Pair can be used to represent key-value pairs, providing a
more expressive and readable alternative to using maps.
3. Simple Data Grouping: When you need to group related values
temporarily, Pair and Triple provide a lightweight and convenient solution.
In Kotlin, labels are used to provide a way to specify the target of a control flow
structure, such as a loop or a conditional statement. Labels allow you to break out of or
continue to an outer loop, even if there are nested loops.
Reduce function:
-> The reduce function is also a higher-order function available on collections in Kotlin. It
is used to accumulate the elements of a collection into a single value by applying a given
operation to adjacent elements iteratively.
-> In Kotlin, when expression is used as a replacement for the traditional switch
statement found in many other programming languages. It provides a more powerful
and concise way to perform conditional branching based on the value of an expression.
-> In Kotlin, both map and flatMap are higher-order functions used to transform
collections, but they differ in their behavior and the type of transformation they apply.
1. Map Function: The map function is used to transform each element of a collection
into another value based on a transformation function provided as an argument. It
returns to a new collection containing the transformed elements, maintaining the
same structure as the original collection.
2. FlatMap function: The flatMap function is similar to map, but it's used when the
transformation function returns a collection or an iterable for each element. It
flattens the resulting collections into a single list by concatenating them together.
What is Kotlin visibility modifier?
In Kotlin, visibility modifiers control the visibility of classes, functions, properties, and other
elements within your codebase. There are four visibility modifiers in Kotlin: