Contents
- 1 Hello World Program
- 2 Variables and Data Types
- 3 Conditionals and Loops
- 4 Functions
- 5 Lists and Iteration
- 6 Nullable Types and Safe Calls
- 7 Class and Object
- 8 Extension Functions
- 9 Data Classes
- 10 Lambdas and Higher-Order Functions
- 11 Exception Handling
- 12 Inheritance and Polymorphism
- 13 Companion Objects
- 14 Sealed Classes
- 15 Coroutines
- 16 Extensions for Standard Library Functions
- 17 Collections: MutableMap
- 18 Data Validation with Regular Expressions
- 19 File Handling
- 20 Scope Functions: apply and let
- 21 Delegated Properties
- 22 Object Expressions
- 23 Destructuring Declarations
- 24 Enum Classes
- 25 25. Tail Recursive Functions
- 26 Extension Properties
- 27 Local Functions
- 28 Custom Getters and Setters
- 29 Higher-Order Functions with Lambda Arguments
- 30 Inline Functions
- 31 Type Aliases
- 32 Sealed Interfaces
- 33 Reified Type Parameters
- 34 Object Declaration
- 35 Smart Casts
- 36 Non-Null Assertion
- 37 Delegated Properties with by lazy
- 38 Default Arguments and Named Arguments
- 39 when Expression with Multiple Conditions
- 40 Using the in Operator
- 41 Extension Functions for Companion Objects
- 42 Functional Programming with map, filter, and reduce
- 43 Elvis Operator and Safe Calls
- 44 Coroutines with Async and Await
- 45 Collections: associateBy and groupingBy
- 46 Extension Functions on Nullable Types
- 47 Inline Classes
- 48 Dynamic Type Checks
- 49 Custom Annotations
- 50 Data Serialization with Kotlinx Serialization
- 51 Type Projection with Generics
- 52 Type Aliases for Function Types
- 53 Type-Safe Builders with DSL
- 54 Annotation Processing with KotlinPoet
- 55 Sealed Properties in Data Classes
- 56 Custom DSL with Lambda Extensions
- 57 Companion Object Extensions
- 58 Variance: out and in Modifiers with Generics
- 59 Reified Type Parameters with inline
- 60 Type Erasure and Inline Reified Types
- 61 Destructuring Declarations in Loops
- 62 String Interpolation with Triple Quotes
- 63 Coroutines: Exception Handling
- 64 Functional Programming with let, run, with, apply, and also
- 65 Sealed Classes and when Expression
- 66 @JvmOverloads Annotation
- 67 Infix Functions
- 68 Delegated Properties: lazy, observable, and vetoable
- 69 Type Aliases for Complex Types
- 70 Custom Setters and Getters
- 71 Local Functions
- 72 Tail Recursive Functions
- 73 Enum Classes and Properties
- 74 Extension Properties
- 75 Late-Initialized Properties
- 76 Tailrec Modifier in Interface Functions
- 77 Secondary Constructors
- 78 Companion Object Extensions with Operator Overloading
- 79 Ranges and Progressions
- 80 Named Arguments
- 81 Extension Function Inference
- 82 Function Types with Receiver (Function Literals with Receiver)
- 83 Type-Safe Builders for HTML DSL
- 84 Type Inference in Lambdas
- 85 Function Composition
- 86 Inline Classes
- 87 Extension Functions for Standard Library Types
- 88 Inlined Higher-Order Functions
- 89 Type Erasure and Reified Type Parameters
- 90 Using when Expression with in and is Checks
- 91 Late-Initialized Properties with lateinit
- 92 Destructuring Declarations with Maps
- 93 Object Expressions
- 94 Unary Minus Operator Overloading
- 95 Using in with Custom Objects
- 96 Smart Casts
- 97 Object Declarations
- 98 Function Type Notation
- 99 Function References
- 100 Using when Expression without Argument
Kotlin is a modern, statically-typed programming language that runs on the Java Virtual Machine (JVM). It was designed by JetBrains to be concise, expressive, and interoperable with existing Java codebases. Since its release in 2011, Kotlin has gained significant popularity due to its robust features and improved syntax. In this overview, we’ll delve into the fundamental concepts and features of the Kotlin programming language.
1. Statically-Typed Language: Kotlin is a statically-typed language, which means that the data types of variables are determined at compile-time rather than at runtime. This helps catch type-related errors early in the development process.
2. Nullable Types: In Kotlin, variables are non-nullable by default. If you want to declare a variable that can hold a null value, you must explicitly indicate that by using the nullable type modifier ?
.
3. Type Inference: Kotlin has strong type inference capabilities, allowing developers to omit explicit type declarations for variables. The compiler can often deduce the appropriate type based on the assigned value.
4. Variables and Constants: Kotlin provides two main keywords for declaring variables:
val
: Used for defining read-only variables (constants).var
: Used for defining mutable variables.
5. Basic Data Types: Kotlin offers a variety of basic data types, including:
- Integers:
Int
,Long
,Short
,Byte
- Floating-Point Numbers:
Float
,Double
- Characters:
Char
- Booleans:
Boolean
6. Strings: Strings in Kotlin are represented using the String
class. They support various operations, including concatenation, interpolation, and manipulation.
7. Control Flow: Kotlin supports traditional control flow constructs like if
, else if
, and else
for conditional branching, as well as when
for more complex matching of values.
8. Loops: Kotlin supports both for
and while
loops. The for
loop can be used to iterate over ranges, collections, arrays, and other iterable objects.
9. Functions: Functions in Kotlin are declared using the fun
keyword. They can have parameters, return types, and can also be extension functions or infix functions.
10. Null Safety: Kotlin addresses the infamous null pointer exceptions common in Java by introducing a comprehensive null safety system. This system includes nullable types, safe calls (?.
), and the Elvis operator (?:
).
11. Smart Casts: Kotlin’s smart cast feature allows the compiler to automatically cast a variable to a more specific type when certain conditions are met. This reduces the need for explicit type casting.
12. Object-Oriented Programming: Kotlin is an object-oriented language, supporting classes and objects. It also offers various features like inheritance, interfaces, and abstract classes.
13. Extension Functions: Extension functions allow developers to add new functions to existing classes without modifying their source code. This promotes cleaner and more modular code.
14. Companion Objects: In Kotlin, companion objects are used to define static members and methods associated with a class.
15. Data Classes: Kotlin’s data classes are used for creating classes that primarily hold data. The compiler automatically generates standard methods like equals
, hashCode
, and toString
.
16. Collections: Kotlin provides a rich set of collection classes, including lists, sets, and maps. These collections offer convenient functions for manipulation and iteration.
17. Interoperability with Java: One of Kotlin’s core strengths is its seamless interoperability with Java code. Kotlin code can call Java functions and vice versa, making it a great choice for projects that integrate with existing Java libraries.
18. Coroutines: Kotlin offers native support for coroutines, which are used for asynchronous programming. Coroutines simplify concurrent code and allow developers to write non-blocking code in a more sequential manner.
19. Standard Library: Kotlin includes a comprehensive standard library with useful functions and classes for common programming tasks, reducing the need for external libraries.
20. Android Development: Kotlin has gained popularity in the Android development community due to its concise syntax and improved safety features. It is now officially supported by Google as a first-class language for Android app development.
21. Type Aliases: Kotlin allows you to create type aliases using the typealias
keyword. This can help improve code readability and provide more descriptive names for complex types.
22. Sealed Classes: Sealed classes are used to represent restricted class hierarchies. They are often used in combination with when
expressions to handle a specific set of cases exhaustively.
23. Delegation: Kotlin supports class delegation, which allows you to delegate the implementation of certain methods to another object. This promotes code reuse and modularity.
24. Late-Initialized Properties: In Kotlin, you can declare properties as lateinit
if they are intended to be initialized later, before their first use. This can be useful when working with dependency injection.
25. Property Observers: Kotlin provides the observable
and vetoable
delegates that allow you to add custom behavior when a property’s value changes.
26. Ranges: Ranges are a powerful feature in Kotlin that allow you to create sequences of values. They are often used in for
loops and other iteration constructs.
27. Operator Overloading: Kotlin allows you to overload operators for your custom classes, providing a more natural and expressive syntax for working with your types.
28. Inline Functions: The inline
keyword in Kotlin is used to annotate functions that should be inlined at the call site. Inlining can improve performance by reducing the overhead of function calls.
29. Type Checks and Casts: Kotlin provides the is
operator for type checking and the as
operator for type casting. These operators are safer and more concise compared to their Java counterparts.
30. Named and Default Arguments: When calling functions in Kotlin, you can specify arguments by name, allowing for more readable and self-explanatory function calls. Additionally, functions can have default parameter values.
31. Destructuring Declarations: You can destructure data classes and other classes by assigning their properties to individual variables using a destructuring declaration.
32. Infix Functions: Infix functions allow you to call methods with a more natural language-like syntax. They are often used for operations that feel like language constructs, such as adding elements to a collection.
33. Equality and Identity: Kotlin distinguishes between structural equality (equals
) and referential identity (===
). This helps prevent confusion when comparing objects.
34. Annotations: Annotations in Kotlin are used for adding metadata to classes, functions, and properties. They can be used for documentation, runtime behavior modification, and more.
35. Access Modifiers: Kotlin provides access modifiers like private
, protected
, and internal
to control the visibility and accessibility of classes, functions, and properties.
36. Platform Types: Kotlin has the concept of platform types, which are types that are partially known by the Kotlin compiler and partially known by the underlying platform (e.g., Java). Care must be taken when working with platform types to avoid potential issues.
37. Type Erasure: Like Java, Kotlin uses type erasure for generic types at runtime. However, Kotlin provides reified type parameters for inline functions, allowing for more type-safe operations on generic types.
38. Inline Classes: Introduced in Kotlin 1.3, inline classes are used to wrap a single value and provide a type-safe way to represent that value. They are designed to have minimal runtime overhead.
39. Multiplatform Projects: Kotlin offers the capability to create multiplatform projects, where you can share code between different platforms such as JVM, Android, iOS, and more.
40. Scripting: Kotlin can also be used for scripting, allowing you to write short, concise scripts that can be executed directly from the command line or integrated into other systems.
In summary, Kotlin is a feature-rich programming language with a plethora of capabilities that enhance code expressiveness, safety, and maintainability. Its modern syntax and extensive standard library make it suitable for a wide range of applications, from mobile and web development to server-side programming and beyond. Whether you’re a beginner or an experienced developer, Kotlin’s intuitive design and robust features can make your coding journey more enjoyable and productive.
Here’s a simple Kotlin tutorial for beginners that covers basic concepts using content H2 headings, code examples, and their respective outputs.
Hello World Program
fun main() {
println("Hello, World!")
}
Output:
Hello, World!
Variables and Data Types
fun main() {
// Integer variable
val age: Int = 25
// Double variable
val height: Double = 5.9
// String variable
val name: String = "John Doe"
// Boolean variable
val isStudent: Boolean = true
println("Name: $name, Age: $age, Height: $height, Is Student: $isStudent")
}
Output:
Name: John Doe, Age: 25, Height: 5.9, Is Student: true
Conditionals and Loops
fun main() {
val num = 7
// If-else statement
if (num % 2 == 0) {
println("$num is even.")
} else {
println("$num is odd.")
}
// For loop
for (i in 1..5) {
println("Iteration $i")
}
// While loop
var counter = 0
while (counter < 3) {
println("Counter: $counter")
counter++
}
}
Output:
7 is odd.
Iteration 1
Iteration 2
Iteration 3
Iteration 4
Iteration 5
Counter: 0
Counter: 1
Counter: 2
Functions
fun main() {
val sum = add(5, 10)
println("Sum: $sum")
val result = calculate(3, 4, 2)
println("Result: $result")
}
fun add(a: Int, b: Int): Int {
return a + b
}
fun calculate(x: Int, y: Int, z: Int): Int {
return (x * y) + z
}
Output:
Sum: 15
Result: 14
Lists and Iteration
In this example, we’ll learn how to work with lists and use iteration to perform various operations on them.
fun main() {
// Creating a list of numbers
val numbers = listOf(1, 2, 3, 4, 5)
// Printing the elements of the list
println("List of numbers: $numbers")
// Summing all elements in the list
val sum = numbers.sum()
println("Sum of the numbers: $sum")
// Finding the maximum value in the list
val maxNum = numbers.maxOrNull()
println("Maximum number: $maxNum")
// Checking if a number exists in the list
val searchNumber = 3
val containsNumber = numbers.contains(searchNumber)
println("List contains $searchNumber: $containsNumber")
// Using map to create a new list with squared values
val squaredNumbers = numbers.map { it * it }
println("Squared numbers: $squaredNumbers")
}
Output:
List of numbers: [1, 2, 3, 4, 5]
Sum of the numbers: 15
Maximum number: 5
List contains 3: true
Squared numbers: [1, 4, 9, 16, 25]
Nullable Types and Safe Calls
In Kotlin, variables can be nullable or non-nullable. We’ll explore safe calls to handle nullable types.
fun main() {
var nullableName: String? = "John Doe"
nullableName = null
// Safe call with the ?. operator
val nameLength = nullableName?.length
println("Name Length: $nameLength")
// Elvis operator ?: to provide a default value
val finalName = nullableName ?: "Unknown"
println("Final Name: $finalName")
}
Output:
Name Length: null
Final Name: Unknown
Class and Object
Let’s create a simple Person
class and an object instance to represent a person’s information.
class Person(val name: String, val age: Int) {
fun introduce() {
println("Hello, my name is $name and I am $age years old.")
}
}
fun main() {
val person = Person("Alice", 30)
person.introduce()
}
Output:
Hello, my name is Alice and I am 30 years old.
I hope these additional examples help you further understand Kotlin programming concepts! If you have any more specific topics you’d like to explore, feel free to ask. Happy coding!
Extension Functions
Extension functions allow you to add new functionality to existing classes without modifying their source code. In this example, we’ll create an extension function for the String
class to simplify string manipulation.
// Extension function to capitalize the first letter of a string
fun String.capitalizeFirstLetter(): String {
return if (isNotEmpty()) {
this.substring(0, 1).toUpperCase() + this.substring(1)
} else {
this
}
}
fun main() {
val text = "hello, world!"
val capitalizedText = text.capitalizeFirstLetter()
println("Original text: $text")
println("Capitalized text: $capitalizedText")
}
Output:
Original text: hello, world!
Capitalized text: Hello, world!
Data Classes
Data classes are special classes in Kotlin designed to hold data. They automatically provide implementations for common methods like equals()
, hashCode()
, and toString()
. Let’s create a simple data class representing a book.
data class Book(val title: String, val author: String, val year: Int)
fun main() {
val book = Book("The Alchemist", "Paulo Coelho", 1988)
println("Book Details: $book")
}
Output:
Book Details: Book(title=The Alchemist, author=Paulo Coelho, year=1988)
Lambdas and Higher-Order Functions
Lambdas are anonymous functions that can be passed as arguments to other functions. We’ll use higher-order functions to filter a list based on a condition.
fun main() {
val numbers = listOf(1, 2, 3, 4, 5, 6)
// Filtering even numbers using a lambda
val evenNumbers = numbers.filter { it % 2 == 0 }
println("Even numbers: $evenNumbers")
// Using a higher-order function to perform custom filtering
val filteredNumbers = filterNumbers(numbers) { num -> num > 3 }
println("Filtered numbers: $filteredNumbers")
}
// Higher-order function to filter numbers based on a condition
fun filterNumbers(numbers: List, condition: (Int) -> Boolean): List {
return numbers.filter { condition(it) }
}
Output:
Even numbers: [2, 4, 6]
Filtered numbers: [4, 5, 6]
I hope you find these examples helpful in exploring more Kotlin features. If you have any specific topics or questions, feel free to ask! Happy coding!
Exception Handling
Exception handling allows you to handle errors gracefully when they occur during the execution of your program. Here’s an example of how to catch and handle exceptions in Kotlin.
fun main() {
try {
val result = divideNumbers(10, 0)
println("Result: $result")
} catch (e: ArithmeticException) {
println("Error: Cannot divide by zero.")
}
}
fun divideNumbers(a: Int, b: Int): Int {
if (b == 0) {
throw ArithmeticException("Division by zero is not allowed.")
}
return a / b
}
Output:
Error: Cannot divide by zero.
Inheritance and Polymorphism
Inheritance allows a class to inherit properties and methods from another class. Polymorphism allows objects of different classes to be treated as objects of a common superclass. Let’s see how this works:
// Base class
open class Shape {
open fun draw() {
println("Drawing a shape.")
}
}
// Derived class
class Circle : Shape() {
override fun draw() {
println("Drawing a circle.")
}
}
// Function to draw any shape
fun drawShape(shape: Shape) {
shape.draw()
}
fun main() {
val shape: Shape = Circle()
drawShape(shape)
}
Output:
Drawing a circle.
Companion Objects
Companion objects are used to create static-like members within a class. These members can be accessed without creating an instance of the class.
class Logger {
companion object {
fun logMessage(message: String) {
println("LOG: $message")
}
}
}
fun main() {
Logger.logMessage("This is a log message.")
}
Output:
LOG: This is a log message.
I hope you find these additional examples useful for your Kotlin learning journey. If you have more specific topics or questions in mind, feel free to ask! Happy coding!
Sealed Classes
Sealed classes are used to represent restricted class hierarchies. They can have a fixed set of subclasses defined within the same file. Here’s an example to demonstrate sealed classes:
sealed class Result
data class Success(val data: String) : Result()
data class Error(val message: String) : Result()
fun processResult(result: Result) {
when (result) {
is Success -> println("Success: ${result.data}")
is Error -> println("Error: ${result.message}")
}
}
fun main() {
val result1 = Success("Data successfully processed.")
val result2 = Error("Something went wrong!")
processResult(result1)
processResult(result2)
}
Output:
Success: Data successfully processed.
Error: Something went wrong!
Coroutines
Coroutines are used for asynchronous programming in Kotlin. They allow you to write asynchronous code in a more sequential and readable manner. Here’s a simple coroutine example:
import kotlinx.coroutines.*
fun main() = runBlocking {
val job = launch {
delay(1000)
println("Coroutines are awesome!")
}
println("Waiting for coroutine to finish...")
job.join()
}
Output:
Waiting for coroutine to finish...
Coroutines are awesome!
Extensions for Standard Library Functions
Kotlin’s standard library provides many useful functions. You can also extend them to add custom functionality. Here’s an example of extending the String
class to get a substring after the last occurrence of a character:
fun String.afterLast(char: Char): String? {
val lastIndex = lastIndexOf(char)
return if (lastIndex != -1 && lastIndex < length - 1) {
substring(lastIndex + 1)
} else {
null
}
}
fun main() {
val text = "example.com/test"
val afterLastSlash = text.afterLast('/')
println("Substring after last slash: $afterLastSlash")
}
Output:
Substring after last slash: test
I hope you find these additional examples helpful in expanding your knowledge of Kotlin. If you have any more specific topics or questions, feel free to ask! Happy coding!
Collections: MutableMap
MutableMap is a collection that represents a map with mutable entries. You can add, update, and remove key-value pairs in a MutableMap. Here’s an example:
fun main() {
// Creating a MutableMap of names and ages
val ages = mutableMapOf(
"John" to 30,
"Alice" to 25,
"Bob" to 28
)
// Adding a new entry to the map
ages["Michael"] = 35
// Updating the age of an existing entry
ages["Alice"] = 26
// Removing an entry from the map
ages.remove("Bob")
// Iterating through the map
for ((name, age) in ages) {
println("$name is $age years old.")
}
}
Output:
John is 30 years old.
Alice is 26 years old.
Michael is 35 years old.
Data Validation with Regular Expressions
Regular expressions allow you to validate and manipulate strings based on specific patterns. Here’s an example of using a regular expression to validate an email address:
fun isValidEmail(email: String): Boolean {
val emailPattern = Regex("[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\\.[a-zA-Z]{2,}")
return email.matches(emailPattern)
}
fun main() {
val email = "john.doe@example.com"
val isValid = isValidEmail(email)
if (isValid) {
println("Valid email address.")
} else {
println("Invalid email address.")
}
}
Output:
Valid email address.
File Handling
Kotlin provides easy-to-use APIs for file handling. Here’s an example of reading and writing to a file:
import java.io.File
fun main() {
// Writing to a file
val content = "Hello, this is the content to be written to the file."
val outputFile = File("output.txt")
outputFile.writeText(content)
// Reading from a file
val inputFile = File("output.txt")
val readContent = inputFile.readText()
println("File Content: $readContent")
}
Output:
File Content: Hello, this is the content to be written to the file.
I hope you find these additional examples useful for your Kotlin learning journey. If you have more specific topics or questions in mind, feel free to ask! Happy coding!
Scope Functions: apply and let
Scope functions in Kotlin provide a concise way to work with objects within a limited scope. The ‘apply’ and ‘let’ functions are commonly used. Here’s an example:
data class Person(var name: String, var age: Int)
fun main() {
val person = Person("Alice", 30)
// 'apply' sets properties and returns the receiver object
val updatedPerson = person.apply {
name = "Bob"
age = 28
}
println("Original Person: $person")
println("Updated Person: $updatedPerson")
// 'let' performs an operation and returns a result
val fullName = person.let { "${it.name} Doe" }
println("Full Name: $fullName")
}
Output:
Original Person: Person(name=Alice, age=30)
Updated Person: Person(name=Bob, age=28)
Full Name: Alice Doe
Delegated Properties
Delegated properties allow you to delegate the property access and modification to another object. Kotlin provides several standard delegates, like ‘lazy’ and ‘observable’. Here’s an example of using the ‘lazy’ delegate:
class Example {
val lazyValue: String by lazy {
println("Initializing lazyValue")
"Hello Lazy"
}
}
fun main() {
val example = Example()
println("Before accessing lazyValue")
println(example.lazyValue)
println("After accessing lazyValue")
}
Output:
Before accessing lazyValue
Initializing lazyValue
Hello Lazy
After accessing lazyValue
Object Expressions
Object expressions allow you to create anonymous objects with their own custom behavior. Here’s an example:
interface Shape {
fun draw()
}
fun main() {
val customShape = object : Shape {
override fun draw() {
println("Drawing a custom shape.")
}
}
customShape.draw()
}
Output:
Drawing a custom shape.
I hope these additional examples help you explore more advanced Kotlin features. If you have any specific topics or questions in mind, feel free to ask! Happy coding!
Destructuring Declarations
Destructuring declarations allow you to extract multiple components from an object at once and assign them to individual variables. Here’s an example:
data class Point(val x: Int, val y: Int)
fun main() {
val point = Point(10, 20)
// Destructuring declaration
val (x, y) = point
println("x: $x, y: $y")
}
Output:
x: 10, y: 20
Enum Classes
Enum classes are used to represent a fixed number of constant values. They can have properties, methods, and constructors. Here’s an example:
enum class Direction {
NORTH,
SOUTH,
EAST,
WEST
}
fun main() {
val currentDirection = Direction.NORTH
println("Current Direction: $currentDirection")
}
Output:
Current Direction: NORTH
25. Tail Recursive Functions
Tail recursion is a technique used to optimize recursive functions. It allows the compiler to reuse the current stack frame instead of creating new ones for each recursive call. Here’s an example:
tailrec fun factorial(n: Int, result: Int = 1): Int {
return if (n == 0) {
result
} else {
factorial(n - 1, result * n)
}
}
fun main() {
val n = 5
val factorialResult = factorial(n)
println("Factorial of $n is $factorialResult")
}
Output:
Factorial of 5 is 120
I hope you find these additional examples helpful in further exploring Kotlin. If you have more specific topics or questions in mind, feel free to ask! Happy coding!
Extension Properties
Extension properties allow you to add new properties to existing classes without modifying their source code. Here’s an example:
class Rectangle(var width: Int, var height: Int)
val Rectangle.area: Int
get() = width * height
fun main() {
val rectangle = Rectangle(5, 10)
println("Rectangle Area: ${rectangle.area}")
}
Output:
Rectangle Area: 50
Local Functions
In Kotlin, you can define functions within other functions. These are called local functions and can only be accessed within the enclosing function. Here’s an example:
fun main() {
fun greet(name: String) {
fun formatName(name: String): String {
return name.trim().capitalize()
}
val formattedName = formatName(name)
println("Hello, $formattedName!")
}
greet(" Alice ")
}
Output:
Hello, Alice!
Custom Getters and Setters
You can define custom getters and setters for properties in Kotlin. This allows you to control how the property is accessed and modified. Here’s an example:
class Circle(var radius: Double) {
var diameter: Double
get() = radius * 2
set(value) {
radius = value / 2
}
}
fun main() {
val circle = Circle(5.0)
println("Diameter: ${circle.diameter}")
circle.diameter = 10.0
println("New Radius: ${circle.radius}")
}
Output:
Diameter: 10.0
New Radius: 5.0
I hope you find these additional examples useful for your Kotlin learning journey. If you have more specific topics or questions in mind, feel free to ask! Happy coding!
Higher-Order Functions with Lambda Arguments
Higher-order functions can take other functions as arguments, allowing for powerful and flexible code. Here’s an example of a higher-order function that performs a calculation using a lambda argument:
fun calculate(a: Int, b: Int, operation: (Int, Int) -> Int): Int {
return operation(a, b)
}
fun main() {
val addition = calculate(5, 3) { a, b -> a + b }
println("Addition: $addition")
val multiplication = calculate(5, 3) { a, b -> a * b }
println("Multiplication: $multiplication")
}
Output:
Addition: 8
Multiplication: 15
Inline Functions
The ‘inline’ modifier can be used for higher-order functions to improve performance. It eliminates the overhead of function calls by inlining the function’s body directly at the call site. Here’s an example:
inline fun measureTime(block: () -> Unit) {
val startTime = System.currentTimeMillis()
block()
val endTime = System.currentTimeMillis()
println("Time taken: ${endTime - startTime} ms")
}
fun main() {
measureTime {
for (i in 1..1000000) {
// Some time-consuming operation
}
}
}
Output:
Time taken: [Time in milliseconds]
Type Aliases
Type aliases allow you to create alternative names for existing types. This can be useful to simplify long or complex type names. Here’s an example:
typealias EmployeeId = Int
typealias EmployeeMap = Map
fun main() {
val employees: EmployeeMap = mapOf(
1 to "Alice",
2 to "Bob",
3 to "Charlie"
)
println("Employee Map: $employees")
}
Output:
Employee Map: {1=Alice, 2=Bob, 3=Charlie}
I hope these additional examples help you further explore Kotlin’s features. If you have more specific topics or questions in mind, feel free to ask! Happy coding!
Sealed Interfaces
Kotlin 1.5 introduced sealed interfaces, which are similar to sealed classes but used for interfaces. Sealed interfaces restrict their implementations to be declared within the same file. Here’s an example:
sealed interface Vehicle {
fun start()
}
class Car : Vehicle {
override fun start() {
println("Car started")
}
}
class Motorcycle : Vehicle {
override fun start() {
println("Motorcycle started")
}
}
fun main() {
val car: Vehicle = Car()
val motorcycle: Vehicle = Motorcycle()
car.start()
motorcycle.start()
}
Output:
Car started
Motorcycle started
Reified Type Parameters
The ‘reified’ modifier allows you to access the actual type of a generic parameter at runtime. This is useful when working with inline functions and reflective operations. Here’s an example:
inline fun getTypeName() = T::class.simpleName
fun main() {
val typeName = getTypeName()
println("Type Name: $typeName")
}
Output:
Type Name: Int
Object Declaration
Object declarations allow you to create singletons in Kotlin. The object is created lazily and initialized the first time it is accessed. Here’s an example:
object Logger {
fun log(message: String) {
println("LOG: $message")
}
}
fun main() {
Logger.log("This is a log message.")
}
Output:
LOG: This is a log message.
I hope these additional examples continue to help you explore more advanced Kotlin features. If you have more specific topics or questions in mind, feel free to ask! Happy coding!
Smart Casts
Kotlin’s smart casts automatically cast a variable to a specific type based on a type check. This reduces the need for explicit type casting. Here’s an example:
fun printLength(obj: Any) {
if (obj is String) {
// 'obj' is automatically cast to String inside this block
println("Length of the string: ${obj.length}")
} else {
println("Not a string")
}
}
fun main() {
printLength("Hello, Kotlin")
printLength(42)
}
Output:
Length of the string: 12
Not a string
Non-Null Assertion
The non-null assertion operator (!!
) is used to assert that a nullable variable is not null. It throws a NullPointerException
if the variable is null. Use it with caution as it can lead to runtime exceptions. Here’s an example:
fun printLength(str: String?) {
// Using the non-null assertion operator
val length = str!!.length
println("Length of the string: $length")
}
fun main() {
printLength("Hello, Kotlin")
printLength(null) // This will throw a NullPointerException
}
Output:
Length of the string: 12
Exception in thread "main" kotlin.KotlinNullPointerException
Delegated Properties with by lazy
The ‘by lazy’ delegate allows for lazy initialization of properties. The property will be computed only when it’s accessed for the first time. Subsequent accesses will use the cached value. Here’s an example:
val pi: Double by lazy {
println("Calculating PI...")
3.14159
}
fun main() {
println("Before accessing PI")
println("PI: $pi")
println("After accessing PI")
}
Output:
Before accessing PI
Calculating PI...
PI: 3.14159
After accessing PI
I hope you find these additional examples helpful for your Kotlin learning journey. If you have more specific topics or questions in mind, feel free to ask! Happy coding!
Default Arguments and Named Arguments
Kotlin allows you to define default values for function parameters. This way, you can call functions without providing all the arguments. You can also use named arguments to specify parameters by their names. Here’s an example:
fun greet(name: String = "Guest", age: Int = 0) {
println("Hello, $name! You are $age years old.")
}
fun main() {
greet("Alice", 30)
greet("Bob")
greet(age = 25)
}
Output:
Hello, Alice! You are 30 years old.
Hello, Bob! You are 0 years old.
Hello, Guest! You are 25 years old.
when Expression with Multiple Conditions
Kotlin’s ‘when’ expression can handle multiple conditions in a single branch using commas. It’s a concise way to handle multiple cases. Here’s an example:
fun describeDay(day: String) {
when (day) {
"Monday", "Tuesday", "Wednesday", "Thursday", "Friday" -> println("Weekday")
"Saturday", "Sunday" -> println("Weekend")
else -> println("Invalid day")
}
}
fun main() {
describeDay("Monday")
describeDay("Saturday")
describeDay("Sunday")
describeDay("Wednesday")
describeDay("Friday")
describeDay("Holiday")
}
Output:
Weekday
Weekend
Weekend
Weekday
Weekday
Invalid day
Using the in Operator
The ‘in’ operator checks if a value is present in a range, a collection, or a string. It’s a concise way to perform containment checks. Here’s an example:
fun main() {
val num = 5
if (num in 1..10) {
println("$num is between 1 and 10")
}
val names = listOf("Alice", "Bob", "Charlie")
if ("Alice" in names) {
println("Alice is in the list")
}
}
Output:
5 is between 1 and 10
Alice is in the list
I hope you find these additional examples helpful for your Kotlin learning journey. If you have more specific topics or questions in mind, feel free to ask! Happy coding!
Extension Functions for Companion Objects
You can also create extension functions for companion objects of a class. This allows you to add new functionality to the companion object. Here’s an example:
class MyClass {
companion object {
fun hello() {
println("Hello from the companion object!")
}
}
}
fun MyClass.Companion.greet() {
println("Greetings from the extended companion object!")
}
fun main() {
MyClass.hello()
MyClass.greet()
}
Output:
Hello from the companion object!
Greetings from the extended companion object!
Functional Programming with map, filter, and reduce
Kotlin supports functional programming with higher-order functions like ‘map’, ‘filter’, and ‘reduce’. Here’s an example of using these functions:
fun main() {
val numbers = listOf(1, 2, 3, 4, 5)
// Using 'map' to square each number
val squaredNumbers = numbers.map { it * it }
println("Squared numbers: $squaredNumbers")
// Using 'filter' to get even numbers
val evenNumbers = numbers.filter { it % 2 == 0 }
println("Even numbers: $evenNumbers")
// Using 'reduce' to calculate the sum of numbers
val sum = numbers.reduce { acc, number -> acc + number }
println("Sum of numbers: $sum")
}
Output:
Squared numbers: [1, 4, 9, 16, 25]
Even numbers: [2, 4]
Sum of numbers: 15
Elvis Operator and Safe Calls
The Elvis operator (?:
) provides a default value when dealing with nullable variables. Safe calls (?.
) allow you to safely access properties and methods of nullable objects. Here’s an example:
fun main() {
val name: String? = null
val length = name?.length ?: 0
println("Length of name: $length")
}
Output:
Length of name: 0
I hope you find these additional examples helpful for your Kotlin learning journey. If you have more specific topics or questions in mind, feel free to ask! Happy coding!
Coroutines with Async and Await
Coroutines can perform asynchronous operations using async
and await
to retrieve results. This allows you to perform concurrent tasks efficiently. Here’s an example:
import kotlinx.coroutines.*
suspend fun fetchUserData(userId: Int): String {
// Simulate an API call with a delay
delay(1000)
return "User data for ID $userId"
}
fun main() = runBlocking {
val userIds = listOf(1, 2, 3)
// Launch coroutines to fetch user data concurrently
val deferredResults = userIds.map { userId ->
async { fetchUserData(userId) }
}
// Await all the results and print them
deferredResults.awaitAll().forEach {
println(it)
}
}
Output:
User data for ID 1
User data for ID 2
User data for ID 3
Collections: associateBy and groupingBy
Kotlin’s standard library provides useful collection functions. The associateBy
function creates a map where keys are derived from elements, and groupingBy
groups elements based on a specified key selector. Here’s an example:
data class Person(val name: String, val age: Int)
fun main() {
val people = listOf(
Person("Alice", 30),
Person("Bob", 25),
Person("Charlie", 30)
)
// Creating a map with name as key and person as value
val nameToPersonMap = people.associateBy { it.name }
println("Name to Person Map: $nameToPersonMap")
// Grouping people by age
val peopleByAge = people.groupingBy { it.age }.eachCount()
println("People grouped by age: $peopleByAge")
}
Output:
Name to Person Map: {Alice=Person(name=Alice, age=30), Bob=Person(name=Bob, age=25), Charlie=Person(name=Charlie, age=30)}
People grouped by age: {30=2, 25=1}
I hope these additional examples help you further explore Kotlin. If you have more specific topics or questions in mind, feel free to ask! Happy coding!
Extension Functions on Nullable Types
You can define extension functions on nullable types, allowing you to perform operations on nullable variables. Here’s an example:
fun String?.isNullOrBlankOrEmpty(): Boolean {
return this == null || this.isBlank()
}
fun main() {
val str1: String? = null
val str2: String? = ""
val str3: String? = "Hello, Kotlin!"
println("str1 is blank or empty: ${str1.isNullOrBlankOrEmpty()}")
println("str2 is blank or empty: ${str2.isNullOrBlankOrEmpty()}")
println("str3 is blank or empty: ${str3.isNullOrBlankOrEmpty()}")
}
Output:
str1 is blank or empty: true
str2 is blank or empty: true
str3 is blank or empty: false
Inline Classes
Kotlin 1.3 introduced inline classes, which are used to wrap a single value and add new functionality to it. They are similar to primitive types but with additional methods. Here’s an example:
inline class ZipCode(val value: String)
fun main() {
val zipCode = ZipCode("12345")
println("Zip Code: ${zipCode.value}")
}
Output:
Zip Code: 12345
Dynamic Type Checks
Kotlin provides the ‘is’ and ‘as’ operators for dynamic type checks and type casting. Here’s an example:
fun printType(obj: Any) {
if (obj is String) {
println("$obj is a String")
} else if (obj is Int) {
println("$obj is an Int")
} else {
println("Unknown type")
}
}
fun main() {
printType("Hello")
printType(42)
printType(3.14)
}
Output:
Hello is a String
42 is an Int
Unknown type
I hope these additional examples help you further explore Kotlin. If you have more specific topics or questions in mind, feel free to ask! Happy coding!
Custom Annotations
Kotlin allows you to create custom annotations to add metadata or behavior to your code. Here’s an example of defining and using a custom annotation:
@Target(AnnotationTarget.CLASS)
@Retention(AnnotationRetention.RUNTIME)
annotation class MyAnnotation
@MyAnnotation
class MyClass
fun main() {
val annotation = MyClass::class.annotations.find { it.annotationClass == MyAnnotation::class }
if (annotation != null) {
println("MyAnnotation found on MyClass")
} else {
println("MyAnnotation not found on MyClass")
}
}
Output:
MyAnnotation found on MyClass
Data Serialization with Kotlinx Serialization
Kotlinx Serialization is a multiplatform library that provides serialization and deserialization support for Kotlin classes. Here’s an example of using Kotlinx Serialization:
import kotlinx.serialization.Serializable
import kotlinx.serialization.json.Json
@Serializable
data class Person(val name: String, val age: Int)
fun main() {
val person = Person("Alice", 30)
// Serialize the object to JSON
val json = Json.encodeToString(Person.serializer(), person)
println("Serialized JSON: $json")
// Deserialize JSON to an object
val deserializedPerson = Json.decodeFromString(Person.serializer(), json)
println("Deserialized Person: $deserializedPerson")
}
Output:
Serialized JSON: {"name":"Alice","age":30}
Deserialized Person: Person(name=Alice, age=30)
Type Projection with Generics
Kotlin supports type projection with generics using the out
and in
keywords. This allows you to work with subtypes and supertypes. Here’s an example:
interface Source {
fun get(): T
}
fun getSource(): Source {
return object : Source {
override fun get(): String = "Hello, Kotlin!"
}
}
fun main() {
val source: Source = getSource()
val value: Any = source.get()
println("Value: $value")
}
Output:
Value: Hello, Kotlin!
I hope you find these additional examples helpful for your Kotlin learning journey. If you have more specific topics or questions in mind, feel free to ask! Happy coding!
Type Aliases for Function Types
Kotlin allows you to create type aliases for function types, which can make code more readable, especially for complex function types. Here’s an example:
typealias Operation = (Int, Int) -> Int
fun add(a: Int, b: Int): Int = a + b
fun subtract(a: Int, b: Int): Int = a - b
fun main() {
val operation: Operation = ::add
println("Result of add operation: ${operation(5, 3)}")
val anotherOperation: Operation = ::subtract
println("Result of subtract operation: ${anotherOperation(5, 3)}")
}
Output:
Result of add operation: 8
Result of subtract operation: 2
Type-Safe Builders with DSL
Domain-Specific Languages (DSLs) can be created in Kotlin using type-safe builders. This allows you to create more concise and expressive code for specific tasks. Here’s an example:
class Person {
var name: String = ""
var age: Int = 0
}
fun person(block: Person.() -> Unit): Person {
val person = Person()
person.block()
return person
}
fun main() {
val person = person {
name = "Alice"
age = 30
}
println("Person: ${person.name}, ${person.age} years old.")
}
Output:
Person: Alice, 30 years old.
Annotation Processing with KotlinPoet
KotlinPoet is a library for generating Kotlin code through a Kotlin DSL. It is often used for annotation processing and code generation tasks. Here’s a simple example:
import com.squareup.kotlinpoet.FileSpec
import com.squareup.kotlinpoet.FunSpec
fun main() {
val mainFun = FunSpec.builder("main")
.addStatement("println(\"Hello, KotlinPoet!\")")
.build()
val file = FileSpec.builder("com.example", "HelloWorld")
.addFunction(mainFun)
.build()
file.writeTo(System.out)
}
Output:
package com.example
fun main() {
println("Hello, KotlinPoet!")
}
I hope you find these additional examples helpful for your Kotlin learning journey. If you have more specific topics or questions in mind, feel free to ask! Happy coding!
Sealed Properties in Data Classes
Kotlin 1.5 introduced a new feature called “sealed properties” in data classes. It allows you to mark individual properties in a data class as sealed, restricting their subclasses and enforcing a fixed set of subclasses. Here’s an example:
sealed interface Shape
data class Circle(val radius: Double) : Shape()
data class Square(val sideLength: Double) : Shape()
fun calculateArea(shape: Shape): Double {
return when (shape) {
is Circle -> Math.PI * shape.radius * shape.radius
is Square -> shape.sideLength * shape.sideLength
}
}
fun main() {
val circle = Circle(5.0)
val square = Square(4.0)
println("Circle area: ${calculateArea(circle)}")
println("Square area: ${calculateArea(square)}")
}
Output:
Circle area: 78.53981633974483
Square area: 16.0
Custom DSL with Lambda Extensions
You can create custom DSLs in Kotlin using lambda extensions to build more expressive and domain-specific code. Here’s an example of creating a simple HTML DSL:
class Tag(val name: String) {
private val children = mutableListOf()
fun tag(name: String, block: Tag.() -> Unit) {
val child = Tag(name)
child.block()
children.add(child)
}
override fun toString(): String {
return "<$name>${children.joinToString("")}"
}
}
fun html(block: Tag.() -> Unit): Tag {
val root = Tag("html")
root.block()
return root
}
fun main() {
val result = html {
tag("head") {
tag("title") {
"Page Title"
}
}
tag("body") {
tag("h1") {
"Hello, DSL!"
}
tag("p") {
"This is a custom DSL example."
}
}
}
println(result)
}
Output:
Page TitleHello, DSL!This is a custom DSL example.
I hope you find these additional examples helpful for your Kotlin learning journey. If you have more specific topics or questions in mind, feel free to ask! Happy coding!
Companion Object Extensions
In Kotlin, you can also add extension functions to companion objects. This allows you to add behavior to the companion object without modifying its source code. Here’s an example:
class MyClass {
companion object {
fun greet() {
println("Hello from the companion object!")
}
}
}
fun MyClass.Companion.sayHello() {
println("Hello from the extended companion object!")
}
fun main() {
MyClass.greet()
MyClass.sayHello()
}
Output:
Hello from the companion object!
Hello from the extended companion object!
Variance: out and in Modifiers with Generics
The ‘out’ and ‘in’ modifiers are used in generic types to specify variance. ‘out’ makes a type parameter covariant (read-only), while ‘in’ makes it contravariant (write-only). Here’s an example:
interface Producer {
fun produce(): T
}
interface Consumer {
fun consume(item: T)
}
fun main() {
val stringProducer: Producer = object : Producer {
override fun produce(): String = "Hello, Kotlin!"
}
val anyConsumer: Consumer = object : Consumer {
override fun consume(item: Any) {
println("Consuming: $item")
}
}
val item: Any = stringProducer.produce()
anyConsumer.consume(item)
}
Output:
Consuming: Hello, Kotlin!
Reified Type Parameters with inline
The ‘reified’ modifier can only be used with ‘inline’ functions to access the actual type of a generic parameter at runtime. This allows you to perform operations that require type information. Here’s an example:
inline fun printType() {
println("Type: ${T::class.simpleName}")
}
fun main() {
printType()
printType()
printType()
}
Output:
Type: String
Type: Int
Type: Double
I hope you find these additional examples helpful for your Kotlin learning journey. If you have more specific topics or questions in mind, feel free to ask! Happy coding!
Type Erasure and Inline Reified Types
Kotlin, like Java, suffers from type erasure when working with generic types. However, using inline
functions with reified
type parameters, you can overcome this limitation and access the generic type’s information at runtime. Here’s an example:
inline fun printType(list: List) {
println("List of ${T::class.simpleName}: $list")
}
fun main() {
val listInt = listOf(1, 2, 3)
val listString = listOf("a", "b", "c")
printType(listInt)
printType(listString)
}
Output:
List of Int: [1, 2, 3]
List of String: [a, b, c]
Destructuring Declarations in Loops
Kotlin allows destructuring declarations in loops, making it easier to iterate over elements of pairs, triples, or custom data classes. Here’s an example:
data class Person(val name: String, val age: Int)
fun main() {
val people = listOf(Person("Alice", 30), Person("Bob", 25), Person("Charlie", 40))
for ((name, age) in people) {
println("$name is $age years old.")
}
}
Output:
Alice is 30 years old.
Bob is 25 years old.
Charlie is 40 years old.
String Interpolation with Triple Quotes
Kotlin supports triple-quoted strings for multiline strings, preserving line breaks and allowing string interpolation. Here’s an example:
fun main() {
val message = """
|Hello, Kotlin!
|This is a multiline string.
|${2 + 3} equals 5.
""".trimMargin()
println(message)
}
Output:
Hello, Kotlin!
This is a multiline string.
5 equals 5.
I hope you find these additional examples helpful for your Kotlin learning journey. If you have more specific topics or questions in mind, feel free to ask! Happy coding!
Coroutines: Exception Handling
In coroutines, you can handle exceptions using try-catch
blocks, similar to regular synchronous code. Here’s an example:
import kotlinx.coroutines.*
suspend fun doSomething() {
delay(100)
throw RuntimeException("Oops, something went wrong!")
}
fun main() = runBlocking {
try {
doSomething()
} catch (e: Exception) {
println("Caught exception: ${e.message}")
}
}
Output:
Caught exception: Oops, something went wrong!
Functional Programming with let, run, with, apply, and also
Kotlin provides various scope functions that make functional programming more concise. These functions are let
, run
, with
, apply
, and also
. Here’s an example of using each of them:
data class Person(var name: String, var age: Int)
fun main() {
val person = Person("Alice", 30)
val letResult = person.let {
it.name = "Bob"
it.age + 5
}
println("letResult: $letResult, person: $person")
val runResult = person.run {
name = "Charlie"
age + 10
}
println("runResult: $runResult, person: $person")
val withResult = with(person) {
name = "David"
age + 15
}
println("withResult: $withResult, person: $person")
val applyResult = person.apply {
name = "Eva"
age + 20
}
println("applyResult: $applyResult, person: $person")
val alsoResult = person.also {
it.name = "Fiona"
it.age + 25
}
println("alsoResult: $alsoResult, person: $person")
}
Output:
letResult: 35, person: Person(name=Bob, age=30)
runResult: 40, person: Person(name=Charlie, age=40)
withResult: 45, person: Person(name=David, age=45)
applyResult: Person(name=Eva, age=45), person: Person(name=Eva, age=45)
alsoResult: Person(name=Fiona, age=45), person: Person(name=Fiona, age=45)
I hope you find these additional examples helpful for your Kotlin learning journey. If you have more specific topics or questions in mind, feel free to ask! Happy coding!
Sure, let’s continue exploring more Kotlin concepts:
Sealed Classes and when Expression
Sealed classes are used to represent restricted class hierarchies. They can be used in combination with the when
expression for exhaustive checks. Here’s an example:
sealed class Result
data class Success(val data: String) : Result()
data class Error(val message: String) : Result()
fun processResult(result: Result) {
when (result) {
is Success -> println("Data: ${result.data}")
is Error -> println("Error: ${result.message}")
}
}
fun main() {
val successResult = Success("Kotlin is awesome!")
val errorResult = Error("Something went wrong!")
processResult(successResult)
processResult(errorResult)
}
Output:
Data: Kotlin is awesome!
Error: Something went wrong!
@JvmOverloads Annotation
The @JvmOverloads
annotation is used when defining functions with default parameter values. It instructs the Kotlin compiler to generate overloaded versions of the function for Java interoperability. Here’s an example:
class MyCalculator {
@JvmOverloads
fun add(a: Int, b: Int, c: Int = 0): Int {
return a + b + c
}
}
fun main() {
val calculator = MyCalculator()
println(calculator.add(2, 3))
println(calculator.add(2, 3, 5))
}
Output:
5
10
Infix Functions
Infix functions allow you to call a function with a single parameter using the infix notation. It can make your code more readable when working with certain operations. Here’s an example:
infix fun Int.multiplyBy(x: Int): Int {
return this * x
}
fun main() {
val result = 5 multiplyBy 3
println("Result: $result")
}
Output:
Result: 15
I hope you find these additional examples helpful for your Kotlin learning journey. If you have more specific topics or questions in mind, feel free to ask! Happy coding!
Delegated Properties: lazy, observable, and vetoable
Kotlin provides three useful delegated property types: lazy
, observable
, and vetoable
.
lazy
: Lazily initializes the value and caches it for subsequent accesses.observable
: Adds a listener that observes changes to the property value.vetoable
: Allows you to veto changes to the property by providing a custom validation.
Here’s an example:
import kotlin.properties.Delegates
class Example {
val lazyValue: String by lazy {
println("Initializing lazyValue")
"Lazy value"
}
var observableValue: String by Delegates.observable("Initial value") { _, oldValue, newValue ->
println("Old value: $oldValue, New value: $newValue")
}
var vetoableValue: Int by Delegates.vetoable(0) { _, oldValue, newValue ->
newValue >= oldValue
}
}
fun main() {
val example = Example()
println(example.lazyValue)
println(example.lazyValue)
example.observableValue = "Updated value"
example.observableValue = "Another update"
println("vetoableValue: ${example.vetoableValue}")
example.vetoableValue = 10
println("vetoableValue: ${example.vetoableValue}")
example.vetoableValue = 5
}
Output:
Initializing lazyValue
Lazy value
Lazy value
Old value: Initial value, New value: Updated value
Old value: Updated value, New value: Another update
vetoableValue: 0
vetoableValue: 10
Type Aliases for Complex Types
Kotlin allows you to create type aliases for complex types, making your code more concise and readable. Here’s an example:
typealias DoubleList = List
typealias StringMap = Map
fun main() {
val doubleList: DoubleList = listOf(1.0, 2.0, 3.0)
val stringMap: StringMap = mapOf("one" to 1, "two" to 2, "three" to 3)
println("Double List: $doubleList")
println("String Map: $stringMap")
}
Output:
Double List: [1.0, 2.0, 3.0]
String Map: {one=1, two=2, three=3}
Custom Setters and Getters
In Kotlin, you can define custom setters and getters for properties, allowing you to add logic when getting or setting values. Here’s an example:
class Person {
var name: String = ""
get() {
println("Getting name: $field")
return field
}
set(value) {
println("Setting name to $value")
field = value
}
}
fun main() {
val person = Person()
person.name = "Alice"
println("Name: ${person.name}")
}
Output:
Setting name to Alice
Getting name: Alice
Name: Alice
I hope you find these additional examples helpful for your Kotlin learning journey. If you have more specific topics or questions in mind, feel free to ask! Happy coding!
Local Functions
In Kotlin, you can define functions inside other functions, known as local functions. These functions have access to the variables in the enclosing function and can help you encapsulate logic. Here’s an example:
fun main() {
fun printMessage(message: String) {
println("Message: $message")
}
printMessage("Hello, Kotlin!")
}
Output:
Message: Hello, Kotlin!
Tail Recursive Functions
Kotlin supports tail recursion optimization for functions that call themselves as the last operation. This can prevent stack overflow errors for large recursive calls. Here’s an example:
tailrec fun factorial(n: Int, acc: Int = 1): Int {
return if (n == 0) acc else factorial(n - 1, acc * n)
}
fun main() {
val result = factorial(5)
println("Factorial of 5: $result")
}
Output:
Factorial of 5: 120
Enum Classes and Properties
Enum classes in Kotlin can have properties, just like regular classes. This allows you to add additional data to each enum constant. Here’s an example:
enum class Color(val hexCode: String) {
RED("#FF0000"),
GREEN("#00FF00"),
BLUE("#0000FF")
}
fun main() {
println("Red Hex Code: ${Color.RED.hexCode}")
println("Green Hex Code: ${Color.GREEN.hexCode}")
println("Blue Hex Code: ${Color.BLUE.hexCode}")
}
Output:
Red Hex Code: #FF0000
Green Hex Code: #00FF00
Blue Hex Code: #0000FF
I hope you find these additional examples helpful for your Kotlin learning journey. If you have more specific topics or questions in mind, feel free to ask! Happy coding!
Extension Properties
In Kotlin, you can also define extension properties to add new properties to existing classes without modifying their source code. Here’s an example:
class Rectangle(val width: Double, val height: Double)
val Rectangle.area: Double
get() = width * height
fun main() {
val rectangle = Rectangle(5.0, 3.0)
println("Rectangle area: ${rectangle.area}")
}
Output:
Rectangle area: 15.0
Late-Initialized Properties
In Kotlin, you can use the lateinit
modifier to mark properties that will be initialized later (after object creation). The property must be a non-nullable type and should be initialized before accessing it, or it will throw a LateinitPropertyAccessException
. Here’s an example:
class Example {
lateinit var name: String
fun init() {
name = "Kotlin"
}
}
fun main() {
val example = Example()
example.init()
println("Name: ${example.name}")
}
Output:
Name: Kotlin
Tailrec Modifier in Interface Functions
Starting from Kotlin 1.5, you can use the tailrec
modifier in interface functions, just like in regular functions. This allows tail call optimization in implementing classes. Here’s an example:
interface FactorialCalculator {
tailrec fun factorial(n: Int, acc: Int = 1): Int {
return if (n == 0) acc else factorial(n - 1, acc * n)
}
}
class FactorialImpl : FactorialCalculator
fun main() {
val factorialImpl = FactorialImpl()
val result = factorialImpl.factorial(5)
println("Factorial of 5: $result")
}
Output:
Factorial of 5: 120
I hope you find these additional examples helpful for your Kotlin learning journey. If you have more specific topics or questions in mind, feel free to ask! Happy coding!
Secondary Constructors
Kotlin allows you to define secondary constructors in a class, providing alternative ways to initialize objects. Here’s an example:
class Person(val name: String) {
var age: Int = 0
constructor(name: String, age: Int) : this(name) {
this.age = age
}
}
fun main() {
val person1 = Person("Alice")
val person2 = Person("Bob", 25)
println("Person 1: ${person1.name}, ${person1.age} years old.")
println("Person 2: ${person2.name}, ${person2.age} years old.")
}
Output:
Person 1: Alice, 0 years old.
Person 2: Bob, 25 years old.
Companion Object Extensions with Operator Overloading
You can define operator overloads as extension functions on the companion object to provide additional behavior to your classes. Here’s an example:
data class Point(val x: Int, val y: Int) {
companion object {
operator fun Point.plus(other: Point): Point {
return Point(x + other.x, y + other.y)
}
}
}
fun main() {
val p1 = Point(2, 3)
val p2 = Point(4, 5)
val sum = p1 + p2
println("Sum: (${sum.x}, ${sum.y})")
}
Output:
Sum: (6, 8)
Ranges and Progressions
Kotlin provides ranges and progressions to represent sequences of values. Here’s an example:
fun main() {
// Closed range [1, 10]
val range = 1..10
// Half-open range [1, 10)
val halfOpenRange = 1 until 10
// Range with step 2 [1, 3, 5, 7, 9]
val steppedRange = 1..10 step 2
// Reverse range [10, 9, 8, 7, 6, 5, 4, 3, 2, 1]
val reversedRange = 10 downTo 1
// Print the ranges
println("Range: $range")
println("Half-Open Range: $halfOpenRange")
println("Stepped Range: $steppedRange")
println("Reversed Range: $reversedRange")
// Iterate through the range
for (i in 1..5) {
print("$i ")
}
}
Output:
Range: 1..10
Half-Open Range: 1 until 10
Stepped Range: 1..9 step 2
Reversed Range: 10 downTo 1
1 2 3 4 5
Named Arguments
In Kotlin, you can use named arguments to specify function parameters by their names, regardless of their order. This can make your code more readable. Here’s an example:
fun greetPerson(name: String, age: Int) {
println("Hello, $name! You are $age years old.")
}
fun main() {
greetPerson(name = "Alice", age = 30)
greetPerson(age = 25, name = "Bob")
}
Output:
Hello, Alice! You are 30 years old.
Hello, Bob! You are 25 years old.
I hope you find these additional examples helpful for your Kotlin learning journey. If you have more specific topics or questions in mind, feel free to ask! Happy coding!
Extension Function Inference
Kotlin allows you to call extension functions directly on nullable receiver objects without explicit null-checks. The extension function will be called only when the receiver is non-null. Here’s an example:
fun String?.printLength() {
if (this != null) {
println("Length of the string: ${this.length}")
} else {
println("String is null")
}
}
fun main() {
val str1: String? = null
val str2: String? = "Hello, Kotlin!"
str1.printLength()
str2.printLength()
}
Output:
String is null
Length of the string: 13
Function Types with Receiver (Function Literals with Receiver)
Kotlin allows you to define function types with a receiver, also known as function literals with receiver. This allows you to access the receiver object inside the function block using the this
keyword. Here’s an example:
val greet: String.(String) -> String = { name -> "Hello, $name! I'm $this" }
fun main() {
val greeting1 = "Alice".greet("Bob")
val greeting2 = "Kotlin".greet("Developer")
println(greeting1)
println(greeting2)
}
Output:
Hello, Bob! I'm Alice
Hello, Developer! I'm Kotlin
Type-Safe Builders for HTML DSL
Kotlin allows you to create type-safe builders for DSLs, making it easier to generate structured code like HTML. Here’s an example:
class Tag(val name: String) {
private val children = mutableListOf()
fun tag(name: String, block: Tag.() -> Unit) {
val child = Tag(name)
child.block()
children.add(child)
}
override fun toString(): String {
return buildString {
append("<$name>")
children.forEach {
append(it)
}
append("")
}
}
}
fun html(block: Tag.() -> Unit): Tag {
val root = Tag("html")
root.block()
return root
}
fun main() {
val result = html {
tag("head") {
tag("title") {
+"Page Title"
}
}
tag("body") {
tag("h1") {
+"Hello, DSL!"
}
tag("p") {
+"This is a custom HTML DSL example."
}
}
}
println(result)
}
Output:
Page TitleHello, DSL!This is a custom HTML DSL example.
I hope you find these additional examples helpful for your Kotlin learning journey. If you have more specific topics or questions in mind, feel free to ask! Happy coding!
Type Inference in Lambdas
In Kotlin, you can rely on type inference for lambda parameters. If the lambda is passed as an argument to a function with a functional interface parameter, the compiler can often infer the types of the lambda parameters automatically. Here’s an example:
fun processNumbers(numbers: List, operation: (Int) -> Int): List {
return numbers.map { operation(it) }
}
fun main() {
val numbers = listOf(1, 2, 3, 4, 5)
val squaredNumbers = processNumbers(numbers) { it * it }
val doubledNumbers = processNumbers(numbers) { it * 2 }
println("Squared Numbers: $squaredNumbers")
println("Doubled Numbers: $doubledNumbers")
}
Output:
Squared Numbers: [1, 4, 9, 16, 25]
Doubled Numbers: [2, 4, 6, 8, 10]
Function Composition
Kotlin allows you to compose functions using the andThen
and compose
methods, which are available in functional interfaces. Here’s an example:
val addOne: (Int) -> Int = { it + 1 }
val double: (Int) -> Int = { it * 2 }
val addOneAndThenDouble = addOne andThen double
val doubleAndThenAddOne = double compose addOne
fun main() {
val result1 = addOneAndThenDouble(5)
val result2 = doubleAndThenAddOne(5)
println("Result1: $result1")
println("Result2: $result2")
}
Output:
Result1: 12
Result2: 11
Inline Classes
Kotlin 1.3 introduced inline classes, which allow you to create lightweight wrapper classes. Inline classes are replaced with their underlying types at compile-time, reducing runtime overhead. Here’s an example:
inline class Username(val value: String)
fun printUsername(username: Username) {
println("Username: ${username.value}")
}
fun main() {
val username = Username("kotlin_user")
printUsername(username)
}
Output:
Username: kotlin_user
I hope you find these additional examples helpful for your Kotlin learning journey. If you have more specific topics or questions in mind, feel free to ask! Happy coding!
Extension Functions for Standard Library Types
You can create extension functions for standard library types in Kotlin, making it easier to add utility functions to built-in classes. Here’s an example:
fun String.removeWhitespace(): String {
return this.replace("\\s+".toRegex(), "")
}
fun List.sumAll(): Int {
return this.reduce { acc, i -> acc + i }
}
fun main() {
val text = " Hello Kotlin "
val numbers = listOf(1, 2, 3, 4, 5)
val cleanedText = text.removeWhitespace()
val totalSum = numbers.sumAll()
println("Cleaned Text: '$cleanedText'")
println("Total Sum: $totalSum")
}
Output:
Cleaned Text: 'HelloKotlin'
Total Sum: 15
Inlined Higher-Order Functions
In Kotlin, you can use the inline
modifier for higher-order functions. Inlining the function reduces the overhead of creating lambda objects, leading to performance improvements in certain cases. Here’s an example:
inline fun execute(action: () -> Unit) {
println("Start execution")
action()
println("End execution")
}
fun main() {
execute {
println("Inside inline function")
}
}
Output:
Start execution
Inside inline function
End execution
Type Erasure and Reified Type Parameters
In Kotlin, generic types undergo type erasure, and their type arguments are not available at runtime. However, you can use the reified
modifier with inline functions to retrieve type information at runtime. Here’s an example:
inline fun printType() {
println("Type: ${T::class.simpleName}")
}
fun main() {
printType()
printType()
printType()
}
Output:
Type: String
Type: Int
Type: Double
I hope you find these additional examples helpful for your Kotlin learning journey. If you have more specific topics or questions in mind, feel free to ask! Happy coding!
Using when Expression with in and is Checks
In addition to simple value checks, the when
expression in Kotlin allows you to use ‘in’ and ‘is’ checks for more complex conditions. Here’s an example:
fun describeValue(value: Any) {
when (value) {
in 1..10 -> println("Value is in the range of 1 to 10")
is String -> println("Value is a String with length ${value.length}")
is Boolean -> println("Value is a Boolean with value $value")
else -> println("Value is something else")
}
}
fun main() {
describeValue(5)
describeValue("Kotlin")
describeValue(true)
describeValue(3.14)
}
Output:
Value is in the range of 1 to 10
Value is a String with length 6
Value is a Boolean with value true
Value is something else
Late-Initialized Properties with lateinit
The lateinit
modifier can be used for non-null properties that will be initialized later, typically in a different function or lifecycle. The property must be of a non-primitive type and should be initialized before accessing it, or it will throw a LateinitPropertyAccessException
. Here’s an example:
class Person {
lateinit var name: String
fun initPerson() {
name = "Alice"
}
fun printName() {
println("Name: $name")
}
}
fun main() {
val person = Person()
person.initPerson()
person.printName()
}
Output:
Name: Alice
Destructuring Declarations with Maps
In Kotlin, you can use destructuring declarations to extract key-value pairs from maps. Here’s an example:
fun main() {
val personInfo = mapOf("name" to "Alice", "age" to 30, "city" to "Wonderland")
val (name, age, city) = personInfo
println("Name: $name, Age: $age, City: $city")
}
Output:
Name: Alice, Age: 30, City: Wonderland
I hope you find these additional examples helpful for your Kotlin learning journey. If you have more specific topics or questions in mind, feel free to ask! Happy coding!
Object Expressions
Kotlin allows you to create anonymous objects using object expressions. These objects can be used to create instances of an ad-hoc class that inherits from a specified type or implements one or more interfaces. Here’s an example:
interface Greeter {
fun greet(name: String)
}
fun main() {
val englishGreeter = object : Greeter {
override fun greet(name: String) {
println("Hello, $name!")
}
}
val frenchGreeter = object : Greeter {
override fun greet(name: String) {
println("Bonjour, $name!")
}
}
englishGreeter.greet("Alice")
frenchGreeter.greet("Bob")
}
Output:
Hello, Alice!
Bonjour, Bob!
Unary Minus Operator Overloading
In Kotlin, you can overload the unary minus operator (-
) for your custom classes by defining a unaryMinus() function. Here’s an example:
data class Point(val x: Int, val y: Int) {
operator fun unaryMinus() = Point(-x, -y)
}
fun main() {
val point = Point(3, 4)
val negativePoint = -point
println("Original Point: (${point.x}, ${point.y})")
println("Negative Point: (${negativePoint.x}, ${negativePoint.y})")
}
Output:
Original Point: (3, 4)
Negative Point: (-3, -4)
Using in with Custom Objects
In Kotlin, you can define the in
operator for your custom classes by overloading the contains
operator (in
). This allows you to use the in
keyword to check if an object belongs to a collection of your custom objects. Here’s an example:
data class Fruit(val name: String)
class FruitBasket(private val fruits: List) {
operator fun contains(fruit: Fruit) = fruit in fruits
}
fun main() {
val apple = Fruit("Apple")
val orange = Fruit("Orange")
val basket = FruitBasket(listOf(apple, orange))
println("Is Apple in the basket? ${apple in basket}")
println("Is Banana in the basket? ${Fruit("Banana") in basket}")
}
Output:
Is Apple in the basket? true
Is Banana in the basket? false
I hope you find these additional examples helpful for your Kotlin learning journey. If you have more specific topics or questions in mind, feel free to ask! Happy coding!
Smart Casts
Kotlin features smart casts, which allow you to automatically cast a variable to a different type when certain conditions are met. For example, after a type check, the variable is automatically cast to the checked type within the corresponding block. Here’s an example:
fun printLength(obj: Any) {
if (obj is String) {
println("String length: ${obj.length}")
} else {
println("Not a String")
}
}
fun main() {
printLength("Kotlin")
printLength(10)
}
Output:
String length: 6
Not a String
Object Declarations
In Kotlin, object declarations allow you to create singletons, which are classes that have only one instance. Object declarations are used to create an object instance on-demand when first accessed. Here’s an example:
object Singleton {
fun greet() {
println("Hello, I'm a Singleton!")
}
}
fun main() {
Singleton.greet()
}
Output:
Hello, I'm a Singleton!
Function Type Notation
Kotlin allows you to use function types as parameters in functions. Function types are denoted using parentheses, where the parameter types are listed in the parentheses, followed by an arrow (->
) and the return type. Here’s an example:
fun doOperation(operation: (Int, Int) -> Int) {
val result = operation(5, 3)
println("Result: $result")
}
fun main() {
val add: (Int, Int) -> Int = { a, b -> a + b }
val subtract: (Int, Int) -> Int = { a, b -> a - b }
doOperation(add)
doOperation(subtract)
}
Output:
Result: 8
Result: 2
I hope you find these additional examples helpful for your Kotlin learning journey. If you have more specific topics or questions in mind, feel free to ask! Happy coding!
Function References
Kotlin allows you to use function references, which are a concise way to pass a function as an argument without invoking it. You can use the ::
operator to obtain a reference to a function. Here’s an example:
fun add(a: Int, b: Int): Int {
return a + b
}
fun multiply(a: Int, b: Int): Int {
return a * b
}
fun main() {
val numbers = listOf(2, 3, 4)
// Using function references
val sum = numbers.fold(0, ::add)
val product = numbers.fold(1, ::multiply)
println("Sum: $sum")
println("Product: $product")
}
Output:
Sum: 9
Product: 24
Using when Expression without Argument
In Kotlin, you can use the when
expression without an argument. In this case, each branch is evaluated and executed if its condition is true
. Here’s an example:
fun main() {
val day = "Monday"
when {
day == "Monday" -> println("Start of the week")
day == "Friday" -> println("End of the week")
day == "Wednesday" || day == "Thursday" -> println("Midweek")
else -> println("Normal day")
}
}
Output:
Start of the week