From 1bf90d656ca570c7f7920cde82c22c183c87964f Mon Sep 17 00:00:00 2001 From: gpl-gowthamchand Date: 2025ๅนด8ๆœˆ24ๆ—ฅ 16:15:30 +0530 Subject: [PATCH 01/23] feat: Add comprehensive documentation structure with detailed guides - Create organized docs/ directory structure - Add main documentation index with navigation - Create 5 comprehensive basic topic guides: * Hello World with exercises * Variables and Data Types * Comments and Documentation * Data Types Deep Dive * String Interpolation - Add Functions Basics guide - Update main README with clear navigation - Include hands-on exercises for each topic - Provide clear learning paths and prerequisites --- README.md | 226 ++++++++------ docs/README.md | 116 +++++++ docs/basics/01-hello-world.md | 206 +++++++++++++ docs/basics/02-variables-data-types.md | 323 +++++++++++++++++++ docs/basics/03-comments.md | 410 +++++++++++++++++++++++++ docs/basics/04-data-types.md | 409 ++++++++++++++++++++++++ docs/basics/05-string-interpolation.md | 380 +++++++++++++++++++++++ docs/functions/01-functions-basics.md | 305 ++++++++++++++++++ 8 files changed, 2279 insertions(+), 96 deletions(-) create mode 100644 docs/README.md create mode 100644 docs/basics/01-hello-world.md create mode 100644 docs/basics/02-variables-data-types.md create mode 100644 docs/basics/03-comments.md create mode 100644 docs/basics/04-data-types.md create mode 100644 docs/basics/05-string-interpolation.md create mode 100644 docs/functions/01-functions-basics.md diff --git a/README.md b/README.md index 760d3fd..6ff5ddc 100644 --- a/README.md +++ b/README.md @@ -1,96 +1,130 @@ -# Kotlin Programming Tutorial for Beginners -Learn Kotlin Programming, its basics and Fundamentals from scratch. - -## Topics to be covered -0. Overview - - Course introduction, prequisites and software required -1. Installation - - Install required softwares for Windows, MAC and Linux ( Ubuntu ) -2. Getting Started with Kotlin Programming - - Run your first app in Kotlin -3. Exploring Data Types and Variables - - Data Types and Variables - - String, Literals and String Interpolation - - Comments -4. Constants, Variables and Data Types -5. Control Flow Statements - - IF ELSE - - IF Expressions - - WHEN Expressions -6. Loop Control Statements - - What are Iterators? - - FOR Loop and how it works - - WHILE Loop - - DO WHILE Loop - - BREAK statements - - CONTINUE keyword - - Labelled FOR Loop -7. Functions and Interoperability - - Declaring functions - - Interoperability with Java code - - Function as Expressions - - Extension Functions - - Infix Functions - - Default Parameters - - Named Parameters - - Tailrec Functions -8. Object Oriented Programming in Kotlin - - Defining Class and creating Objects - - INIT block - - Primary and Secondary Constructors - - Properties ( Field variables ) - - Inheritance - - Method and Property Overriding - - Polymorphism - - Abstract Class, Property and Method - - Interface - - Data Class - - Object Declaration - - Enum class - - Sealed class - - Companion Object -9. Functional Programming in Koltin - - Lambdas - - Higher-Order Functions - - Closures - - 'it' keyword - - 'with' function - - 'apply' function -10. Collections in Kotlin - - Arrays - - List - - Map and HashMap - - Set and HashSet -11. Sorting and Filtering - - "filter" function - - "map" function - - Predicates: all, any, find, count. -12. Kotlin NULL Safety - - Safe call - - with Let - - Elvis - - Lateinit keyword - - Lazy delegation and 'lateinit' vs. 'lazy' -13. Scope Functions - - with - - apply - - let - - also - - run -14. Coroutines - - What are Coroutines? How are they related to Threads? - - launch, async, runBlocking, withContext, withTimeoutOrNull, - - Suspending function - - Cancellation and Timeouts - - Cooperative suspending functions and isActive flag - - Exception Handling in Coroutines - - Sequential execution of suspending function in Coroutines - - Concurrency within Coroutine - - lazy 'async' - - CoroutineScope and CoroutineContext - - Dispacthers: Confined dispatcher, Default Dispatcher, and Unconfined Displatcher -15. Conclusion - -## Authors - -* **Sriyank Siddhartha** +# ๐Ÿš€ Kotlin Programming Tutorial for Beginners + +**Learn Kotlin Programming from scratch with practical examples and hands-on exercises.** + +## ๐Ÿ“– Quick Navigation + +- **๐Ÿ“š [Complete Documentation](docs/README.md)** - Start here for detailed learning +- **๐Ÿ’ป [Source Code](src/)** - All Kotlin examples +- **๐ŸŽฏ [Learning Path](docs/README.md#learning-path)** - Recommended study order + +## ๐ŸŽฏ What You'll Learn + +This tutorial covers everything from basic syntax to advanced concepts: + +### **๐Ÿš€ Beginner Level** +- Hello World and basic syntax +- Variables, data types, and constants +- Control flow (if, when, loops) +- Basic functions and parameters + +### **โš™๏ธ Intermediate Level** +- Advanced functions and expressions +- Object-oriented programming +- Collections and data structures +- Null safety and error handling + +### **๐Ÿ”ง Advanced Level** +- Functional programming with lambdas +- Coroutines and asynchronous programming +- Scope functions and advanced features +- Real-world project examples + +## ๐Ÿš€ Quick Start + +1. **Clone this repository:** + ```bash + git clone https://github.com/gpl-gowthamchand/KotlinTutorial.git + cd KotlinTutorial + ``` + +2. **Open in IntelliJ IDEA:** + - File โ†’ Open โ†’ Select the project folder + - Wait for project indexing to complete + +3. **Start Learning:** + - Begin with [Hello World](docs/basics/01-hello-world.md) + - Follow the [Learning Path](docs/README.md#learning-path) + - Practice with exercises in each section + +## ๐Ÿ“ How to Use This Tutorial + +1. **Read the Documentation:** Start with the `.md` file for each topic +2. **Study the Code:** Look at the corresponding `.kt` file +3. **Run Examples:** Execute the code to see it in action +4. **Practice:** Complete the exercises provided +5. **Move Forward:** Follow the "Next Steps" links + +## ๐ŸŽ“ Recommended Learning Order + +### **Week 1: Basics** +1. [Hello World](docs/basics/01-hello-world.md) - Your first program +2. [Variables and Data Types](docs/basics/02-variables-data-types.md) - Storing data +3. [Control Flow](docs/control-flow/01-if-expressions.md) - Making decisions +4. [Loops](docs/control-flow/03-for-loops.md) - Repeating actions + +### **Week 2: Functions & OOP** +1. [Functions Basics](docs/functions/01-functions-basics.md) - Reusable code +2. [Classes and Constructors](docs/oop/01-classes-constructors.md) - Object creation +3. [Inheritance](docs/oop/02-inheritance.md) - Code reuse +4. [Data Classes](docs/oop/06-data-classes.md) - Simple data containers + +### **Week 3: Advanced Concepts** +1. [Functional Programming](docs/functional-programming/01-lambdas.md) - Modern programming +2. [Collections](docs/collections/01-arrays.md) - Data structures +3. [Null Safety](docs/null-safety/01-null-safety.md) - Preventing errors +4. [Coroutines](docs/coroutines/01-introduction.md) - Asynchronous programming + +## ๐Ÿ› ๏ธ Prerequisites + +- Basic programming concepts (variables, functions, loops) +- No prior Kotlin experience required +- IntelliJ IDEA or Android Studio recommended + +## ๐Ÿ“ Project Structure + +``` +KotlinTutorial/ +โ”œโ”€โ”€ docs/ # ๐Ÿ“š Documentation +โ”‚ โ”œโ”€โ”€ README.md # Main documentation index +โ”‚ โ”œโ”€โ”€ basics/ # Beginner topics +โ”‚ โ”œโ”€โ”€ control-flow/ # Decision making and loops +โ”‚ โ”œโ”€โ”€ functions/ # Function concepts +โ”‚ โ”œโ”€โ”€ oop/ # Object-oriented programming +โ”‚ โ”œโ”€โ”€ functional-programming/ # Functional concepts +โ”‚ โ”œโ”€โ”€ collections/ # Data structures +โ”‚ โ”œโ”€โ”€ null-safety/ # Error prevention +โ”‚ โ””โ”€โ”€ coroutines/ # Asynchronous programming +โ”œโ”€โ”€ src/ # ๐Ÿ’ป Source code +โ”‚ โ”œโ”€โ”€ 01_hello_world.kt # Hello World example +โ”‚ โ”œโ”€โ”€ 04_variables_data_types.kt # Variables and types +โ”‚ โ”œโ”€โ”€ 17_functions_basics.kt # Functions basics +โ”‚ โ””โ”€โ”€ ... # More examples +โ””โ”€โ”€ README.md # This file +``` + +## ๐Ÿค Contributing + +Found an error or want to improve something? Contributions are welcome! + +1. Fork the repository +2. Create a feature branch +3. Make your changes +4. Submit a pull request + +## ๐Ÿ“š Additional Resources + +- [Official Kotlin Documentation](https://kotlinlang.org/docs/home.html) +- [Kotlin Playground](https://play.kotlinlang.org/) - Try code online +- [Kotlin by Example](https://play.kotlinlang.org/byExample/overview) + +## ๐Ÿ‘จโ€๐Ÿซ About + +**Original Author:** Sriyank Siddhartha +**Forked by:** [gpl-gowthamchand](https://github.com/gpl-gowthamchand) + +--- + +**๐ŸŽฏ Ready to start learning? Begin with [Hello World](docs/basics/01-hello-world.md)!** + +**Happy Learning! ๐ŸŽ‰** diff --git a/docs/README.md b/docs/README.md new file mode 100644 index 0000000..77e107e --- /dev/null +++ b/docs/README.md @@ -0,0 +1,116 @@ +# ๐Ÿ“š Kotlin Tutorial Documentation + +Welcome to the comprehensive Kotlin learning guide! This documentation is organized by topic and provides detailed explanations with practical examples. + +## ๐ŸŽฏ How to Use This Documentation + +1. **Start with the topic you want to learn** +2. **Read the detailed explanation** in the `.md` file +3. **Study the code examples** in the linked `.kt` files +4. **Practice with the exercises** provided +5. **Follow the learning path** to progress systematically + +## ๐Ÿ“– Learning Path + +### **๐Ÿš€ Beginner Level (Start Here)** + +#### **Basics** +- [Hello World - Your First Program](basics/01-hello-world.md) โ†’ [Code](src/01_hello_world.kt) +- [Variables and Data Types](basics/02-variables-data-types.md) โ†’ [Code](src/04_variables_data_types.kt) +- [Comments and Documentation](basics/03-comments.md) โ†’ [Code](src/03_comments.kt) +- [Data Types Deep Dive](basics/04-data-types.md) โ†’ [Code](src/07_data_types.kt) +- [String Interpolation](basics/05-string-interpolation.md) โ†’ [Code](src/08_string_interpolation.kt) +- [Ranges and Sequences](basics/06-ranges.md) โ†’ [Code](src/09_ranges.kt) + +#### **Control Flow** +- [If Expressions](control-flow/01-if-expressions.md) โ†’ [Code](src/10_if_expression.kt) +- [When Expressions](control-flow/02-when-expressions.md) โ†’ [Code](src/11_when_expression.kt) +- [For Loops](control-flow/03-for-loops.md) โ†’ [Code](src/12_for_loop.kt) +- [While Loops](control-flow/04-while-loops.md) โ†’ [Code](src/13_while_loop.kt) +- [Break and Continue](control-flow/05-break-continue.md) โ†’ [Code](src/15_break_keyword.kt) + +### **โš™๏ธ Intermediate Level** + +#### **Functions** +- [Functions Basics](functions/01-functions-basics.md) โ†’ [Code](src/17_functions_basics.kt) +- [Functions as Expressions](functions/02-functions-expressions.md) โ†’ [Code](src/18_functions_as_expressions.kt) +- [Extension Functions](functions/03-extension-functions.md) โ†’ [Code](src/22_extension_function_one.kt) +- [Infix Functions](functions/04-infix-functions.md) โ†’ [Code](src/24_infix_function.kt) +- [Named Parameters](functions/05-named-parameters.md) โ†’ [Code](src/21_named_parameters.kt) + +#### **Object-Oriented Programming** +- [Classes and Constructors](oop/01-classes-constructors.md) โ†’ [Code](src/26_class_and_constructor.kt) +- [Inheritance](oop/02-inheritance.md) โ†’ [Code](src/27_inheritance.kt) +- [Method Overriding](oop/03-method-overriding.md) โ†’ [Code](src/28_overriding_methods_properties.kt) +- [Abstract Classes](oop/04-abstract-classes.md) โ†’ [Code](src/30_abstract_class.kt) +- [Interfaces](oop/05-interfaces.md) โ†’ [Code](src/31_interface.kt) +- [Data Classes](oop/06-data-classes.md) โ†’ [Code](src/32_data_class.kt) + +### **๐Ÿ”ง Advanced Level** + +#### **Functional Programming** +- [Lambdas and Higher-Order Functions](functional-programming/01-lambdas.md) โ†’ [Code](src/35_lambdas_higher_order_functions.kt) +- [Scope Functions](functional-programming/02-scope-functions.md) โ†’ [Code](src/39_with_apply_functions.kt) + +#### **Collections** +- [Arrays](collections/01-arrays.md) โ†’ [Code](src/40_arrays.kt) +- [Lists](collections/02-lists.md) โ†’ [Code](src/41_list.kt) +- [Maps and HashMaps](collections/03-maps.md) โ†’ [Code](src/42_map_hashmap.kt) +- [Sets and HashSets](collections/04-sets.md) โ†’ [Code](src/43_set_hashset.kt) + +#### **Null Safety** +- [Null Safety](null-safety/01-null-safety.md) โ†’ [Code](src/46_null_safety.kt) +- [Lateinit and Lazy](null-safety/02-lateinit-lazy.md) โ†’ [Code](src/47_lateinit_keyword.kt) + +#### **Coroutines** +- [Introduction to Coroutines](coroutines/01-introduction.md) โ†’ [Code](src/61_first_coroutine.kt) +- [Launch and Async](coroutines/02-launch-async.md) โ†’ [Code](src/64_launch_coroutine_builder.kt) +- [Exception Handling](coroutines/03-exception-handling.md) โ†’ [Code](src/70_exception_handling.kt) + +## ๐ŸŽ“ Recommended Learning Order + +### **Week 1: Basics** +1. Start with [Hello World](basics/01-hello-world.md) +2. Learn [Variables and Data Types](basics/02-variables-data-types.md) +3. Understand [Control Flow](control-flow/01-if-expressions.md) +4. Practice [Loops](control-flow/03-for-loops.md) + +### **Week 2: Functions & OOP** +1. Master [Functions](functions/01-functions-basics.md) +2. Learn [Classes](oop/01-classes-constructors.md) +3. Understand [Inheritance](oop/02-inheritance.md) +4. Explore [Data Classes](oop/06-data-classes.md) + +### **Week 3: Advanced Concepts** +1. Dive into [Functional Programming](functional-programming/01-lambdas.md) +2. Master [Collections](collections/01-arrays.md) +3. Learn [Null Safety](null-safety/01-null-safety.md) +4. Practice [Coroutines](coroutines/01-introduction.md) + +## ๐Ÿ› ๏ธ Prerequisites + +- Basic programming concepts (variables, functions, loops) +- No prior Kotlin experience required +- IntelliJ IDEA or Android Studio recommended + +## ๐Ÿ“ How to Use + +1. **Choose a topic** from the learning path above +2. **Read the detailed explanation** in the `.md` file +3. **Study the code examples** in the linked `.kt` file +4. **Run the examples** to see them in action +5. **Complete the exercises** to reinforce learning +6. **Move to the next topic** following the learning path + +## ๐Ÿค Contributing + +Found an error or want to improve something? Contributions are welcome! + +1. Fork the repository +2. Create a feature branch +3. Make your changes +4. Submit a pull request + +--- + +**Happy Learning! ๐ŸŽ‰** diff --git a/docs/basics/01-hello-world.md b/docs/basics/01-hello-world.md new file mode 100644 index 0000000..ad2fd99 --- /dev/null +++ b/docs/basics/01-hello-world.md @@ -0,0 +1,206 @@ +# ๐Ÿš€ Hello World - Your First Kotlin Program + +Welcome to Kotlin! This is your first step into the world of Kotlin programming. Let's start with the traditional "Hello World" program. + +## ๐Ÿ“‹ Learning Objectives + +By the end of this lesson, you will be able to: +- โœ… Write your first Kotlin program +- โœ… Understand the basic structure of a Kotlin application +- โœ… Use the `main` function as the entry point +- โœ… Output text to the console +- โœ… Understand basic syntax and formatting + +## ๐Ÿ” What You'll Learn + +- **Function declaration syntax** - How to create functions in Kotlin +- **Parameter handling** - Understanding the `main` function parameters +- **Basic output operations** - Using `print()` and `println()` +- **Kotlin code structure** - How Kotlin programs are organized + +## ๐Ÿ“ Prerequisites + +**None!** This is your starting point. You don't need any prior programming experience. + +## ๐Ÿ’ป The Code + +Let's look at your first Kotlin program: + +```kotlin +// Hello World App + +fun main(args: Array) { + print("Hello World") +} +``` + +**๐Ÿ“ File:** [01_hello_world.kt](src/01_hello_world.kt) + +## ๐Ÿ” Code Breakdown + +### **Line 1: Comment** +```kotlin +// Hello World App +``` +- `//` indicates a single-line comment +- Comments are ignored by the compiler +- They help explain what your code does + +### **Line 3: Main Function Declaration** +```kotlin +fun main(args: Array) { +``` +- `fun` - Keyword to declare a function +- `main` - Function name (this is special - it's the entry point) +- `args: Array` - Parameter declaration + - `args` - Parameter name + - `Array` - Type (array of strings) +- `{` - Opening brace for function body + +### **Line 4: Function Body** +```kotlin + print("Hello World") +``` +- `print()` - Function to output text without a newline +- `"Hello World"` - String literal (text in quotes) +- `;` - Semicolon is optional in Kotlin (not needed here) + +### **Line 5: Closing Brace** +```kotlin +} +``` +- Closes the function body + +## ๐ŸŽฏ Key Concepts Explained + +### **1. The `main` Function** +- **Entry Point**: Every Kotlin program starts with the `main` function +- **Special Name**: The compiler looks for this specific function +- **Parameters**: `args` contains command-line arguments (we'll use these later) + +### **2. Function Declaration** +```kotlin +fun functionName(parameters): returnType { + // function body +} +``` + +### **3. Output Functions** +- **`print()`**: Outputs text without moving to the next line +- **`println()`**: Outputs text and moves to the next line + +## ๐Ÿงช Running Your First Program + +### **Step 1: Open IntelliJ IDEA** +1. Launch IntelliJ IDEA +2. Open the project folder +3. Navigate to `src/01_hello_world.kt` + +### **Step 2: Run the Program** +1. Right-click on the file +2. Select "Run '01_hello_worldKt'" +3. Or use the green play button + +### **Step 3: See the Output** +``` +Hello World +``` + +## ๐ŸŽฎ Hands-On Exercises + +### **Exercise 1: Modify the Message** +Change "Hello World" to "Hello Kotlin!" and run the program. + +**Solution:** +```kotlin +fun main(args: Array) { + print("Hello Kotlin!") +} +``` + +### **Exercise 2: Use println()** +Replace `print()` with `println()` and see the difference. + +**Solution:** +```kotlin +fun main(args: Array) { + println("Hello World") +} +``` + +### **Exercise 3: Add Your Name** +Add another print statement with your name. + +**Solution:** +```kotlin +fun main(args: Array) { + print("Hello World") + print("My name is [Your Name]") +} +``` + +### **Exercise 4: Multiple Lines** +Use multiple println statements to create a multi-line output. + +**Solution:** +```kotlin +fun main(args: Array) { + println("Hello World") + println("Welcome to Kotlin") + println("Let's start coding!") +} +``` + +## ๐Ÿ” Understanding the Output + +### **Using `print()`:** +```kotlin +print("Hello") +print("World") +``` +**Output:** `HelloWorld` (no space, no newline) + +### **Using `println()`:** +```kotlin +println("Hello") +println("World") +``` +**Output:** +``` +Hello +World +``` + +## ๐Ÿšจ Common Mistakes to Avoid + +1. **Missing quotes**: `print(Hello World)` โŒ + - **Correct**: `print("Hello World")` โœ… + +2. **Missing braces**: `fun main(args: Array) print("Hello")` โŒ + - **Correct**: `fun main(args: Array) { print("Hello") }` โœ… + +3. **Wrong function name**: `fun Main(args: Array)` โŒ + - **Correct**: `fun main(args: Array)` โœ… + +## ๐ŸŽฏ What's Next? + +Congratulations! You've written your first Kotlin program. Now you're ready to: + +1. **Learn about variables** โ†’ [Variables and Data Types](02-variables-data-types.md) +2. **Understand data types** โ†’ [Data Types Deep Dive](04-data-types.md) +3. **Explore more output** โ†’ [Exploring Your First App](../02-explore-first-app.md) + +## ๐Ÿ“š Additional Resources + +- [Official Kotlin Documentation](https://kotlinlang.org/docs/home.html) +- [Kotlin Playground](https://play.kotlinlang.org/) - Try code online +- [IntelliJ IDEA Tutorial](https://www.jetbrains.com/help/idea/creating-and-running-your-first-kotlin-application.html) + +## ๐Ÿ† Summary + +- โœ… You've written your first Kotlin program +- โœ… You understand the basic structure +- โœ… You can output text to the console +- โœ… You're ready to learn more! + +**Keep practicing and happy coding! ๐ŸŽ‰** diff --git a/docs/basics/02-variables-data-types.md b/docs/basics/02-variables-data-types.md new file mode 100644 index 0000000..e609428 --- /dev/null +++ b/docs/basics/02-variables-data-types.md @@ -0,0 +1,323 @@ +# ๐Ÿ”ค Variables and Data Types in Kotlin + +Welcome to the world of variables and data types! This is where you'll learn how to store and manipulate data in your Kotlin programs. + +## ๐Ÿ“‹ Learning Objectives + +By the end of this lesson, you will be able to: +- โœ… Declare variables using `var` and `val` +- โœ… Understand the difference between mutable and immutable variables +- โœ… Use type inference to let Kotlin determine types automatically +- โœ… Explicitly declare variable types when needed +- โœ… Work with basic data types: Int, Double, Boolean, String, Char + +## ๐Ÿ” What You'll Learn + +- **Variable declaration** - How to create variables in Kotlin +- **Mutability** - Understanding `var` vs `val` +- **Type inference** - Kotlin's automatic type detection +- **Explicit typing** - When and how to specify types manually +- **Basic data types** - Numbers, text, and logical values + +## ๐Ÿ“ Prerequisites + +- Basic understanding of programming concepts +- Completed [Hello World](01-hello-world.md) lesson + +## ๐Ÿ’ป The Code + +Let's examine the variables and data types example: + +```kotlin +fun main(args: Array) { + // Type inference examples - Kotlin automatically determines the type + var myNumber = 10 // Type: Int + var myDecimal = 1.0 // Type: Double (not Float!) + var isActive = true // Type: Boolean + + // Explicit type declaration + var myString: String // Mutable String with explicit type + myString = "Hello World" + myString = "Another World" // Can change value since it's var + + // Immutable variable using val + val myAnotherString = "My constant string value" // Immutable String + // myAnotherString = "some value" // COMPILE ERROR: Cannot reassign val + + // Best practice: Use val by default, var only when you need mutability + val pi = 3.14159 // Immutable constant + val maxRetries = 3 // Immutable configuration + + // Printing values + println("Number: $myNumber") + println("Decimal: $myDecimal") + println("Is Active: $isActive") + println("String: $myString") + println("Constant: $myAnotherString") + println("Pi: $pi") + println("Max Retries: $maxRetries") +} +``` + +**๐Ÿ“ File:** [04_variables_data_types.kt](src/04_variables_data_types.kt) + +## ๐Ÿ” Code Breakdown + +### **Variable Declaration with Type Inference** + +```kotlin +var myNumber = 10 // Type: Int +var myDecimal = 1.0 // Type: Double +var isActive = true // Type: Boolean +``` + +- **`var`**: Declares a mutable variable (can be changed) +- **Type inference**: Kotlin automatically determines the type based on the value +- **`10`** โ†’ `Int` (32-bit integer) +- **`1.0`** โ†’ `Double` (64-bit floating point) +- **`true`** โ†’ `Boolean` (true/false value) + +### **Explicit Type Declaration** + +```kotlin +var myString: String // Mutable String with explicit type +myString = "Hello World" +myString = "Another World" // Can change value since it's var +``` + +- **`: String`**: Explicitly declares the variable type +- **`var`**: Allows reassignment +- **Multiple assignments**: Can change the value multiple times + +### **Immutable Variables with `val`** + +```kotlin +val myAnotherString = "My constant string value" // Immutable String +// myAnotherString = "some value" // COMPILE ERROR: Cannot reassign val +``` + +- **`val`**: Declares an immutable variable (cannot be changed) +- **Single assignment**: Value is set once and cannot be modified +- **Compile-time safety**: Kotlin prevents accidental reassignment + +## ๐ŸŽฏ Key Concepts Explained + +### **1. Variable Declaration Syntax** + +```kotlin +// Mutable variable +var variableName: Type = value + +// Immutable variable +val constantName: Type = value + +// With type inference +var inferredVariable = value +val inferredConstant = value +``` + +### **2. Mutability: `var` vs `val`** + +| Feature | `var` | `val` | +|---------|-------|-------| +| **Mutability** | Mutable (can change) | Immutable (cannot change) | +| **Reassignment** | โœ… Allowed | โŒ Not allowed | +| **Use case** | When value needs to change | When value should remain constant | +| **Safety** | Less safe | More safe | + +### **3. Type Inference** + +Kotlin automatically determines the type based on the assigned value: + +```kotlin +val number = 42 // Int +val decimal = 3.14 // Double +val text = "Hello" // String +val flag = true // Boolean +val character = 'A' // Char +``` + +### **4. Explicit Type Declaration** + +Sometimes you want to be explicit about types: + +```kotlin +var age: Int = 25 +var name: String = "John" +var height: Double = 1.75 +var isStudent: Boolean = true +``` + +## ๐Ÿ“Š Basic Data Types in Kotlin + +### **Numbers** + +| Type | Size | Range | Example | +|------|------|-------|---------| +| **Byte** | 8 bits | -128 to 127 | `val byte: Byte = 100` | +| **Short** | 16 bits | -32,768 to 32,767 | `val short: Short = 1000` | +| **Int** | 32 bits | -231 to 231-1 | `val int: Int = 1000000` | +| **Long** | 64 bits | -263 to 263-1 | `val long: Long = 1000000000L` | +| **Float** | 32 bits | ยฑ3.4E-38 to ยฑ3.4E+38 | `val float: Float = 3.14f` | +| **Double** | 64 bits | ยฑ1.7E-308 to ยฑ1.7E+308 | `val double: Double = 3.14` | + +### **Text** + +| Type | Description | Example | +|------|-------------|---------| +| **Char** | Single Unicode character | `val char: Char = 'A'` | +| **String** | Sequence of characters | `val string: String = "Hello"` | + +### **Logical** + +| Type | Description | Example | +|------|-------------|---------| +| **Boolean** | true or false | `val boolean: Boolean = true` | + +## ๐Ÿงช Hands-On Exercises + +### **Exercise 1: Create Different Variable Types** +Create variables for your personal information using different data types. + +**Solution:** +```kotlin +fun main(args: Array) { + val name: String = "Your Name" + val age: Int = 25 + val height: Double = 1.75 + val isStudent: Boolean = true + val grade: Char = 'A' + + println("Name: $name") + println("Age: $age") + println("Height: $height") + println("Is Student: $isStudent") + println("Grade: $grade") +} +``` + +### **Exercise 2: Experiment with Mutability** +Create both `var` and `val` variables and try to reassign them. + +**Solution:** +```kotlin +fun main(args: Array) { + var mutableNumber = 10 + val immutableNumber = 20 + + println("Mutable: $mutableNumber") + mutableNumber = 15 // This works + println("Mutable after change: $mutableNumber") + + println("Immutable: $immutableNumber") + // immutableNumber = 25 // This would cause a compile error +} +``` + +### **Exercise 3: Type Inference Challenge** +Let Kotlin infer the types and then verify them. + +**Solution:** +```kotlin +fun main(args: Array) { + val number = 42 + val decimal = 3.14 + val text = "Hello" + val flag = true + + // Kotlin inferred these types automatically + println("Number type: ${number::class.simpleName}") + println("Decimal type: ${decimal::class.simpleName}") + println("Text type: ${text::class.simpleName}") + println("Flag type: ${flag::class.simpleName}") +} +``` + +### **Exercise 4: Calculate with Variables** +Create variables for mathematical calculations. + +**Solution:** +```kotlin +fun main(args: Array) { + val radius: Double = 5.0 + val pi: Double = 3.14159 + + val area = pi * radius * radius + val circumference = 2 * pi * radius + + println("Radius: $radius") + println("Area: $area") + println("Circumference: $circumference") +} +``` + +## ๐Ÿ” Understanding Type Inference + +### **When Type Inference Works Well** + +```kotlin +// Clear and obvious types +val age = 25 // Int +val name = "John" // String +val isActive = true // Boolean +val price = 19.99 // Double +``` + +### **When to Use Explicit Types** + +```kotlin +// Unclear or specific types +val number: Long = 1000000000L +val decimal: Float = 3.14f +val character: Char = 'A' +``` + +## ๐Ÿšจ Common Mistakes to Avoid + +1. **Confusing `var` and `val`**: + ```kotlin + val constant = 10 + constant = 20 // โŒ Compile error + ``` + +2. **Type mismatch**: + ```kotlin + var number: Int = "Hello" // โŒ Type mismatch + ``` + +3. **Forgetting quotes for strings**: + ```kotlin + var name = Hello // โŒ Missing quotes + var name = "Hello" // โœ… Correct + ``` + +4. **Using wrong number types**: + ```kotlin + val decimal: Float = 3.14 // โŒ 3.14 is Double by default + val decimal: Float = 3.14f // โœ… Correct + ``` + +## ๐ŸŽฏ What's Next? + +Great job! You've learned about variables and data types. Now you're ready to: + +1. **Explore more data types** โ†’ [Data Types Deep Dive](04-data-types.md) +2. **Learn string operations** โ†’ [String Interpolation](05-string-interpolation.md) +3. **Understand control flow** โ†’ [If Expressions](../control-flow/01-if-expressions.md) +4. **Work with functions** โ†’ [Functions Basics](../functions/01-functions-basics.md) + +## ๐Ÿ“š Additional Resources + +- [Kotlin Data Types Documentation](https://kotlinlang.org/docs/basic-types.html) +- [Kotlin Variables Documentation](https://kotlinlang.org/docs/properties.html) +- [Type Inference in Kotlin](https://kotlinlang.org/docs/type-inference.html) + +## ๐Ÿ† Summary + +- โœ… You can declare variables using `var` and `val` +- โœ… You understand the difference between mutable and immutable +- โœ… You can use type inference and explicit typing +- โœ… You know the basic data types in Kotlin +- โœ… You're ready to work with more complex data structures! + +**Keep practicing and happy coding! ๐ŸŽ‰** diff --git a/docs/basics/03-comments.md b/docs/basics/03-comments.md new file mode 100644 index 0000000..90b51e8 --- /dev/null +++ b/docs/basics/03-comments.md @@ -0,0 +1,410 @@ +# ๐Ÿ’ฌ Comments and Documentation in Kotlin + +Welcome to the world of code documentation! Comments are essential for making your code readable and maintainable. In this lesson, you'll learn how to write clear, helpful comments in Kotlin. + +## ๐Ÿ“‹ Learning Objectives + +By the end of this lesson, you will be able to: +- โœ… Write single-line comments using `//` +- โœ… Create multi-line comments using `/* */` +- โœ… Understand when and how to use comments effectively +- โœ… Write self-documenting code +- โœ… Follow Kotlin documentation best practices + +## ๐Ÿ” What You'll Learn + +- **Single-line comments** - Quick explanations for individual lines +- **Multi-line comments** - Detailed explanations for code blocks +- **Documentation comments** - Professional documentation standards +- **Comment best practices** - When and how to comment effectively +- **Self-documenting code** - Writing code that explains itself + +## ๐Ÿ“ Prerequisites + +- Basic understanding of Kotlin syntax +- Completed [Hello World](01-hello-world.md) lesson + +## ๐Ÿ’ป The Code + +Let's examine the comments example: + +```kotlin +/* +* This is comment line 1 +* +* This is comment line 2 +* +* This is main function. Entry point of the application. +* */ + +fun main(args: Array) { // This is inline comment ... + print("Hello World") +} +``` + +**๐Ÿ“ File:** [03_comments.kt](src/03_comments.kt) + +## ๐Ÿ” Code Breakdown + +### **Multi-line Comment Block** + +```kotlin +/* +* This is comment line 1 +* +* This is comment line 2 +* +* This is main function. Entry point of the application. +* */ +``` + +- **`/*`**: Starts a multi-line comment block +- **`*`**: Each line can start with an asterisk for readability +- **`*/`**: Ends the multi-line comment block +- **Purpose**: Explains what the following code does + +### **Single-line Comment** + +```kotlin +fun main(args: Array) { // This is inline comment ... + print("Hello World") +} +``` + +- **`//`**: Starts a single-line comment +- **Inline comment**: Appears on the same line as code +- **Purpose**: Quick explanation of the specific line + +## ๐ŸŽฏ Key Concepts Explained + +### **1. Single-line Comments (`//`)** + +```kotlin +// This is a single-line comment +val name = "John" // This comment explains the variable +val age = 25 // Age in years +``` + +**Use cases:** +- Quick explanations for single lines +- Clarifying complex expressions +- Adding context to variables +- Explaining "why" not "what" + +### **2. Multi-line Comments (`/* */`)** + +```kotlin +/* + * This is a multi-line comment + * It can span multiple lines + * Useful for explaining complex logic + * or documenting functions + */ +fun calculateArea(radius: Double): Double { + return Math.PI * radius * radius +} +``` + +**Use cases:** +- Explaining complex algorithms +- Documenting functions and classes +- Providing context for code blocks +- License information or file headers + +### **3. KDoc Comments (`/** */`)** + +```kotlin +/** + * Calculates the area of a circle + * + * @param radius The radius of the circle + * @return The area of the circle + * @throws IllegalArgumentException if radius is negative + */ +fun calculateArea(radius: Double): Double { + require(radius>= 0) { "Radius cannot be negative" } + return Math.PI * radius * radius +} +``` + +**Use cases:** +- API documentation +- Function and class documentation +- Parameter and return value descriptions +- Exception documentation + +## ๐Ÿงช Hands-On Exercises + +### **Exercise 1: Add Comments to Code** +Add appropriate comments to this code: + +```kotlin +fun main(args: Array) { + val temperature = 25 + val isHot = temperature> 30 + if (isHot) { + println("It's hot today!") + } else { + println("It's not too hot.") + } +} +``` + +**Solution:** +```kotlin +fun main(args: Array) { + // Get current temperature in Celsius + val temperature = 25 + + // Check if temperature is considered hot (above 30ยฐC) + val isHot = temperature> 30 + + // Display appropriate message based on temperature + if (isHot) { + println("It's hot today!") + } else { + println("It's not too hot.") + } +} +``` + +### **Exercise 2: Create a Well-Documented Function** +Write a function with comprehensive comments: + +```kotlin +// TODO: Add your solution here +``` + +**Solution:** +```kotlin +/** + * Calculates the grade based on a numerical score + * + * @param score The numerical score (0-100) + * @return The letter grade (A, B, C, D, or F) + * @throws IllegalArgumentException if score is not between 0 and 100 + */ +fun calculateGrade(score: Int): Char { + // Validate input range + require(score in 0..100) { "Score must be between 0 and 100" } + + // Determine grade based on score ranges + return when { + score>= 90 -> 'A' // 90-100: A grade + score>= 80 -> 'B' // 80-89: B grade + score>= 70 -> 'C' // 70-79: C grade + score>= 60 -> 'D' // 60-69: D grade + else -> 'F' // 0-59: F grade + } +} +``` + +### **Exercise 3: Comment Types Practice** +Create examples of all three comment types: + +```kotlin +// TODO: Add your solution here +``` + +**Solution:** +```kotlin +// Single-line comment: This function calculates the factorial +fun factorial(n: Int): Int { + /* + * Multi-line comment: + * This function uses recursion to calculate factorial + * Factorial of n = n * (n-1) * (n-2) * ... * 1 + */ + + /** + * KDoc comment: + * Calculates the factorial of a non-negative integer + * + * @param n The number to calculate factorial for + * @return The factorial of n + * @throws IllegalArgumentException if n is negative + */ + + require(n>= 0) { "Factorial is not defined for negative numbers" } + + return if (n <= 1) 1 else n * factorial(n - 1) +} +``` + +## ๐Ÿ” Comment Best Practices + +### **โœ… Do's** + +1. **Explain "why" not "what"**: + ```kotlin + // Good: Explains the reasoning + val timeout = 5000 // 5 seconds timeout for network operations + + // Bad: Obvious from code + val timeout = 5000 // Sets timeout to 5000 + ``` + +2. **Keep comments up-to-date**: + ```kotlin + // Update this comment when you change the logic + val maxRetries = 3 // Maximum number of retry attempts + ``` + +3. **Use clear, concise language**: + ```kotlin + // Good: Clear and specific + val bufferSize = 1024 // Buffer size optimized for 4K displays + + // Bad: Vague and unhelpful + val bufferSize = 1024 // Some buffer size + ``` + +### **โŒ Don'ts** + +1. **Don't comment obvious code**: + ```kotlin + // Bad: Comment adds no value + val name = "John" // Assigns "John" to name variable + + // Good: No comment needed + val name = "John" + ``` + +2. **Don't leave outdated comments**: + ```kotlin + // Bad: Comment is wrong + val maxUsers = 100 // Maximum 50 users allowed + + // Good: Comment matches code + val maxUsers = 100 // Maximum 100 users allowed + ``` + +3. **Don't use comments to fix bad code**: + ```kotlin + // Bad: Comment tries to explain confusing code + val x = a + b * c // Add a to the product of b and c + + // Good: Self-documenting code + val result = a + (b * c) + ``` + +## ๐ŸŽฏ When to Use Comments + +### **โœ… Good Times to Comment** + +1. **Complex algorithms** - Explain the logic step by step +2. **Business rules** - Document why certain decisions are made +3. **Workarounds** - Explain temporary solutions or known issues +4. **API usage** - Document how to use your functions +5. **Non-obvious code** - Explain code that isn't self-explanatory + +### **โŒ Avoid Commenting** + +1. **Obvious code** - Code that explains itself +2. **Temporary code** - Code that will be removed +3. **Bad code** - Fix the code instead of explaining it +4. **Implementation details** - Focus on what and why, not how + +## ๐Ÿ” Self-Documenting Code Examples + +### **Instead of Comments, Use Better Names** + +```kotlin +// Bad: Needs comment to explain +val x = 86400000 // Milliseconds in a day + +// Good: Self-documenting +val millisecondsInDay = 86400000 +``` + +### **Use Constants for Magic Numbers** + +```kotlin +// Bad: Magic number with comment +if (score>= 90) { // 90 is the threshold for A grade + grade = 'A' +} + +// Good: Named constant +const val A_GRADE_THRESHOLD = 90 +if (score>= A_GRADE_THRESHOLD) { + grade = 'A' +} +``` + +### **Break Complex Logic into Functions** + +```kotlin +// Bad: Complex logic with comments +fun processUser(user: User) { + // Check if user is eligible for premium features + if (user.age>= 18 && user.subscriptionType == "premium" && user.paymentStatus == "active") { + // Enable premium features + enablePremiumFeatures(user) + } +} + +// Good: Self-documenting functions +fun processUser(user: User) { + if (isEligibleForPremium(user)) { + enablePremiumFeatures(user) + } +} + +private fun isEligibleForPremium(user: User): Boolean { + return user.age>= 18 && + user.subscriptionType == "premium" && + user.paymentStatus == "active" +} +``` + +## ๐Ÿšจ Common Mistakes to Avoid + +1. **Commenting obvious code**: + ```kotlin + val name = "John" // Sets name to John โŒ + ``` + +2. **Outdated comments**: + ```kotlin + val maxUsers = 100 // Maximum 50 users โŒ + ``` + +3. **Using comments to explain bad code**: + ```kotlin + // This is a hack because the API is broken โŒ + val result = hackyWorkaround() + ``` + +4. **Too many comments**: + ```kotlin + // Get the name + val name = getName() + // Print the name + println(name) โŒ + ``` + +## ๐ŸŽฏ What's Next? + +Excellent! You've learned about comments and documentation. Now you're ready to: + +1. **Explore data types** โ†’ [Data Types Deep Dive](04-data-types.md) +2. **Learn string operations** โ†’ [String Interpolation](05-string-interpolation.md) +3. **Understand control flow** โ†’ [If Expressions](../control-flow/01-if-expressions.md) +4. **Work with functions** โ†’ [Functions Basics](../functions/01-functions-basics.md) + +## ๐Ÿ“š Additional Resources + +- [Kotlin Documentation](https://kotlinlang.org/docs/kotlin-doc.html) +- [KDoc Reference](https://kotlinlang.org/docs/kotlin-doc.html) +- [Comment Best Practices](https://blog.jetbrains.com/kotlin/2019/02/kotlin-1-3-50-released/) + +## ๐Ÿ† Summary + +- โœ… You can write single-line and multi-line comments +- โœ… You understand when and how to use comments effectively +- โœ… You know how to write self-documenting code +- โœ… You follow Kotlin documentation best practices +- โœ… You're ready to write clear, maintainable code! + +**Keep practicing and happy coding! ๐ŸŽ‰** diff --git a/docs/basics/04-data-types.md b/docs/basics/04-data-types.md new file mode 100644 index 0000000..c35fd70 --- /dev/null +++ b/docs/basics/04-data-types.md @@ -0,0 +1,409 @@ +# ๐Ÿ”ข Data Types Deep Dive in Kotlin + +Welcome to the comprehensive guide to data types in Kotlin! In this lesson, you'll explore all the fundamental data types, their characteristics, and how to use them effectively in your programs. + +## ๐Ÿ“‹ Learning Objectives + +By the end of this lesson, you will be able to: +- โœ… Understand all basic data types in Kotlin +- โœ… Choose the right data type for your needs +- โœ… Work with numbers, text, and logical values +- โœ… Understand type conversion and casting +- โœ… Use data types efficiently in your programs + +## ๐Ÿ” What You'll Learn + +- **Number types** - Integers, floating-point numbers, and their ranges +- **Text types** - Characters and strings +- **Logical types** - Boolean values +- **Type conversion** - Converting between different types +- **Type safety** - How Kotlin ensures type safety + +## ๐Ÿ“ Prerequisites + +- Basic understanding of variables +- Completed [Variables and Data Types](02-variables-data-types.md) lesson + +## ๐Ÿ’ป The Code + +Let's examine the data types example: + +```kotlin +fun main(args: Array) { + var name: String + name = "Kevin" + + var age: Int = 10 + var myAge = 10 + + var isAlive: Boolean = true + var marks: Float = 97.4F + var percentage: Double = 90.78 + var gender: Char = 'M' + + print(marks) +} +``` + +**๐Ÿ“ File:** [07_data_types.kt](src/07_data_types.kt) + +## ๐Ÿ” Code Breakdown + +### **String Type** + +```kotlin +var name: String +name = "Kevin" +``` + +- **`String`**: Represents a sequence of characters +- **`"Kevin"`**: String literal (text in double quotes) +- **Mutable**: Can be changed since it's declared with `var` + +### **Integer Types** + +```kotlin +var age: Int = 10 +var myAge = 10 +``` + +- **`Int`**: 32-bit integer type +- **`10`**: Integer literal +- **Type inference**: `myAge` automatically becomes `Int` + +### **Boolean Type** + +```kotlin +var isAlive: Boolean = true +``` + +- **`Boolean`**: Represents true or false values +- **`true`**: Boolean literal +- **Use case**: Logical conditions and flags + +### **Floating-Point Types** + +```kotlin +var marks: Float = 97.4F +var percentage: Double = 90.78 +``` + +- **`Float`**: 32-bit floating-point number +- **`Double`**: 64-bit floating-point number (default for decimals) +- **`97.4F`**: Float literal (note the `F` suffix) +- **`90.78`**: Double literal (default type for decimal numbers) + +### **Character Type** + +```kotlin +var gender: Char = 'M' +``` + +- **`Char`**: Represents a single Unicode character +- **`'M'`**: Character literal (single quotes) + +## ๐ŸŽฏ Key Concepts Explained + +### **1. Number Types in Detail** + +#### **Integer Types** + +| Type | Size | Range | Suffix | Example | +|------|------|-------|--------|---------| +| **Byte** | 8 bits | -128 to 127 | None | `val byte: Byte = 100` | +| **Short** | 16 bits | -32,768 to 32,767 | None | `val short: Short = 1000` | +| **Int** | 32 bits | -231 to 231-1 | None | `val int: Int = 1000000` | +| **Long** | 64 bits | -263 to 263-1 | `L` or `l` | `val long: Long = 1000000000L` | + +**When to use each:** +- **`Byte`**: Very small numbers, memory optimization +- **`Short`**: Small numbers, legacy APIs +- **`Int`**: General-purpose integers (default choice) +- **`Long`**: Large numbers, timestamps, IDs + +#### **Floating-Point Types** + +| Type | Size | Precision | Suffix | Example | +|------|------|-----------|--------|---------| +| **Float** | 32 bits | 6-7 digits | `F` or `f` | `val float: Float = 3.14f` | +| **Double** | 64 bits | 15-17 digits | None | `val double: Double = 3.14` | + +**When to use each:** +- **`Float`**: Memory optimization, sufficient precision +- **`Double`**: High precision, default for decimal numbers + +### **2. Text Types** + +#### **Character (`Char`)** + +```kotlin +val letter: Char = 'A' +val digit: Char = '5' +val symbol: Char = '$' +val unicode: Char = '\u0041' // Unicode for 'A' +``` + +**Characteristics:** +- Single Unicode character +- 16-bit Unicode value +- Single quotes required +- Can represent any Unicode character + +#### **String** + +```kotlin +val name: String = "John Doe" +val empty: String = "" +val multiline: String = """ + This is a + multi-line string +""" +``` + +**Characteristics:** +- Sequence of characters +- Immutable (cannot be changed) +- Double quotes required +- Rich set of methods and properties + +### **3. Boolean Type** + +```kotlin +val isActive: Boolean = true +val isComplete: Boolean = false +val isValid: Boolean = (age>= 18) +``` + +**Use cases:** +- Conditional statements +- Flags and status indicators +- Logical operations +- Control flow decisions + +## ๐Ÿงช Hands-On Exercises + +### **Exercise 1: Type Declaration Practice** +Create variables of different types and observe type inference: + +```kotlin +fun main(args: Array) { + // TODO: Add your solution here +} +``` + +**Solution:** +```kotlin +fun main(args: Array) { + // Explicit type declarations + val age: Int = 25 + val height: Double = 1.75 + val isStudent: Boolean = true + val grade: Char = 'A' + val name: String = "Alice" + + // Type inference examples + val temperature = 23.5 // Double (default for decimals) + val year = 2024 // Int + val message = "Hello" // String + val flag = false // Boolean + + // Print types to verify + println("Age type: ${age::class.simpleName}") + println("Height type: ${height::class.simpleName}") + println("Temperature type: ${temperature::class.simpleName}") +} +``` + +### **Exercise 2: Number Type Limits** +Explore the limits of different number types: + +```kotlin +fun main(args: Array) { + // TODO: Add your solution here +} +``` + +**Solution:** +```kotlin +fun main(args: Array) { + // Byte limits + val maxByte: Byte = Byte.MAX_VALUE + val minByte: Byte = Byte.MIN_VALUE + println("Byte range: $minByte to $maxByte") + + // Int limits + val maxInt: Int = Int.MAX_VALUE + val minInt: Int = Int.MIN_VALUE + println("Int range: $minInt to $maxInt") + + // Long limits + val maxLong: Long = Long.MAX_VALUE + val minLong: Long = Long.MIN_VALUE + println("Long range: $minLong to $maxLong") + + // Float vs Double precision + val floatValue: Float = 3.14159265359f + val doubleValue: Double = 3.14159265359 + println("Float: $floatValue") + println("Double: $doubleValue") +} +``` + +### **Exercise 3: Type Conversion** +Practice converting between different types: + +```kotlin +fun main(args: Array) { + // TODO: Add your solution here +} +``` + +**Solution:** +```kotlin +fun main(args: Array) { + val intValue = 42 + val doubleValue = 3.14 + + // Int to other types + val longFromInt: Long = intValue.toLong() + val floatFromInt: Float = intValue.toFloat() + val doubleFromInt: Double = intValue.toDouble() + + // Double to other types + val intFromDouble: Int = doubleValue.toInt() // Truncates decimal part + val floatFromDouble: Float = doubleValue.toFloat() + val longFromDouble: Long = doubleValue.toLong() + + // String conversions + val stringFromInt = intValue.toString() + val intFromString = "123".toInt() + + println("Original int: $intValue") + println("As long: $longFromInt") + println("As float: $floatFromInt") + println("As double: $doubleFromInt") + println("As string: $stringFromInt") +} +``` + +### **Exercise 4: Practical Data Types** +Create a student information system using appropriate data types: + +```kotlin +fun main(args: Array) { + // TODO: Add your solution here +} +``` + +**Solution:** +```kotlin +fun main(args: Array) { + // Student information with appropriate types + val studentId: Long = 2024001L // Long for large ID numbers + val firstName: String = "John" + val lastName: String = "Doe" + val age: Int = 20 // Int for age + val height: Double = 1.75 // Double for precise height + val weight: Float = 70.5f // Float for weight + val grade: Char = 'A' // Char for single grade + val isEnrolled: Boolean = true // Boolean for enrollment status + val gpa: Double = 3.85 // Double for precise GPA + + // Display student information + println("Student ID: $studentId") + println("Name: $firstName $lastName") + println("Age: $age years") + println("Height: ${height}m") + println("Weight: ${weight}kg") + println("Grade: $grade") + println("Enrolled: $isEnrolled") + println("GPA: $gpa") + + // Calculate age in months + val ageInMonths: Int = age * 12 + println("Age in months: $ageInMonths") +} +``` + +## ๐Ÿ” Type Conversion and Casting + +### **Implicit Conversion (Automatic)** + +Kotlin automatically converts smaller types to larger types: + +```kotlin +val intValue: Int = 42 +val longValue: Long = intValue // Automatic conversion Int โ†’ Long +val doubleValue: Double = intValue // Automatic conversion Int โ†’ Double +``` + +### **Explicit Conversion (Manual)** + +Use conversion functions for potentially lossy conversions: + +```kotlin +val doubleValue: Double = 3.14 +val intValue: Int = doubleValue.toInt() // Truncates to 3 +val longValue: Long = doubleValue.toLong() // Truncates to 3 +``` + +### **Safe Conversion Functions** + +```kotlin +val stringNumber = "123" +val intNumber = stringNumber.toIntOrNull() // Returns null if conversion fails +val longNumber = stringNumber.toLongOrNull() +val doubleNumber = stringNumber.toDoubleOrNull() +``` + +## ๐Ÿšจ Common Mistakes to Avoid + +1. **Type mismatch errors**: + ```kotlin + val age: Int = "25" // โŒ Type mismatch + val age: Int = 25 // โœ… Correct + ``` + +2. **Forgetting suffixes for specific types**: + ```kotlin + val floatValue: Float = 3.14 // โŒ 3.14 is Double by default + val floatValue: Float = 3.14f // โœ… Correct + ``` + +3. **Using wrong number types**: + ```kotlin + val largeNumber: Int = 3000000000 // โŒ Too large for Int + val largeNumber: Long = 3000000000L // โœ… Correct + ``` + +4. **Character vs String confusion**: + ```kotlin + val letter: Char = "A" // โŒ Double quotes for String + val letter: Char = 'A' // โœ… Single quotes for Char + ``` + +## ๐ŸŽฏ What's Next? + +Excellent! You've mastered data types in Kotlin. Now you're ready to: + +1. **Learn string operations** โ†’ [String Interpolation](05-string-interpolation.md) +2. **Understand control flow** โ†’ [If Expressions](../control-flow/01-if-expressions.md) +3. **Work with functions** โ†’ [Functions Basics](../functions/01-functions-basics.md) +4. **Explore collections** โ†’ [Arrays](../collections/01-arrays.md) + +## ๐Ÿ“š Additional Resources + +- [Kotlin Basic Types](https://kotlinlang.org/docs/basic-types.html) +- [Number Types](https://kotlinlang.org/docs/numbers.html) +- [Strings](https://kotlinlang.org/docs/strings.html) +- [Type Conversion](https://kotlinlang.org/docs/numbers.html#explicit-conversions) + +## ๐Ÿ† Summary + +- โœ… You understand all basic data types in Kotlin +- โœ… You can choose the right data type for your needs +- โœ… You know how to work with numbers, text, and logical values +- โœ… You understand type conversion and casting +- โœ… You can use data types efficiently in your programs! + +**Keep practicing and happy coding! ๐ŸŽ‰** diff --git a/docs/basics/05-string-interpolation.md b/docs/basics/05-string-interpolation.md new file mode 100644 index 0000000..3f83310 --- /dev/null +++ b/docs/basics/05-string-interpolation.md @@ -0,0 +1,380 @@ +# ๐Ÿ”ค String Interpolation in Kotlin + +Welcome to the world of string interpolation! This powerful feature allows you to embed variables and expressions directly into strings, making your code more readable and maintainable. + +## ๐Ÿ“‹ Learning Objectives + +By the end of this lesson, you will be able to: +- โœ… Use string interpolation with the `$` symbol +- โœ… Embed variables and expressions in strings +- โœ… Format complex expressions with `${}` syntax +- โœ… Create dynamic and readable string output +- โœ… Understand when and how to use string interpolation + +## ๐Ÿ” What You'll Learn + +- **Basic interpolation** - Embedding simple variables in strings +- **Expression interpolation** - Using complex expressions with `${}` +- **Formatting** - Controlling how values are displayed +- **Best practices** - When and how to use interpolation effectively +- **Performance considerations** - Understanding interpolation overhead + +## ๐Ÿ“ Prerequisites + +- Basic understanding of variables and data types +- Completed [Data Types Deep Dive](04-data-types.md) lesson + +## ๐Ÿ’ป The Code + +Let's examine the string interpolation example: + +```kotlin +fun main(args: Array) { + var rect = Rectangle() + rect.length = 5 + rect.breadth = 3 + + print("The length of the rectangle is ${rect.length} and breadth is ${rect.breadth}. The area is ${rect.length * rect.breadth}") +} + +class Rectangle { + var length: Int = 0 + var breadth: Int = 0 +} +``` + +**๐Ÿ“ File:** [08_string_interpolation.kt](src/08_string_interpolation.kt) + +## ๐Ÿ” Code Breakdown + +### **Rectangle Class** + +```kotlin +class Rectangle { + var length: Int = 0 + var breadth: Int = 0 +} +``` + +- **Simple class** with two properties +- **`length`** and **`breadth`** represent rectangle dimensions +- **Default values** of 0 for both properties + +### **String Interpolation Usage** + +```kotlin +print("The length of the rectangle is ${rect.length} and breadth is ${rect.breadth}. The area is ${rect.length * rect.breadth}") +``` + +- **`${rect.length}`**: Embeds the length value +- **`${rect.breadth}`**: Embeds the breadth value +- **`${rect.length * rect.breadth}`**: Embeds the calculated area + +## ๐ŸŽฏ Key Concepts Explained + +### **1. Basic String Interpolation** + +#### **Simple Variable Interpolation** + +```kotlin +val name = "John" +val age = 25 +val message = "Hello, my name is $name and I am $age years old" +println(message) +// Output: Hello, my name is John and I am 25 years old +``` + +**Syntax:** +- **`$variableName`**: Embeds a simple variable +- **No spaces**: Variable name must immediately follow `$` +- **Simple types**: Works with strings, numbers, booleans + +#### **Expression Interpolation** + +```kotlin +val a = 10 +val b = 5 +val result = "Sum: ${a + b}, Product: ${a * b}, Difference: ${a - b}" +println(result) +// Output: Sum: 15, Product: 50, Difference: 5 +``` + +**Syntax:** +- **`${expression}`**: Embeds any valid Kotlin expression +- **Curly braces**: Required for complex expressions +- **Arithmetic**: Can include calculations, function calls, etc. + +### **2. When to Use Each Syntax** + +#### **Use `$variable` for:** +- Simple variable names +- Properties without spaces +- Basic values + +```kotlin +val name = "Alice" +val greeting = "Hello $name!" // โœ… Simple variable +``` + +#### **Use `${expression}` for:** +- Complex expressions +- Function calls +- Calculations +- Properties with spaces +- Any expression that needs evaluation + +```kotlin +val firstName = "John" +val lastName = "Doe" +val fullName = "${firstName} ${lastName}" // โœ… Expression with spaces + +val radius = 5.0 +val area = "Area: ${Math.PI * radius * radius}" // โœ… Complex calculation +``` + +### **3. Advanced Interpolation Examples** + +#### **Function Calls in Interpolation** + +```kotlin +fun getCurrentTime(): String = "12:00 PM" + +val message = "Current time is ${getCurrentTime()}" +println(message) +// Output: Current time is 12:00 PM +``` + +#### **Conditional Expressions** + +```kotlin +val score = 85 +val grade = "Your grade is ${if (score>= 90) 'A' else if (score>= 80) 'B' else 'C'}" +println(grade) +// Output: Your grade is B +``` + +#### **Object Properties** + +```kotlin +data class Person(val name: String, val age: Int) + +val person = Person("Bob", 30) +val info = "Person: ${person.name}, Age: ${person.age}" +println(info) +// Output: Person: Bob, Age: 30 +``` + +## ๐Ÿงช Hands-On Exercises + +### **Exercise 1: Basic String Interpolation** +Create a program that uses string interpolation to display personal information: + +```kotlin +fun main(args: Array) { + // TODO: Add your solution here +} +``` + +**Solution:** +```kotlin +fun main(args: Array) { + val name = "Alice" + val age = 25 + val city = "New York" + val profession = "Software Developer" + + val bio = "Hi, I'm $name, a $age-year-old $profession from $city." + println(bio) + + // Using expressions + val birthYear = 2024 - age + val message = "I was born in $birthYear and have been coding for ${age - 18} years." + println(message) +} +``` + +### **Exercise 2: Mathematical Expressions** +Create a calculator that displays results using string interpolation: + +```kotlin +fun main(args: Array) { + // TODO: Add your solution here +} +``` + +**Solution:** +```kotlin +fun main(args: Array) { + val a = 15 + val b = 7 + + println("Numbers: $a and $b") + println("Sum: ${a + b}") + println("Difference: ${a - b}") + println("Product: ${a * b}") + println("Quotient: ${a / b}") + println("Remainder: ${a % b}") + println("Power: ${a.toDouble().pow(b.toDouble())}") + + // Calculate average + val average = (a + b) / 2.0 + println("Average: $average") +} +``` + +### **Exercise 3: Complex Object Interpolation** +Create a product catalog using string interpolation: + +```kotlin +fun main(args: Array) { + // TODO: Add your solution here +} +``` + +**Solution:** +```kotlin +data class Product(val name: String, val price: Double, val category: String) + +fun main(args: Array) { + val products = listOf( + Product("Laptop", 999.99, "Electronics"), + Product("Book", 19.99, "Education"), + Product("Coffee", 4.99, "Beverages") + ) + + for (product in products) { + val description = """ + Product: ${product.name} + Price: $${product.price} + Category: ${product.category} + Tax (8%): $${product.price * 0.08} + Total: $${product.price * 1.08} + """.trimIndent() + + println(description) + println("---") + } +} +``` + +### **Exercise 4: Dynamic Messages** +Create a weather app that generates dynamic messages: + +```kotlin +fun main(args: Array) { + // TODO: Add your solution here +} +``` + +**Solution:** +```kotlin +fun main(args: Array) { + val temperature = 28 + val humidity = 65 + val isRaining = false + + val weatherMessage = """ + ๐ŸŒค๏ธ Weather Report + Temperature: ${temperature}ยฐC + Humidity: ${humidity}% + Conditions: ${if (isRaining) "Rainy" else "Clear"} + + ${when { + temperature> 30 -> "It's hot today! Stay hydrated." + temperature> 20 -> "Pleasant weather for outdoor activities." + temperature> 10 -> "Cool weather, bring a light jacket." + else -> "Cold weather, dress warmly." + }} + + ${if (humidity> 70) "High humidity - expect muggy conditions." else "Comfortable humidity levels."} + """.trimIndent() + + println(weatherMessage) +} +``` + +## ๐Ÿ” String Interpolation vs String Concatenation + +### **String Interpolation (Recommended)** + +```kotlin +val name = "John" +val age = 25 +val message = "Hello $name, you are $age years old" +``` + +**Advantages:** +- โœ… More readable +- โœ… Less error-prone +- โœ… Better performance +- โœ… Cleaner syntax + +### **String Concatenation (Old Way)** + +```kotlin +val name = "John" +val age = 25 +val message = "Hello " + name + ", you are " + age + " years old" +``` + +**Disadvantages:** +- โŒ Harder to read +- โŒ More error-prone +- โŒ Poorer performance +- โŒ Verbose syntax + +## ๐Ÿšจ Common Mistakes to Avoid + +1. **Missing curly braces for expressions**: + ```kotlin + val a = 5 + val b = 3 + val result = "Sum: $a + b" // โŒ Output: Sum: 5 + b + val result = "Sum: ${a + b}" // โœ… Output: Sum: 8 + ``` + +2. **Using `$` in regular strings**: + ```kotlin + val price = 100 + val message = "Price: $price" // โŒ Won't work if you want literal $ + val message = "Price: \$$price" // โœ… Escapes the $ symbol + ``` + +3. **Complex expressions without braces**: + ```kotlin + val name = "John" + val message = "Hello $name.toUpperCase()" // โŒ Won't work + val message = "Hello ${name.uppercase()}" // โœ… Correct + ``` + +4. **Forgetting to import functions**: + ```kotlin + val result = "Power: ${Math.pow(2.0, 3.0)}" // โŒ Math not imported + import kotlin.math.pow + val result = "Power: ${2.0.pow(3.0)}" // โœ… Correct + ``` + +## ๐ŸŽฏ What's Next? + +Excellent! You've mastered string interpolation in Kotlin. Now you're ready to: + +1. **Learn control flow** โ†’ [If Expressions](../control-flow/01-if-expressions.md) +2. **Understand loops** โ†’ [For Loops](../control-flow/03-for-loops.md) +3. **Work with functions** โ†’ [Functions Basics](../functions/01-functions-basics.md) +4. **Explore collections** โ†’ [Arrays](../collections/01-arrays.md) + +## ๐Ÿ“š Additional Resources + +- [Kotlin Strings](https://kotlinlang.org/docs/strings.html) +- [String Templates](https://kotlinlang.org/docs/strings.html#string-templates) +- [String Formatting](https://kotlinlang.org/docs/strings.html#string-templates) + +## ๐Ÿ† Summary + +- โœ… You can use string interpolation with the `$` symbol +- โœ… You can embed variables and expressions in strings +- โœ… You understand when to use `${}` vs `$` +- โœ… You can create dynamic and readable string output +- โœ… You know the best practices for string interpolation! + +**Keep practicing and happy coding! ๐ŸŽ‰** diff --git a/docs/functions/01-functions-basics.md b/docs/functions/01-functions-basics.md new file mode 100644 index 0000000..08e9634 --- /dev/null +++ b/docs/functions/01-functions-basics.md @@ -0,0 +1,305 @@ +# โš™๏ธ Functions Basics in Kotlin + +Welcome to the world of functions! Functions are the building blocks of reusable code in Kotlin. They allow you to organize your code into logical, reusable pieces. + +## ๐Ÿ“‹ Learning Objectives + +By the end of this lesson, you will be able to: +- โœ… Declare and call functions in Kotlin +- โœ… Understand function parameters and return types +- โœ… Create functions that perform specific tasks +- โœ… Call functions from the main function +- โœ… Understand basic function syntax and structure + +## ๐Ÿ” What You'll Learn + +- **Function declaration** - How to create functions in Kotlin +- **Parameters** - Input values that functions can receive +- **Return types** - The type of value the function returns +- **Function calls** - How to execute a function +- **Code organization** - How to structure your programs + +## ๐Ÿ“ Prerequisites + +- Basic understanding of variables and data types +- Completed [Variables and Data Types](../basics/02-variables-data-types.md) lesson + +## ๐Ÿ’ป The Code + +Let's examine the functions basics example: + +```kotlin +fun main(args: Array) { + // Calling the add function with two integer parameters + val sum = add(2, 4) + println("Sum is $sum") + + // You can call functions multiple times with different values + val anotherSum = add(10, 20) + println("Another sum is $anotherSum") + + // Functions can be called in expressions + val total = add(5, 3) + add(1, 2) + println("Total of multiple additions: $total") +} + +/** + * Adds two integers and returns their sum + * + * @param a The first integer to add + * @param b The second integer to add + * @return The sum of a and b + * + * This is a simple function that demonstrates: + * - Parameter declaration + * - Return type specification + * - Basic arithmetic operations + * - Return statement usage + */ +fun add(a: Int, b: Int): Int { + return a + b +} +``` + +**๐Ÿ“ File:** [17_functions_basics.kt](src/17_functions_basics.kt) + +## ๐Ÿ” Code Breakdown + +### **Main Function** + +```kotlin +fun main(args: Array) { + // Function calls and logic here +} +``` + +- **Entry point** of the program +- **Calls other functions** to perform tasks +- **Demonstrates** how functions work together + +### **Function Declaration** + +```kotlin +fun add(a: Int, b: Int): Int { + return a + b +} +``` + +- **`fun`**: Keyword to declare a function +- **`add`**: Function name (descriptive and meaningful) +- **`(a: Int, b: Int)`**: Parameter list + - `a: Int` - First parameter named `a` of type `Int` + - `b: Int` - Second parameter named `b` of type `Int` +- **`: Int`**: Return type (function returns an integer) +- **`{ return a + b }`**: Function body with return statement + +### **Function Calls** + +```kotlin +val sum = add(2, 4) +val anotherSum = add(10, 20) +val total = add(5, 3) + add(1, 2) +``` + +- **`add(2, 4)`**: Calls the function with arguments `2` and `4` +- **Arguments**: The actual values passed to the function +- **Return value**: The result is stored in variables + +## ๐ŸŽฏ Key Concepts Explained + +### **1. Function Declaration Syntax** + +```kotlin +fun functionName(parameter1: Type1, parameter2: Type2): ReturnType { + // Function body + return result +} +``` + +**Components:** +- **`fun`**: Function keyword +- **`functionName`**: Descriptive name (use camelCase) +- **`(parameters)`**: Input values the function needs +- **`: ReturnType`**: Type of value the function returns +- **`{ }`**: Function body containing the logic +- **`return`**: Statement that sends back the result + +### **2. Parameters vs Arguments** + +```kotlin +// Function declaration (parameters) +fun greet(name: String, age: Int): String { + return "Hello $name, you are $age years old" +} + +// Function call (arguments) +val message = greet("Alice", 25) +``` + +- **Parameters**: Variables declared in the function signature +- **Arguments**: Actual values passed when calling the function + +### **3. Return Types** + +```kotlin +fun noReturn(): Unit { // Unit is default, can be omitted + println("Hello") +} + +fun returnString(): String { + return "Hello" +} + +fun returnNumber(): Int { + return 42 +} + +fun returnBoolean(): Boolean { + return true +} +``` + +- **`Unit`**: No return value (like `void` in other languages) +- **`String`**: Returns text +- **`Int`**: Returns a number +- **`Boolean`**: Returns true/false + +## ๐Ÿงช Hands-On Exercises + +### **Exercise 1: Create a Multiply Function** +Create a function that multiplies two numbers and returns the result. + +**Solution:** +```kotlin +fun main(args: Array) { + val result = multiply(5, 3) + println("5 * 3 = $result") +} + +fun multiply(a: Int, b: Int): Int { + return a * b +} +``` + +### **Exercise 2: Create a Greet Function** +Create a function that takes a name and returns a greeting message. + +**Solution:** +```kotlin +fun main(args: Array) { + val message = greet("John") + println(message) +} + +fun greet(name: String): String { + return "Hello, $name! Welcome to Kotlin!" +} +``` + +### **Exercise 3: Create an isEven Function** +Create a function that checks if a number is even and returns true/false. + +**Solution:** +```kotlin +fun main(args: Array) { + println("Is 4 even? ${isEven(4)}") + println("Is 7 even? ${isEven(7)}") +} + +fun isEven(number: Int): Boolean { + return number % 2 == 0 +} +``` + +### **Exercise 4: Create a calculateArea Function** +Create a function that calculates the area of a rectangle. + +**Solution:** +```kotlin +fun main(args: Array) { + val area = calculateArea(5.0, 3.0) + println("Area of rectangle: $area") +} + +fun calculateArea(length: Double, width: Double): Double { + return length * width +} +``` + +## ๐Ÿ” Function Syntax Breakdown + +```kotlin +fun add(a: Int, b: Int): Int { +โ”‚ โ”‚ โ”‚ โ”‚ โ”‚ โ”‚ โ”‚ +โ”‚ โ”‚ โ”‚ โ”‚ โ”‚ โ”‚ โ””โ”€ Function body (what the function does) +โ”‚ โ”‚ โ”‚ โ”‚ โ”‚ โ””โ”€โ”€โ”€โ”€โ”€ Return type (Int in this case) +โ”‚ โ”‚ โ”‚ โ”‚ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ Second parameter (b of type Int) +โ”‚ โ”‚ โ”‚ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ First parameter (a of type Int) +โ”‚ โ”‚ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ Function name +โ”‚ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ Function keyword +``` + +## ๐Ÿšจ Common Mistakes to Avoid + +1. **Missing return type for non-Unit functions**: + ```kotlin + fun add(a: Int, b: Int) { // โŒ Missing return type + return a + b + } + + fun add(a: Int, b: Int): Int { // โœ… Correct + return a + b + } + ``` + +2. **Wrong parameter types**: + ```kotlin + fun greet(name: String) { + println("Hello $name") + } + + greet(42) // โŒ Wrong type, should be String + greet("42") // โœ… Correct + ``` + +3. **Missing return statement**: + ```kotlin + fun getNumber(): Int { + // โŒ Missing return statement + } + + fun getNumber(): Int { + return 42 // โœ… Correct + } + ``` + +4. **Function name conflicts**: + ```kotlin + fun add(a: Int, b: Int): Int { return a + b } + fun add(a: Int, b: Int): Int { return a + b } // โŒ Duplicate function + ``` + +## ๐ŸŽฏ What's Next? + +Excellent! You've learned the basics of functions. Now you're ready to: + +1. **Learn functions as expressions** โ†’ [Functions as Expressions](02-functions-expressions.md) +2. **Explore extension functions** โ†’ [Extension Functions](03-extension-functions.md) +3. **Understand named parameters** โ†’ [Named Parameters](05-named-parameters.md) +4. **Work with classes** โ†’ [Classes and Constructors](../oop/01-classes-constructors.md) + +## ๐Ÿ“š Additional Resources + +- [Kotlin Functions Documentation](https://kotlinlang.org/docs/functions.html) +- [Function Parameters](https://kotlinlang.org/docs/functions.html#function-parameters) +- [Function Return Types](https://kotlinlang.org/docs/functions.html#function-scope) + +## ๐Ÿ† Summary + +- โœ… You can declare and call functions in Kotlin +- โœ… You understand function parameters and return types +- โœ… You can create functions that perform specific tasks +- โœ… You know how to organize code into reusable pieces +- โœ… You're ready to explore more advanced function concepts! + +**Keep practicing and happy coding! ๐ŸŽ‰** From ecb7c092fd6b38dec97c1995f26ad3ba9de8dc67 Mon Sep 17 00:00:00 2001 From: gpl-gowthamchand Date: 2025ๅนด8ๆœˆ24ๆ—ฅ 18:54:56 +0530 Subject: [PATCH 02/23] feat: Add comprehensive control flow documentation - Add If Expressions guide with exercises and best practices - Add When Expressions guide with multiple examples - Add For Loops guide with range types and collection iteration - Include hands-on exercises for each topic - Provide clear learning paths and advanced concepts --- docs/control-flow/01-if-expressions.md | 408 +++++++++++++++++++ docs/control-flow/02-when-expressions.md | 473 ++++++++++++++++++++++ docs/control-flow/03-for-loops.md | 490 +++++++++++++++++++++++ 3 files changed, 1371 insertions(+) create mode 100644 docs/control-flow/01-if-expressions.md create mode 100644 docs/control-flow/02-when-expressions.md create mode 100644 docs/control-flow/03-for-loops.md diff --git a/docs/control-flow/01-if-expressions.md b/docs/control-flow/01-if-expressions.md new file mode 100644 index 0000000..dc26981 --- /dev/null +++ b/docs/control-flow/01-if-expressions.md @@ -0,0 +1,408 @@ +# ๐ŸŽฏ If Expressions in Kotlin + +Welcome to the world of decision-making in Kotlin! If expressions allow your programs to make choices and execute different code based on conditions. This is where your programs start to become truly dynamic and intelligent. + +## ๐Ÿ“‹ Learning Objectives + +By the end of this lesson, you will be able to: +- โœ… Write basic if-else statements +- โœ… Use if expressions to return values +- โœ… Understand the difference between statements and expressions +- โœ… Create nested if conditions +- โœ… Use logical operators in conditions + +## ๐Ÿ” What You'll Learn + +- **Basic if statements** - Simple decision-making +- **If expressions** - Using if to return values +- **If-else chains** - Multiple conditions +- **Nested if statements** - Complex decision trees +- **Logical operators** - Combining conditions + +## ๐Ÿ“ Prerequisites + +- Basic understanding of variables and data types +- Completed [String Interpolation](../basics/05-string-interpolation.md) lesson + +## ๐Ÿ’ป The Code + +Let's examine the if expression example: + +```kotlin +fun main(args: Array) { + val a = 2 + val b = 5 + + var maxValue: Int = if (a> b) { + print("a is greater") + a + } else { + print("b is greater") + b + } + + println(maxValue) +} +``` + +**๐Ÿ“ File:** [10_if_expression.kt](src/10_if_expression.kt) + +## ๐Ÿ” Code Breakdown + +### **Variable Declaration** + +```kotlin +val a = 2 +val b = 5 +``` + +- **Two integers** for comparison +- **`val`** makes them immutable (cannot be changed) + +### **If Expression** + +```kotlin +var maxValue: Int = if (a> b) { + print("a is greater") + a + } else { + print("b is greater") + b + } +``` + +- **`if (a> b)`**: Condition to check if `a` is greater than `b` +- **`{ }`**: Code block for when condition is true +- **`else`**: Code block for when condition is false +- **`a` and `b`**: Values returned by the if expression +- **`maxValue`**: Variable that stores the result + +## ๐ŸŽฏ Key Concepts Explained + +### **1. If Statements vs If Expressions** + +#### **Traditional If Statement (No Return Value)** + +```kotlin +val a = 10 +val b = 5 + +if (a> b) { + println("a is greater") +} else { + println("b is greater") +} +``` + +**Characteristics:** +- Executes code blocks +- No return value +- Used for side effects (printing, calculations) + +#### **If Expression (Returns a Value)** + +```kotlin +val a = 10 +val b = 5 + +val maxValue = if (a> b) a else b +println("Maximum value: $maxValue") +``` + +**Characteristics:** +- Returns a value +- Can be assigned to variables +- More functional programming style + +### **2. Basic If Statement Syntax** + +```kotlin +if (condition) { + // Code to execute when condition is true +} else { + // Code to execute when condition is false +} +``` + +**Components:** +- **`if (condition)`**: Boolean expression that evaluates to true/false +- **`{ }`**: Code blocks containing statements +- **`else`**: Optional alternative execution path + +### **3. If Expression Syntax** + +```kotlin +val result = if (condition) { + // Code block that returns a value + value1 +} else { + // Code block that returns a value + value2 +} +``` + +**Key Points:** +- **Last expression** in each block becomes the return value +- **Same type** must be returned from both branches +- **Can be assigned** directly to variables + +## ๐Ÿงช Hands-On Exercises + +### **Exercise 1: Basic If Statement** +Create a program that checks if a number is positive, negative, or zero: + +```kotlin +fun main(args: Array) { + // TODO: Add your solution here +} +``` + +**Solution:** +```kotlin +fun main(args: Array) { + val number = -5 + + if (number> 0) { + println("$number is positive") + } else if (number < 0) { + println("$number is negative") + } else { + println("$number is zero") + } +} +``` + +### **Exercise 2: If Expression for Grade Calculation** +Use if expressions to calculate a letter grade: + +```kotlin +fun main(args: Array) { + // TODO: Add your solution here +} +``` + +**Solution:** +```kotlin +fun main(args: Array) { + val score = 85 + + val grade = if (score>= 90) { + 'A' + } else if (score>= 80) { + 'B' + } else if (score>= 70) { + 'C' + } else if (score>= 60) { + 'D' + } else { + 'F' + } + + println("Score: $score, Grade: $grade") +} +``` + +### **Exercise 3: Complex If Expression** +Create a program that determines the best discount based on purchase amount and customer type: + +```kotlin +fun main(args: Array) { + // TODO: Add your solution here +} +``` + +**Solution:** +```kotlin +fun main(args: Array) { + val purchaseAmount = 150.0 + val isPremiumCustomer = true + + val discount = if (isPremiumCustomer) { + if (purchaseAmount>= 200) { + purchaseAmount * 0.20 // 20% discount + } else if (purchaseAmount>= 100) { + purchaseAmount * 0.15 // 15% discount + } else { + purchaseAmount * 0.10 // 10% discount + } + } else { + if (purchaseAmount>= 200) { + purchaseAmount * 0.10 // 10% discount + } else if (purchaseAmount>= 100) { + purchaseAmount * 0.05 // 5% discount + } else { + 0.0 // No discount + } + } + + val finalAmount = purchaseAmount - discount + println("Purchase Amount: $${purchaseAmount}") + println("Discount: $${discount}") + println("Final Amount: $${finalAmount}") +} +``` + +### **Exercise 4: Logical Operators in If** +Create a program that checks multiple conditions: + +```kotlin +fun main(args: Array) { + // TODO: Add your solution here +} +``` + +**Solution:** +```kotlin +fun main(args: Array) { + val age = 25 + val hasLicense = true + val hasInsurance = false + + val canDrive = if (age>= 18 && hasLicense) { + if (hasInsurance) { + "Can drive safely with insurance" + } else { + "Can drive but needs insurance" + } + } else if (age < 18) { + "Too young to drive" + } else { + "Needs a driver's license" + } + + println(canDrive) +} +``` + +## ๐Ÿ” Advanced If Expression Concepts + +### **1. Single-Line If Expressions** + +```kotlin +val max = if (a> b) a else b +val status = if (isActive) "Online" else "Offline" +val message = if (age>= 18) "Adult" else "Minor" +``` + +**Use when:** +- Simple conditions +- Single expressions +- Quick assignments + +### **2. Multi-Line If Expressions** + +```kotlin +val result = if (condition) { + val temp = calculateTemp() + val processed = process(temp) + processed +} else { + val default = getDefault() + default +} +``` + +**Use when:** +- Complex calculations +- Multiple steps +- Need intermediate variables + +### **3. Nested If Expressions** + +```kotlin +val category = if (age < 13) { + "Child" +} else if (age < 20) { + if (isStudent) "Teen Student" else "Teen" +} else if (age < 65) { + if (isWorking) "Working Adult" else "Adult" +} else { + "Senior" +} +``` + +## ๐Ÿ” Logical Operators + +### **Comparison Operators** + +| Operator | Description | Example | +|----------|-------------|---------| +| **`==`** | Equal to | `a == b` | +| **`!=`** | Not equal to | `a != b` | +| **`>`** | Greater than | `a> b` | +| **`<`** | Less than | `a < b` | +| **`>=`** | Greater than or equal to | `a>= b` | +| **`<=`** | Less than or equal to | `a <= b` | + +### **Logical Operators** + +| Operator | Description | Example | +|----------|-------------|---------| +| **`&&`** | AND (both must be true) | `a> 0 && b> 0` | +| **`\|\|`** | OR (at least one must be true) | `a> 0 \|\| b> 0` | +| **`!`** | NOT (inverts the condition) | `!(a> 0)` | + +### **Examples** + +```kotlin +val age = 25 +val hasLicense = true +val hasInsurance = false + +// Multiple conditions +val canDrive = age>= 18 && hasLicense +val needsInsurance = canDrive && !hasInsurance +val isEligible = age>= 18 && (hasLicense || hasInsurance) +``` + +## ๐Ÿšจ Common Mistakes to Avoid + +1. **Missing else branch in expressions**: + ```kotlin + val result = if (a> b) a // โŒ Missing else branch + val result = if (a> b) a else b // โœ… Correct + ``` + +2. **Using assignment instead of comparison**: + ```kotlin + if (a = 5) { } // โŒ Assignment, not comparison + if (a == 5) { } // โœ… Comparison + ``` + +3. **Forgetting curly braces for multiple statements**: + ```kotlin + if (a> b) println("a is greater"); a // โŒ Only first statement executes + if (a> b) { println("a is greater"); a } // โœ… Both statements execute + ``` + +4. **Inconsistent return types**: + ```kotlin + val result = if (a> b) "Greater" else 42 // โŒ Different types + val result = if (a> b) "Greater" else "Less" // โœ… Same type + ``` + +## ๐ŸŽฏ What's Next? + +Excellent! You've mastered if expressions in Kotlin. Now you're ready to: + +1. **Learn when expressions** โ†’ [When Expressions](02-when-expressions.md) +2. **Understand loops** โ†’ [For Loops](03-for-loops.md) +3. **Work with functions** โ†’ [Functions Basics](../functions/01-functions-basics.md) +4. **Explore collections** โ†’ [Arrays](../collections/01-arrays.md) + +## ๐Ÿ“š Additional Resources + +- [Kotlin Control Flow](https://kotlinlang.org/docs/control-flow.html) +- [If Expressions](https://kotlinlang.org/docs/control-flow.html#if-expression) +- [When Expression](https://kotlinlang.org/docs/control-flow.html#when-expression) + +## ๐Ÿ† Summary + +- โœ… You can write basic if-else statements +- โœ… You can use if expressions to return values +- โœ… You understand the difference between statements and expressions +- โœ… You can create nested if conditions +- โœ… You can use logical operators in conditions +- โœ… You're ready to make your programs intelligent! + +**Keep practicing and happy coding! ๐ŸŽ‰** diff --git a/docs/control-flow/02-when-expressions.md b/docs/control-flow/02-when-expressions.md new file mode 100644 index 0000000..c5eebe1 --- /dev/null +++ b/docs/control-flow/02-when-expressions.md @@ -0,0 +1,473 @@ +# ๐ŸŽฒ When Expressions in Kotlin + +Welcome to the powerful world of when expressions! When expressions are Kotlin's replacement for switch statements and provide a much more flexible and expressive way to handle multiple conditions. They're perfect for replacing long if-else chains with clean, readable code. + +## ๐Ÿ“‹ Learning Objectives + +By the end of this lesson, you will be able to: +- โœ… Write basic when expressions +- โœ… Use when expressions to return values +- โœ… Handle multiple conditions elegantly +- โœ… Use when expressions with different data types +- โœ… Understand when expressions vs if-else chains + +## ๐Ÿ” What You'll Learn + +- **Basic when expressions** - Simple multiple condition handling +- **When expressions as values** - Using when to return results +- **Multiple conditions** - Handling various cases elegantly +- **Type matching** - Using when with different data types +- **Best practices** - When and how to use when expressions + +## ๐Ÿ“ Prerequisites + +- Basic understanding of if expressions +- Completed [If Expressions](01-if-expressions.md) lesson + +## ๐Ÿ’ป The Code + +Let's examine the when expression example: + +```kotlin +fun main(args: Array) { + val x = 100 + + val str: String = when (x) { + 1 -> "x is 1" + 2 -> "x is 2" + else -> { + "x value is unknown" + "x is an alien" + } + } + + println(str) +} +``` + +**๐Ÿ“ File:** [11_when_expression.kt](src/11_when_expression.kt) + +## ๐Ÿ” Code Breakdown + +### **Variable Declaration** + +```kotlin +val x = 100 +``` + +- **Single variable** to test against multiple values +- **`val`** makes it immutable + +### **When Expression** + +```kotlin +val str: String = when (x) { + 1 -> "x is 1" + 2 -> "x is 2" + else -> { + "x value is unknown" + "x is an alien" + } +} +``` + +- **`when (x)`**: Tests the value of `x` against different cases +- **`1 -> "x is 1"`**: If `x` equals 1, return "x is 1" +- **`2 -> "x is 2"`**: If `x` equals 2, return "x is 2" +- **`else -> { }`**: Default case when no other conditions match +- **`str`**: Variable that stores the result + +## ๐ŸŽฏ Key Concepts Explained + +### **1. When Expression Syntax** + +```kotlin +val result = when (expression) { + value1 -> result1 + value2 -> result2 + value3 -> result3 + else -> defaultResult +} +``` + +**Components:** +- **`when (expression)`**: The value to test +- **`value -> result`**: Each case with its corresponding result +- **`else ->`**: Default case (optional but recommended) + +### **2. When vs If-Else Chains** + +#### **If-Else Chain (Verbose)** + +```kotlin +val grade = if (score>= 90) { + 'A' +} else if (score>= 80) { + 'B' +} else if (score>= 70) { + 'C' +} else if (score>= 60) { + 'D' +} else { + 'F' +} +``` + +#### **When Expression (Clean)** + +```kotlin +val grade = when { + score>= 90 -> 'A' + score>= 80 -> 'B' + score>= 70 -> 'C' + score>= 60 -> 'D' + else -> 'F' +} +``` + +**Advantages of when:** +- โœ… More readable +- โœ… Less repetitive +- โœ… Better performance +- โœ… Easier to maintain + +### **3. Different Types of When Expressions** + +#### **Value-Based When** + +```kotlin +val day = 3 +val dayName = when (day) { + 1 -> "Monday" + 2 -> "Tuesday" + 3 -> "Wednesday" + 4 -> "Thursday" + 5 -> "Friday" + 6 -> "Saturday" + 7 -> "Sunday" + else -> "Invalid day" +} +``` + +#### **Expression-Based When** + +```kotlin +val score = 85 +val grade = when { + score>= 90 -> 'A' + score>= 80 -> 'B' + score>= 70 -> 'C' + score>= 60 -> 'D' + else -> 'F' +} +``` + +#### **Range-Based When** + +```kotlin +val age = 25 +val category = when (age) { + in 0..12 -> "Child" + in 13..19 -> "Teenager" + in 20..64 -> "Adult" + else -> "Senior" +} +``` + +## ๐Ÿงช Hands-On Exercises + +### **Exercise 1: Basic When Expression** +Create a program that converts numbers to day names: + +```kotlin +fun main(args: Array) { + // TODO: Add your solution here +} +``` + +**Solution:** +```kotlin +fun main(args: Array) { + val dayNumber = 3 + + val dayName = when (dayNumber) { + 1 -> "Monday" + 2 -> "Tuesday" + 3 -> "Wednesday" + 4 -> "Thursday" + 5 -> "Friday" + 6 -> "Saturday" + 7 -> "Sunday" + else -> "Invalid day number" + } + + println("Day $dayNumber is $dayName") +} +``` + +### **Exercise 2: When Expression with Ranges** +Create a program that categorizes ages using ranges: + +```kotlin +fun main(args: Array) { + // TODO: Add your solution here +} +``` + +**Solution:** +```kotlin +fun main(args: Array) { + val age = 17 + + val category = when (age) { + in 0..2 -> "Infant" + in 3..5 -> "Toddler" + in 6..12 -> "Child" + in 13..19 -> "Teenager" + in 20..39 -> "Young Adult" + in 40..59 -> "Middle Aged" + in 60..79 -> "Senior" + else -> "Elderly" + } + + println("Age $age belongs to category: $category") +} +``` + +### **Exercise 3: Complex When Expression** +Create a program that determines shipping cost based on weight and destination: + +```kotlin +fun main(args: Array) { + // TODO: Add your solution here +} +``` + +**Solution:** +```kotlin +fun main(args: Array) { + val weight = 2.5 // in kg + val destination = "International" + + val shippingCost = when { + weight <= 1.0 -> { + when (destination) { + "Local" -> 5.0 + "National" -> 8.0 + "International" -> 15.0 + else -> 0.0 + } + } + weight <= 5.0 -> { + when (destination) { + "Local" -> 10.0 + "National" -> 15.0 + "International" -> 25.0 + else -> 0.0 + } + } + weight <= 10.0 -> { + when (destination) { + "Local" -> 15.0 + "National" -> 25.0 + "International" -> 40.0 + else -> 0.0 + } + } + else -> { + when (destination) { + "Local" -> 20.0 + "National" -> 35.0 + "International" -> 60.0 + else -> 0.0 + } + } + } + + println("Weight: ${weight}kg") + println("Destination: $destination") + println("Shipping Cost: $${shippingCost}") +} +``` + +### **Exercise 4: When Expression with Multiple Values** +Create a program that handles multiple conditions elegantly: + +```kotlin +fun main(args: Array) { + // TODO: Add your solution here +} +``` + +**Solution:** +```kotlin +fun main(args: Array) { + val temperature = 25 + val humidity = 70 + val isRaining = false + + val activity = when { + temperature> 30 && humidity> 80 -> "Stay indoors with AC" + temperature> 25 && !isRaining -> "Go for a walk" + temperature in 15..25 && !isRaining -> "Perfect for outdoor activities" + temperature < 15 -> "Bundle up and stay warm" + isRaining -> "Indoor activities recommended" + else -> "Moderate outdoor activities" + } + + println("Temperature: ${temperature}ยฐC") + println("Humidity: ${humidity}%") + println("Raining: $isRaining") + println("Recommended activity: $activity") +} +``` + +## ๐Ÿ” Advanced When Expression Features + +### **1. Multiple Values in One Case** + +```kotlin +val day = 3 +val dayType = when (day) { + 1, 2, 3, 4, 5 -> "Weekday" + 6, 7 -> "Weekend" + else -> "Invalid day" +} +``` + +### **2. Using When with Custom Objects** + +```kotlin +sealed class Shape +data class Circle(val radius: Double) : Shape() +data class Rectangle(val width: Double, val height: Double) : Shape() +data class Triangle(val base: Double, val height: Double) : Shape() + +fun calculateArea(shape: Shape): Double = when (shape) { + is Circle -> Math.PI * shape.radius * shape.radius + is Rectangle -> shape.width * shape.height + is Triangle -> 0.5 * shape.base * shape.height +} +``` + +### **3. When Expression as Statement** + +```kotlin +val score = 85 +when { + score>= 90 -> println("Excellent!") + score>= 80 -> println("Good job!") + score>= 70 -> println("Not bad!") + else -> println("Keep trying!") +} +``` + +## ๐Ÿ” When Expression Best Practices + +### **โœ… Do's** + +1. **Always include an else branch** (unless exhaustive): + ```kotlin + val result = when (value) { + 1 -> "One" + 2 -> "Two" + else -> "Unknown" // โœ… Safe fallback + } + ``` + +2. **Use ranges for numeric comparisons**: + ```kotlin + val category = when (age) { + in 0..12 -> "Child" // โœ… Clean and readable + in 13..19 -> "Teenager" + else -> "Adult" + } + ``` + +3. **Group related cases together**: + ```kotlin + val season = when (month) { + 12, 1, 2 -> "Winter" // โœ… Logical grouping + 3, 4, 5 -> "Spring" + 6, 7, 8 -> "Summer" + 9, 10, 11 -> "Fall" + else -> "Invalid month" + } + ``` + +### **โŒ Don'ts** + +1. **Don't forget the else branch**: + ```kotlin + val result = when (value) { + 1 -> "One" + 2 -> "Two" + // โŒ Missing else branch + } + ``` + +2. **Don't use when for simple if-else**: + ```kotlin + // โŒ Overkill for simple condition + val result = when (a> b) { + true -> a + false -> b + } + + // โœ… Better approach + val result = if (a> b) a else b + ``` + +## ๐Ÿšจ Common Mistakes to Avoid + +1. **Missing else branch**: + ```kotlin + val result = when (x) { + 1 -> "One" + 2 -> "Two" + // โŒ Compile error: missing else branch + } + ``` + +2. **Using wrong syntax**: + ```kotlin + when (x) { + 1: "One" // โŒ Wrong syntax + 1 -> "One" // โœ… Correct syntax + } + ``` + +3. **Forgetting to handle all cases**: + ```kotlin + val result = when (x) { + 1 -> "One" + 2 -> "Two" + // โŒ What if x is 3? + } + ``` + +## ๐ŸŽฏ What's Next? + +Excellent! You've mastered when expressions in Kotlin. Now you're ready to: + +1. **Learn loops** โ†’ [For Loops](03-for-loops.md) +2. **Understand while loops** โ†’ [While Loops](04-while-loops.md) +3. **Work with functions** โ†’ [Functions Basics](../functions/01-functions-basics.md) +4. **Explore collections** โ†’ [Arrays](../collections/01-arrays.md) + +## ๐Ÿ“š Additional Resources + +- [Kotlin When Expression](https://kotlinlang.org/docs/control-flow.html#when-expression) +- [Control Flow](https://kotlinlang.org/docs/control-flow.html) +- [Sealed Classes](https://kotlinlang.org/docs/sealed-classes.html) + +## ๐Ÿ† Summary + +- โœ… You can write basic when expressions +- โœ… You can use when expressions to return values +- โœ… You can handle multiple conditions elegantly +- โœ… You can use when expressions with different data types +- โœ… You understand when expressions vs if-else chains +- โœ… You're ready to write clean, readable conditional code! + +**Keep practicing and happy coding! ๐ŸŽ‰** diff --git a/docs/control-flow/03-for-loops.md b/docs/control-flow/03-for-loops.md new file mode 100644 index 0000000..8617e77 --- /dev/null +++ b/docs/control-flow/03-for-loops.md @@ -0,0 +1,490 @@ +# ๐Ÿ”„ For Loops in Kotlin + +Welcome to the world of loops! For loops are essential for repeating code and iterating over collections. They're one of the most powerful tools for automating repetitive tasks and processing data efficiently. + +## ๐Ÿ“‹ Learning Objectives + +By the end of this lesson, you will be able to: +- โœ… Write basic for loops with ranges +- โœ… Iterate over collections and arrays +- โœ… Use different loop directions (forward, backward, step) +- โœ… Understand loop control with break and continue +- โœ… Create efficient and readable loops + +## ๐Ÿ” What You'll Learn + +- **Basic for loops** - Simple iteration with ranges +- **Range-based loops** - Using `..`, `downTo`, and `step` +- **Collection iteration** - Looping through lists, arrays, and maps +- **Loop control** - Using break and continue statements +- **Best practices** - Writing efficient and readable loops + +## ๐Ÿ“ Prerequisites + +- Basic understanding of control flow +- Completed [When Expressions](02-when-expressions.md) lesson + +## ๐Ÿ’ป The Code + +Let's examine the for loop example: + +```kotlin +fun main(args: Array) { + for (i in 1..10) { + if (i % 2 == 0) { + println(i) + } + } + + println() + + for (i in 10 downTo 0) { + if (i % 2 == 0) { + println(i) + } + } +} +``` + +**๐Ÿ“ File:** [12_for_loop.kt](src/12_for_loop.kt) + +## ๐Ÿ” Code Breakdown + +### **First For Loop** + +```kotlin +for (i in 1..10) { + if (i % 2 == 0) { + println(i) + } +} +``` + +- **`for (i in 1..10)`**: Loop from 1 to 10 (inclusive) +- **`i`**: Loop variable that takes each value +- **`1..10`**: Range from 1 to 10 +- **`if (i % 2 == 0)`**: Check if number is even +- **`println(i)`**: Print even numbers only + +### **Second For Loop** + +```kotlin +for (i in 10 downTo 0) { + if (i % 2 == 0) { + println(i) + } +} +``` + +- **`for (i in 10 downTo 0)`**: Loop from 10 down to 0 +- **`downTo`**: Reverse direction +- **Same logic**: Print even numbers in descending order + +## ๐ŸŽฏ Key Concepts Explained + +### **1. Basic For Loop Syntax** + +```kotlin +for (variable in range) { + // Code to execute for each iteration +} +``` + +**Components:** +- **`for`**: Loop keyword +- **`variable`**: Loop variable (can be any name) +- **`in`**: Membership operator +- **`range`**: What to iterate over +- **`{ }`**: Loop body + +### **2. Range Types** + +#### **Inclusive Range (`..`)** + +```kotlin +for (i in 1..5) { + println(i) // Prints: 1, 2, 3, 4, 5 +} +``` + +- **`1..5`**: Includes both 1 and 5 +- **Forward direction**: 1 โ†’ 2 โ†’ 3 โ†’ 4 โ†’ 5 + +#### **Exclusive Range (`until`)** + +```kotlin +for (i in 1 until 5) { + println(i) // Prints: 1, 2, 3, 4 (excludes 5) +} +``` + +- **`1 until 5`**: Excludes the upper bound +- **Useful for**: Array indices, avoiding off-by-one errors + +#### **Reverse Range (`downTo`)** + +```kotlin +for (i in 5 downTo 1) { + println(i) // Prints: 5, 4, 3, 2, 1 +} +``` + +- **`5 downTo 1`**: Counts backward +- **Useful for**: Reverse iteration, countdowns + +#### **Step Range** + +```kotlin +for (i in 0..10 step 2) { + println(i) // Prints: 0, 2, 4, 6, 8, 10 +} + +for (i in 10 downTo 0 step 3) { + println(i) // Prints: 10, 7, 4, 1 +} +``` + +- **`step 2`**: Skip every other number +- **`step 3`**: Skip by 3s + +### **3. Collection Iteration** + +#### **Array/List Iteration** + +```kotlin +val numbers = arrayOf(1, 2, 3, 4, 5) + +// By index +for (i in numbers.indices) { + println("Index $i: ${numbers[i]}") +} + +// By value +for (number in numbers) { + println("Number: $number") +} + +// By index and value +for ((index, value) in numbers.withIndex()) { + println("Index $index: $value") +} +``` + +#### **String Iteration** + +```kotlin +val text = "Hello" + +// By character +for (char in text) { + println("Character: $char") +} + +// By index +for (i in text.indices) { + println("Index $i: ${text[i]}") +} +``` + +#### **Map Iteration** + +```kotlin +val map = mapOf("a" to 1, "b" to 2, "c" to 3) + +// By key-value pairs +for ((key, value) in map) { + println("$key -> $value") +} + +// By keys only +for (key in map.keys) { + println("Key: $key") +} + +// By values only +for (value in map.values) { + println("Value: $value") +} +``` + +## ๐Ÿงช Hands-On Exercises + +### **Exercise 1: Basic Range Loops** +Create a program that demonstrates different range types: + +```kotlin +fun main(args: Array) { + // TODO: Add your solution here +} +``` + +**Solution:** +```kotlin +fun main(args: Array) { + println("Forward range (1..5):") + for (i in 1..5) { + print("$i ") + } + + println("\n\nExclusive range (1 until 5):") + for (i in 1 until 5) { + print("$i ") + } + + println("\n\nReverse range (5 downTo 1):") + for (i in 5 downTo 1) { + print("$i ") + } + + println("\n\nStep range (0..10 step 2):") + for (i in 0..10 step 2) { + print("$i ") + } +} +``` + +### **Exercise 2: Number Pattern Loops** +Create a program that prints different number patterns: + +```kotlin +fun main(args: Array) { + // TODO: Add your solution here +} +``` + +**Solution:** +```kotlin +fun main(args: Array) { + println("Multiplication table for 5:") + for (i in 1..10) { + println("5 ร—ใฐใค $i = ${5 * i}") + } + + println("\nEven numbers from 2 to 20:") + for (i in 2..20 step 2) { + print("$i ") + } + + println("\n\nCountdown from 10:") + for (i in 10 downTo 1) { + print("$i ") + if (i == 1) println("Blast off!") else print(", ") + } +} +``` + +### **Exercise 3: Collection Iteration** +Create a program that demonstrates different ways to iterate over collections: + +```kotlin +fun main(args: Array) { + // TODO: Add your solution here +} +``` + +**Solution:** +```kotlin +fun main(args: Array) { + val fruits = listOf("Apple", "Banana", "Orange", "Grape", "Mango") + + println("Fruits by index:") + for (i in fruits.indices) { + println("${i + 1}. ${fruits[i]}") + } + + println("\nFruits by value:") + for (fruit in fruits) { + println("โ€ข $fruit") + } + + println("\nFruits with index:") + for ((index, fruit) in fruits.withIndex()) { + println("${index + 1}. $fruit") + } + + println("\nFruits in reverse:") + for (i in fruits.indices.reversed()) { + println("${i + 1}. ${fruits[i]}") + } +} +``` + +### **Exercise 4: Advanced Loop Patterns** +Create a program that demonstrates complex loop patterns: + +```kotlin +fun main(args: Array) { + // TODO: Add your solution here +} +``` + +**Solution:** +```kotlin +fun main(args: Array) { + println("Pyramid pattern:") + for (i in 1..5) { + // Print spaces + for (j in 1..(5 - i)) { + print(" ") + } + // Print stars + for (j in 1..(2 * i - 1)) { + print("*") + } + println() + } + + println("\nNumber triangle:") + for (i in 1..5) { + for (j in 1..i) { + print("$j ") + } + println() + } + + println("\nAlternating pattern:") + for (i in 1..10) { + val symbol = if (i % 2 == 0) "โ˜…" else "โ˜†" + print("$symbol ") + } +} +``` + +## ๐Ÿ” Advanced For Loop Features + +### **1. Nested Loops** + +```kotlin +// Multiplication table +for (i in 1..5) { + for (j in 1..5) { + print("${i * j}\t") + } + println() +} +``` + +### **2. Loop with Conditions** + +```kotlin +for (i in 1..20) { + when { + i % 3 == 0 && i % 5 == 0 -> println("FizzBuzz") + i % 3 == 0 -> println("Fizz") + i % 5 == 0 -> println("Buzz") + else -> println(i) + } +} +``` + +### **3. Custom Range Functions** + +```kotlin +// Custom step function +fun Int.toward(to: Int): IntProgression { + val step = if (this> to) -1 else 1 + return IntProgression.fromClosedRange(this, to, step) +} + +// Usage +for (i in 5.toward(1)) { + println(i) // Prints: 5, 4, 3, 2, 1 +} +``` + +## ๐Ÿ” Loop Control Statements + +### **1. Break Statement** + +```kotlin +for (i in 1..10) { + if (i == 5) { + break // Exit loop when i equals 5 + } + println(i) +} +// Output: 1, 2, 3, 4 +``` + +### **2. Continue Statement** + +```kotlin +for (i in 1..10) { + if (i % 2 == 0) { + continue // Skip even numbers + } + println(i) +} +// Output: 1, 3, 5, 7, 9 +``` + +### **3. Labeled Loops** + +```kotlin +outer@ for (i in 1..3) { + for (j in 1..3) { + if (i == 2 && j == 2) { + break@outer // Break outer loop + } + println("i=$i, j=$j") + } +} +``` + +## ๐Ÿšจ Common Mistakes to Avoid + +1. **Off-by-one errors**: + ```kotlin + val array = arrayOf(1, 2, 3, 4, 5) + for (i in 0..array.size) { // โŒ Should be array.size - 1 + println(array[i]) + } + + for (i in 0 until array.size) { // โœ… Correct + println(array[i]) + } + ``` + +2. **Infinite loops**: + ```kotlin + for (i in 1..10) { + i++ // โŒ Cannot modify loop variable + } + ``` + +3. **Wrong range direction**: + ```kotlin + for (i in 10..1) { // โŒ Empty range (10 is not <= 1) + println(i) + } + + for (i in 10 downTo 1) { // โœ… Correct + println(i) + } + ``` + +## ๐ŸŽฏ What's Next? + +Excellent! You've mastered for loops in Kotlin. Now you're ready to: + +1. **Learn while loops** โ†’ [While Loops](04-while-loops.md) +2. **Understand break and continue** โ†’ [Break and Continue](05-break-continue.md) +3. **Work with functions** โ†’ [Functions Basics](../functions/01-functions-basics.md) +4. **Explore collections** โ†’ [Arrays](../collections/01-arrays.md) + +## ๐Ÿ“š Additional Resources + +- [Kotlin For Loops](https://kotlinlang.org/docs/control-flow.html#for-loops) +- [Ranges](https://kotlinlang.org/docs/ranges.html) +- [Collections](https://kotlinlang.org/docs/collections-overview.html) + +## ๐Ÿ† Summary + +- โœ… You can write basic for loops with ranges +- โœ… You can iterate over collections and arrays +- โœ… You can use different loop directions (forward, backward, step) +- โœ… You understand loop control with break and continue +- โœ… You can create efficient and readable loops +- โœ… You're ready to automate repetitive tasks! + +**Keep practicing and happy coding! ๐ŸŽ‰** From d075bec9287f69ff27e2d333ddeb84c05e23ef74 Mon Sep 17 00:00:00 2001 From: gpl-gowthamchand Date: 2025ๅนด8ๆœˆ24ๆ—ฅ 19:02:23 +0530 Subject: [PATCH 03/23] feat: Complete control flow documentation - Add While Loops guide with do-while examples - Add Break and Continue guide with labeled statements - Include comprehensive exercises and best practices - Cover infinite loop prevention and common pitfalls - Provide clear learning paths for loop control --- docs/control-flow/04-while-loops.md | 529 ++++++++++++++++++++++ docs/control-flow/05-break-continue.md | 581 +++++++++++++++++++++++++ 2 files changed, 1110 insertions(+) create mode 100644 docs/control-flow/04-while-loops.md create mode 100644 docs/control-flow/05-break-continue.md diff --git a/docs/control-flow/04-while-loops.md b/docs/control-flow/04-while-loops.md new file mode 100644 index 0000000..89921e2 --- /dev/null +++ b/docs/control-flow/04-while-loops.md @@ -0,0 +1,529 @@ +# ๐Ÿ” While Loops in Kotlin + +Welcome to the world of while loops! While loops are perfect for situations where you don't know in advance how many times you need to repeat a block of code. They continue executing as long as a condition remains true, making them ideal for user input validation, data processing, and game loops. + +## ๐Ÿ“‹ Learning Objectives + +By the end of this lesson, you will be able to: +- โœ… Write basic while loops +- โœ… Use do-while loops effectively +- โœ… Understand when to use while vs for loops +- โœ… Control loop execution with break and continue +- โœ… Avoid infinite loops and common pitfalls + +## ๐Ÿ” What You'll Learn + +- **Basic while loops** - Simple conditional repetition +- **Do-while loops** - Execute first, then check condition +- **Loop control** - Using break and continue statements +- **Infinite loop prevention** - Best practices and safety +- **Real-world applications** - Practical use cases + +## ๐Ÿ“ Prerequisites + +- Basic understanding of for loops +- Completed [For Loops](03-for-loops.md) lesson + +## ๐Ÿ’ป The Code + +Let's examine the while loop example: + +```kotlin +fun main(args: Array) { + var i = 1 + + while (i <= 5) { + println("Count: $i") + i++ + } + + println("Loop finished!") +} +``` + +**๐Ÿ“ File:** [13_while_loop.kt](src/13_while_loop.kt) + +## ๐Ÿ” Code Breakdown + +### **Variable Initialization** + +```kotlin +var i = 1 +``` + +- **`var i = 1`**: Initialize loop counter +- **`var`** makes it mutable (can be changed) + +### **While Loop** + +```kotlin +while (i <= 5) { + println("Count: $i") + i++ +} +``` + +- **`while (i <= 5)`**: Condition to check before each iteration +- **`{ }`**: Loop body containing statements +- **`println("Count: $i")`**: Print current count +- **`i++`**: Increment counter (crucial to avoid infinite loop) + +## ๐ŸŽฏ Key Concepts Explained + +### **1. While Loop Syntax** + +```kotlin +while (condition) { + // Code to execute while condition is true +} +``` + +**Components:** +- **`while`**: Loop keyword +- **`(condition)`**: Boolean expression that controls the loop +- **`{ }`**: Loop body containing statements + +**Execution Flow:** +1. **Check condition** - Is it true? +2. **If true** - Execute loop body +3. **Repeat** - Go back to step 1 +4. **If false** - Exit loop + +### **2. Do-While Loop Syntax** + +```kotlin +do { + // Code to execute at least once +} while (condition) +``` + +**Key Difference:** +- **While loop**: Check condition first, then execute +- **Do-while loop**: Execute first, then check condition +- **Guarantee**: Do-while always executes at least once + +### **3. When to Use Each Loop Type** + +#### **Use While Loop When:** +- You don't know the number of iterations in advance +- Condition depends on user input +- Processing data until a condition is met +- Game loops or event-driven programming + +#### **Use For Loop When:** +- You know the exact number of iterations +- Iterating over collections or ranges +- Simple counting scenarios + +#### **Use Do-While Loop When:** +- You need to execute code at least once +- Menu-driven programs +- Input validation scenarios + +## ๐Ÿงช Hands-On Exercises + +### **Exercise 1: Basic While Loop** +Create a program that counts down from 10 to 1: + +```kotlin +fun main(args: Array) { + // TODO: Add your solution here +} +``` + +**Solution:** +```kotlin +fun main(args: Array) { + var count = 10 + + while (count>= 1) { + println("Countdown: $count") + count-- + } + + println("Blast off!") +} +``` + +### **Exercise 2: User Input Validation** +Create a program that asks for a positive number using while loop: + +```kotlin +fun main(args: Array) { + // TODO: Add your solution here +} +``` + +**Solution:** +```kotlin +fun main(args: Array) { + var input: Int + var attempts = 0 + + do { + print("Enter a positive number: ") + input = readLine()?.toIntOrNull() ?: 0 + attempts++ + + if (input <= 0) { + println("Invalid input. Please try again.") + } + } while (input <= 0) + + println("Valid input received: $input") + println("Number of attempts: $attempts") +} +``` + +### **Exercise 3: Data Processing Loop** +Create a program that processes numbers until a sentinel value is encountered: + +```kotlin +fun main(args: Array) { + // TODO: Add your solution here +} +``` + +**Solution:** +```kotlin +fun main(args: Array) { + var sum = 0 + var count = 0 + var number: Int + + println("Enter numbers (enter -1 to stop):") + + while (true) { + print("Enter number: ") + number = readLine()?.toIntOrNull() ?: 0 + + if (number == -1) { + break // Exit loop when sentinel value is entered + } + + sum += number + count++ + } + + if (count> 0) { + val average = sum.toDouble() / count + println("Sum: $sum") + println("Count: $count") + println("Average: $average") + } else { + println("No numbers entered.") + } +} +``` + +### **Exercise 4: Menu-Driven Program** +Create a simple calculator using do-while loop: + +```kotlin +fun main(args: Array) { + // TODO: Add your solution here +} +``` + +**Solution:** +```kotlin +fun main(args: Array) { + var choice: Int + var continueProgram = true + + do { + println("\n=== Simple Calculator ===") + println("1. Addition") + println("2. Subtraction") + println("3. Multiplication") + println("4. Division") + println("5. Exit") + print("Enter your choice (1-5): ") + + choice = readLine()?.toIntOrNull() ?: 0 + + when (choice) { + 1 -> { + print("Enter first number: ") + val a = readLine()?.toDoubleOrNull() ?: 0.0 + print("Enter second number: ") + val b = readLine()?.toDoubleOrNull() ?: 0.0 + println("Result: ${a + b}") + } + 2 -> { + print("Enter first number: ") + val a = readLine()?.toDoubleOrNull() ?: 0.0 + print("Enter second number: ") + val b = readLine()?.toDoubleOrNull() ?: 0.0 + println("Result: ${a - b}") + } + 3 -> { + print("Enter first number: ") + val a = readLine()?.toDoubleOrNull() ?: 0.0 + print("Enter second number: ") + val b = readLine()?.toDoubleOrNull() ?: 0.0 + println("Result: ${a * b}") + } + 4 -> { + print("Enter first number: ") + val a = readLine()?.toDoubleOrNull() ?: 0.0 + print("Enter second number: ") + val b = readLine()?.toDoubleOrNull() ?: 0.0 + if (b != 0.0) { + println("Result: ${a / b}") + } else { + println("Error: Division by zero!") + } + } + 5 -> { + println("Goodbye!") + continueProgram = false + } + else -> println("Invalid choice. Please try again.") + } + } while (continueProgram) +} +``` + +## ๐Ÿ” Advanced While Loop Concepts + +### **1. Nested While Loops** + +```kotlin +var i = 1 +while (i <= 3) { + var j = 1 + while (j <= i) { + print("*") + j++ + } + println() + i++ +} +// Output: +// * +// ** +// *** +``` + +### **2. While Loop with Collections** + +```kotlin +val numbers = mutableListOf(1, 2, 3, 4, 5) +var index = 0 + +while (index < numbers.size) { + if (numbers[index] % 2 == 0) { + numbers[index] *= 2 // Double even numbers + } + index++ +} + +println(numbers) // [1, 4, 3, 8, 5] +``` + +### **3. While Loop with Custom Conditions** + +```kotlin +var password = "" +var attempts = 0 +val correctPassword = "secret123" + +while (password != correctPassword && attempts < 3) { + print("Enter password (attempt ${attempts + 1}/3): ") + password = readLine() ?: "" + attempts++ + + if (password != correctPassword) { + println("Incorrect password. Try again.") + } +} + +if (password == correctPassword) { + println("Access granted!") +} else { + println("Access denied. Too many attempts.") +} +``` + +## ๐Ÿ” Loop Control in While Loops + +### **1. Break Statement** + +```kotlin +var i = 1 +while (true) { + if (i> 10) { + break // Exit loop when i exceeds 10 + } + println(i) + i++ +} +``` + +### **2. Continue Statement** + +```kotlin +var i = 1 +while (i <= 10) { + if (i % 2 == 0) { + i++ + continue // Skip even numbers + } + println("Odd number: $i") + i++ +} +``` + +### **3. Labeled Loops** + +```kotlin +outer@ while (true) { + var i = 1 + while (i <= 5) { + if (i == 3) { + break@outer // Break outer loop + } + println("i = $i") + i++ + } +} +``` + +## ๐Ÿšจ Common Mistakes to Avoid + +### **1. Infinite Loops** + +```kotlin +// โŒ Infinite loop - missing increment +var i = 1 +while (i <= 5) { + println(i) + // i++ is missing! +} + +// โœ… Correct - with increment +var i = 1 +while (i <= 5) { + println(i) + i++ +} +``` + +### **2. Off-by-One Errors** + +```kotlin +// โŒ Wrong condition - will miss last element +var i = 0 +while (i < array.size - 1) { + println(array[i]) + i++ +} + +// โœ… Correct condition +var i = 0 +while (i < array.size) { + println(array[i]) + i++ +} +``` + +### **3. Using Wrong Loop Type** + +```kotlin +// โŒ While loop for simple counting +var i = 1 +while (i <= 10) { + println(i) + i++ +} + +// โœ… For loop is better for counting +for (i in 1..10) { + println(i) +} +``` + +## ๐Ÿ” Best Practices + +### **โœ… Do's** + +1. **Always ensure loop termination**: + ```kotlin + var i = 1 + while (i <= 10) { // โœ… Clear termination condition + println(i) + i++ + } + ``` + +2. **Use meaningful variable names**: + ```kotlin + var count = 0 + while (count < maxAttempts) { // โœ… Clear and descriptive + // Loop body + count++ + } + ``` + +3. **Initialize variables before the loop**: + ```kotlin + var sum = 0 // โœ… Initialize before loop + var i = 1 + while (i <= 10) { + sum += i + i++ + } + ``` + +### **โŒ Don'ts** + +1. **Don't forget to update loop variables**: + ```kotlin + var i = 1 + while (i <= 5) { + println(i) + // โŒ Missing i++ - infinite loop! + } + ``` + +2. **Don't use while loops for simple counting**: + ```kotlin + // โŒ Overkill for simple iteration + var i = 0 + while (i < 10) { + println(i) + i++ + } + + // โœ… Use for loop instead + for (i in 0 until 10) { + println(i) + } + ``` + +## ๐ŸŽฏ What's Next? + +Excellent! You've mastered while loops in Kotlin. Now you're ready to: + +1. **Learn break and continue** โ†’ [Break and Continue](05-break-continue.md) +2. **Work with functions** โ†’ [Functions Basics](../functions/01-functions-basics.md) +3. **Explore collections** โ†’ [Arrays](../collections/01-arrays.md) +4. **Understand OOP** โ†’ [Classes and Constructors](../oop/01-classes-constructors.md) + +## ๐Ÿ“š Additional Resources + +- [Kotlin While Loops](https://kotlinlang.org/docs/control-flow.html#while-loops) +- [Control Flow](https://kotlinlang.org/docs/control-flow.html) +- [Loop Control](https://kotlinlang.org/docs/returns.html) + +## ๐Ÿ† Summary + +- โœ… You can write basic while loops +- โœ… You can use do-while loops effectively +- โœ… You understand when to use while vs for loops +- โœ… You can control loop execution with break and continue +- โœ… You know how to avoid infinite loops and common pitfalls +- โœ… You're ready to handle dynamic repetition scenarios! + +**Keep practicing and happy coding! ๐ŸŽ‰** diff --git a/docs/control-flow/05-break-continue.md b/docs/control-flow/05-break-continue.md new file mode 100644 index 0000000..14c7732 --- /dev/null +++ b/docs/control-flow/05-break-continue.md @@ -0,0 +1,581 @@ +# โน๏ธ Break and Continue in Kotlin + +Welcome to the world of loop control! Break and continue statements give you fine-grained control over how your loops execute. They allow you to skip iterations, exit loops early, and create more efficient and readable code. + +## ๐Ÿ“‹ Learning Objectives + +By the end of this lesson, you will be able to: +- โœ… Use break statements to exit loops early +- โœ… Use continue statements to skip iterations +- โœ… Understand labeled break and continue +- โœ… Choose the right control statement for your needs +- โœ… Write more efficient and readable loops + +## ๐Ÿ” What You'll Learn + +- **Break statements** - Exiting loops prematurely +- **Continue statements** - Skipping specific iterations +- **Labeled statements** - Controlling nested loops +- **Best practices** - When and how to use each statement +- **Real-world applications** - Practical use cases + +## ๐Ÿ“ Prerequisites + +- Basic understanding of loops (for, while) +- Completed [While Loops](04-while-loops.md) lesson + +## ๐Ÿ’ป The Code + +Let's examine the break and continue examples: + +```kotlin +fun main(args: Array) { + for (i in 1..10) { + if (i == 5) { + break // Exit loop when i equals 5 + } + println(i) + } + + println("Loop finished!") +} +``` + +**๐Ÿ“ File:** [15_break_keyword.kt](src/15_break_keyword.kt) + +## ๐Ÿ” Code Breakdown + +### **Break Statement Example** + +```kotlin +for (i in 1..10) { + if (i == 5) { + break // Exit loop when i equals 5 + } + println(i) +} +``` + +- **`for (i in 1..10)`**: Loop from 1 to 10 +- **`if (i == 5)`**: Condition to check +- **`break`**: Exit the loop immediately +- **Output**: Only prints 1, 2, 3, 4 (loop exits at 5) + +## ๐ŸŽฏ Key Concepts Explained + +### **1. Break Statement** + +#### **Basic Break Syntax** + +```kotlin +for (i in 1..10) { + if (condition) { + break // Exit the loop immediately + } + // This code won't execute after break +} +``` + +**What Break Does:** +- **Immediately exits** the innermost loop +- **Skips remaining iterations** +- **Continues execution** after the loop +- **Useful for**: Early termination, error conditions, found items + +#### **Break in Different Loop Types** + +```kotlin +// For loop +for (i in 1..10) { + if (i == 5) break + println(i) +} + +// While loop +var i = 1 +while (i <= 10) { + if (i == 5) break + println(i) + i++ +} + +// Do-while loop +var j = 1 +do { + if (j == 5) break + println(j) + j++ +} while (j <= 10) +``` + +### **2. Continue Statement** + +#### **Basic Continue Syntax** + +```kotlin +for (i in 1..10) { + if (condition) { + continue // Skip to next iteration + } + // This code executes for non-skipped iterations +} +``` + +**What Continue Does:** +- **Skips current iteration** +- **Continues with next iteration** +- **Loop body continues** after continue +- **Useful for**: Skipping invalid data, filtering, conditional processing + +#### **Continue Examples** + +```kotlin +// Skip even numbers +for (i in 1..10) { + if (i % 2 == 0) { + continue // Skip even numbers + } + println("Odd: $i") +} + +// Skip specific values +for (i in 1..10) { + if (i == 3 || i == 7) { + continue // Skip 3 and 7 + } + println(i) +} +``` + +### **3. Labeled Break and Continue** + +#### **Labeled Break** + +```kotlin +outer@ for (i in 1..3) { + for (j in 1..3) { + if (i == 2 && j == 2) { + break@outer // Break outer loop + } + println("i=$i, j=$j") + } +} +``` + +**Output:** +``` +i=1, j=1 +i=1, j=2 +i=1, j=3 +i=2, j=1 +``` + +#### **Labeled Continue** + +```kotlin +outer@ for (i in 1..3) { + for (j in 1..3) { + if (i == 2 && j == 2) { + continue@outer // Continue outer loop + } + println("i=$i, j=$j") + } +} +``` + +**Output:** +``` +i=1, j=1 +i=1, j=2 +i=1, j=3 +i=2, j=1 +i=3, j=1 +i=3, j=2 +i=3, j=3 +``` + +## ๐Ÿงช Hands-On Exercises + +### **Exercise 1: Basic Break Statement** +Create a program that finds the first number divisible by both 3 and 5: + +```kotlin +fun main(args: Array) { + // TODO: Add your solution here +} +``` + +**Solution:** +```kotlin +fun main(args: Array) { + for (i in 1..100) { + if (i % 3 == 0 && i % 5 == 0) { + println("First number divisible by both 3 and 5: $i") + break // Exit loop after finding first match + } + } +} +``` + +### **Exercise 2: Continue Statement** +Create a program that prints all numbers from 1 to 20, except multiples of 3: + +```kotlin +fun main(args: Array) { + // TODO: Add your solution here +} +``` + +**Solution:** +```kotlin +fun main(args: Array) { + for (i in 1..20) { + if (i % 3 == 0) { + continue // Skip multiples of 3 + } + print("$i ") + } + println() +} +``` + +### **Exercise 3: Labeled Break** +Create a program that demonstrates labeled break with nested loops: + +```kotlin +fun main(args: Array) { + // TODO: Add your solution here +} +``` + +**Solution:** +```kotlin +fun main(args: Array) { + val targetSum = 15 + + outer@ for (i in 1..5) { + for (j in 1..5) { + for (k in 1..5) { + val sum = i + j + k + if (sum == targetSum) { + println("Found combination: $i + $j + $k = $targetSum") + break@outer // Exit all loops + } + } + } + } +} +``` + +### **Exercise 4: Complex Loop Control** +Create a program that processes a list of numbers with multiple control statements: + +```kotlin +fun main(args: Array) { + // TODO: Add your solution here +} +``` + +**Solution:** +```kotlin +fun main(args: Array) { + val numbers = listOf(1, 2, 3, 4, 5, 6, 7, 8, 9, 10) + var sum = 0 + var count = 0 + + for (number in numbers) { + // Skip negative numbers (though our list doesn't have any) + if (number < 0) { + continue + } + + // Stop if we've processed 5 numbers + if (count>= 5) { + break + } + + // Skip even numbers + if (number % 2 == 0) { + continue + } + + sum += number + count++ + println("Added $number, sum is now $sum") + } + + println("Final sum: $sum") + println("Numbers processed: $count") +} +``` + +## ๐Ÿ” Advanced Control Flow Patterns + +### **1. Break with When Expression** + +```kotlin +for (i in 1..10) { + val action = when { + i < 5 -> "Continue" + i == 5 -> "Break" + else -> "Process" + } + + when (action) { + "Continue" -> continue + "Break" -> break + "Process" -> println("Processing $i") + } +} +``` + +### **2. Continue with Complex Conditions** + +```kotlin +for (i in 1..20) { + // Skip numbers that are multiples of 2, 3, or 5 + if (i % 2 == 0 || i % 3 == 0 || i % 5 == 0) { + continue + } + + // Only process prime numbers (simplified) + if (i> 1) { + println("Prime number: $i") + } +} +``` + +### **3. Break in Data Processing** + +```kotlin +val data = listOf(1, 2, 3, -1, 4, 5) // -1 is sentinel value +var sum = 0 + +for (item in data) { + if (item == -1) { + break // Stop processing at sentinel value + } + sum += item +} + +println("Sum before sentinel: $sum") +``` + +## ๐Ÿ” When to Use Each Statement + +### **โœ… Use Break When:** + +1. **Searching for items**: + ```kotlin + for (item in list) { + if (item == target) { + println("Found: $item") + break // Exit after finding + } + } + ``` + +2. **Error conditions**: + ```kotlin + for (i in 1..10) { + if (i == 0) { + println("Error: Division by zero") + break // Exit on error + } + println(100 / i) + } + ``` + +3. **Performance optimization**: + ```kotlin + for (i in 1..1000) { + if (i * i> 100) { + break // Exit when condition met + } + println("$i squared is ${i * i}") + } + ``` + +### **โœ… Use Continue When:** + +1. **Filtering data**: + ```kotlin + for (number in numbers) { + if (number < 0) { + continue // Skip negative numbers + } + processPositiveNumber(number) + } + ``` + +2. **Skipping invalid input**: + ```kotlin + for (input in inputs) { + if (!isValid(input)) { + continue // Skip invalid input + } + processValidInput(input) + } + ``` + +3. **Conditional processing**: + ```kotlin + for (i in 1..10) { + if (i % 2 == 0) { + continue // Skip even numbers + } + processOddNumber(i) + } + ``` + +## ๐Ÿšจ Common Mistakes to Avoid + +### **1. Infinite Loops with Continue** + +```kotlin +// โŒ Infinite loop - i never increments +var i = 1 +while (i <= 10) { + if (i % 2 == 0) { + continue // Skips the rest of the loop body + } + println(i) + i++ // This line is never reached for even numbers! +} + +// โœ… Correct - increment before continue +var i = 1 +while (i <= 10) { + if (i % 2 == 0) { + i++ // Increment first + continue + } + println(i) + i++ +} +``` + +### **2. Unreachable Code After Break** + +```kotlin +// โŒ Unreachable code +for (i in 1..10) { + if (i == 5) { + break + println("This will never execute") // Unreachable + } + println(i) +} + +// โœ… Correct - no unreachable code +for (i in 1..10) { + if (i == 5) { + break + } + println(i) +} +``` + +### **3. Wrong Label Usage** + +```kotlin +// โŒ Label doesn't exist +for (i in 1..3) { + for (j in 1..3) { + if (i == 2) { + break@nonexistent // Compile error + } + } +} + +// โœ… Correct label usage +outer@ for (i in 1..3) { + for (j in 1..3) { + if (i == 2) { + break@outer // Valid label + } + } +} +``` + +## ๐Ÿ” Best Practices + +### **โœ… Do's** + +1. **Use descriptive labels**: + ```kotlin + searchLoop@ for (i in 1..100) { + if (found) break@searchLoop + } + ``` + +2. **Keep break/continue conditions simple**: + ```kotlin + // โœ… Simple and clear + if (item == target) break + + // โŒ Complex condition + if (item == target && !isProcessed && isValid) break + ``` + +3. **Document complex control flow**: + ```kotlin + // Skip invalid entries and stop at sentinel + for (entry in entries) { + if (!isValid(entry)) continue // Skip invalid + if (entry == SENTINEL) break // Stop at sentinel + process(entry) + } + ``` + +### **โŒ Don'ts** + +1. **Don't overuse break/continue**: + ```kotlin + // โŒ Too many control statements + for (i in 1..10) { + if (i == 1) continue + if (i == 2) continue + if (i == 3) break + if (i == 4) continue + println(i) + } + ``` + +2. **Don't create deeply nested control flow**: + ```kotlin + // โŒ Hard to follow + outer@ for (i in 1..3) { + for (j in 1..3) { + for (k in 1..3) { + if (condition) break@outer + } + } + } + ``` + +## ๐ŸŽฏ What's Next? + +Excellent! You've mastered break and continue statements in Kotlin. Now you're ready to: + +1. **Work with functions** โ†’ [Functions Basics](../functions/01-functions-basics.md) +2. **Explore collections** โ†’ [Arrays](../collections/01-arrays.md) +3. **Understand OOP** โ†’ [Classes and Constructors](../oop/01-classes-constructors.md) +4. **Learn functional programming** โ†’ [Lambdas](../functional-programming/01-lambdas.md) + +## ๐Ÿ“š Additional Resources + +- [Kotlin Returns and Jumps](https://kotlinlang.org/docs/returns.html) +- [Control Flow](https://kotlinlang.org/docs/control-flow.html) +- [Loop Control](https://kotlinlang.org/docs/returns.html#break-and-continue-labels) + +## ๐Ÿ† Summary + +- โœ… You can use break statements to exit loops early +- โœ… You can use continue statements to skip iterations +- โœ… You understand labeled break and continue +- โœ… You can choose the right control statement for your needs +- โœ… You can write more efficient and readable loops +- โœ… You're ready to create sophisticated loop control logic! + +**Keep practicing and happy coding! ๐ŸŽ‰** From bb6bc4fac2d5db51c7aa047f7e99b3fe148215d2 Mon Sep 17 00:00:00 2001 From: gpl-gowthamchand Date: 2025ๅนด8ๆœˆ24ๆ—ฅ 19:23:43 +0530 Subject: [PATCH 04/23] feat: Add comprehensive functions documentation - Add Functions as Expressions guide with expression syntax - Add Named Parameters guide with parameter flexibility - Add Extension Functions guide with powerful class extensions - Include hands-on exercises for each topic - Provide clear learning paths and best practices --- docs/functions/02-functions-as-expressions.md | 520 ++++++++++++++ docs/functions/03-named-parameters.md | 645 ++++++++++++++++++ docs/functions/04-extension-functions.md | 609 +++++++++++++++++ 3 files changed, 1774 insertions(+) create mode 100644 docs/functions/02-functions-as-expressions.md create mode 100644 docs/functions/03-named-parameters.md create mode 100644 docs/functions/04-extension-functions.md diff --git a/docs/functions/02-functions-as-expressions.md b/docs/functions/02-functions-as-expressions.md new file mode 100644 index 0000000..a70c160 --- /dev/null +++ b/docs/functions/02-functions-as-expressions.md @@ -0,0 +1,520 @@ +# ๐ŸŽญ Functions as Expressions in Kotlin + +Welcome to the elegant world of functions as expressions! In Kotlin, functions can be written as single expressions, making your code more concise and readable. This powerful feature eliminates the need for explicit return statements and curly braces in many cases. + +## ๐Ÿ“‹ Learning Objectives + +By the end of this lesson, you will be able to: +- โœ… Write single-expression functions +- โœ… Understand when to use expression syntax +- โœ… Convert traditional functions to expression syntax +- โœ… Use expression functions with control flow +- โœ… Write more concise and readable code + +## ๐Ÿ” What You'll Learn + +- **Single-expression functions** - Concise function syntax +- **Expression vs statement functions** - When to use each +- **Control flow in expressions** - If, when, and loops +- **Type inference** - Automatic return type detection +- **Best practices** - Readability and maintainability + +## ๐Ÿ“ Prerequisites + +- Basic understanding of functions +- Completed [Functions Basics](01-functions-basics.md) lesson +- Knowledge of if expressions and control flow + +## ๐Ÿ’ป The Code + +Let's examine the functions as expressions example: + +```kotlin +fun max(a: Int, b: Int): Int = if (a> b) { + println("$a is greater") + a +} else { + println("$b is greater") + b +} +``` + +**๐Ÿ“ File:** [18_functions_as_expressions.kt](src/18_functions_as_expressions.kt) + +## ๐Ÿ” Code Breakdown + +### **Function Declaration** + +```kotlin +fun max(a: Int, b: Int): Int = if (a> b) { + println("$a is greater") + a +} else { + println("$b is greater") + b +} +``` + +- **`fun max(a: Int, b: Int): Int`**: Function signature with parameters and return type +- **`=`**: Expression syntax indicator (replaces `{ return }`) +- **`if (a> b) { ... } else { ... }`**: If expression that returns a value +- **`a` and `b`**: Return values from each branch + +### **Main Function Usage** + +```kotlin +fun main(args: Array) { + var largeValue = max(4, 6) + println("The greater number is $largeValue") +} +``` + +- **`max(4, 6)`**: Function call with arguments +- **`largeValue`**: Stores the returned result +- **Output**: "The greater number is 6" + +## ๐ŸŽฏ Key Concepts Explained + +### **1. Expression vs Statement Functions** + +#### **Traditional Function (Statement)** + +```kotlin +fun add(a: Int, b: Int): Int { + return a + b +} +``` + +**Characteristics:** +- Uses curly braces `{ }` +- Explicit `return` statement +- Can have multiple statements +- Good for complex logic + +#### **Expression Function** + +```kotlin +fun add(a: Int, b: Int): Int = a + b +``` + +**Characteristics:** +- Uses `=` instead of `{ }` +- No explicit `return` +- Single expression +- More concise + +### **2. When to Use Expression Syntax** + +#### **โœ… Perfect for Expression Functions:** + +1. **Simple calculations**: + ```kotlin + fun square(x: Int): Int = x * x + fun cube(x: Int): Int = x * x * x + fun abs(x: Int): Int = if (x < 0) -x else x + ``` + +2. **Boolean checks**: + ```kotlin + fun isEven(n: Int): Boolean = n % 2 == 0 + fun isPositive(n: Int): Boolean = n> 0 + fun isInRange(n: Int, min: Int, max: Int): Boolean = n in min..max + ``` + +3. **Simple transformations**: + ```kotlin + fun capitalize(str: String): String = str.capitalize() + fun reverse(str: String): String = str.reversed() + fun double(n: Int): Int = n * 2 + ``` + +#### **โŒ Avoid Expression Functions When:** + +1. **Multiple statements needed**: + ```kotlin + // โŒ Don't do this - too complex + fun processData(data: String): String = data.trim().toLowerCase().replace(" ", "_") + + // โœ… Better as traditional function + fun processData(data: String): String { + return data.trim() + .toLowerCase() + .replace(" ", "_") + } + ``` + +2. **Complex logic with side effects**: + ```kotlin + // โŒ Don't do this - side effects in expression + fun validateUser(user: User): Boolean = user.name.isNotEmpty() && user.email.isNotEmpty() && println("User validated") + + // โœ… Better as traditional function + fun validateUser(user: User): Boolean { + val isValid = user.name.isNotEmpty() && user.email.isNotEmpty() + if (isValid) println("User validated") + return isValid + } + ``` + +### **3. Type Inference with Expression Functions** + +#### **Explicit Return Type** + +```kotlin +fun max(a: Int, b: Int): Int = if (a> b) a else b +``` + +#### **Inferred Return Type** + +```kotlin +fun max(a: Int, b: Int) = if (a> b) a else b // Type inferred as Int +fun isEven(n: Int) = n % 2 == 0 // Type inferred as Boolean +fun greet(name: String) = "Hello, $name!" // Type inferred as String +``` + +**Type Inference Rules:** +- **Simple expressions**: Type is automatically inferred +- **Complex expressions**: Explicit type may be needed +- **Generic functions**: Usually need explicit type parameters + +## ๐Ÿงช Hands-On Exercises + +### **Exercise 1: Convert to Expression Function** +Convert this traditional function to an expression function: + +```kotlin +fun isAdult(age: Int): Boolean { + return age>= 18 +} +``` + +**Solution:** +```kotlin +fun isAdult(age: Int): Boolean = age>= 18 +``` + +### **Exercise 2: Create Expression Functions** +Create expression functions for these operations: + +```kotlin +fun main(args: Array) { + // TODO: Create these functions as expressions + // 1. Check if number is prime + // 2. Get the minimum of three numbers + // 3. Check if string is palindrome +} +``` + +**Solution:** +```kotlin +// Check if number is prime (simplified) +fun isPrime(n: Int): Boolean = n> 1 && (2..n/2).none { n % it == 0 } + +// Get minimum of three numbers +fun minOfThree(a: Int, b: Int, c: Int): Int = minOf(a, b, c) + +// Check if string is palindrome +fun isPalindrome(str: String): Boolean = str == str.reversed() + +fun main(args: Array) { + println("Is 17 prime? ${isPrime(17)}") + println("Min of 5, 2, 8: ${minOfThree(5, 2, 8)}") + println("Is 'racecar' palindrome? ${isPalindrome("racecar")}") +} +``` + +### **Exercise 3: Control Flow in Expressions** +Create expression functions using if and when expressions: + +```kotlin +fun main(args: Array) { + // TODO: Create these functions using control flow + // 1. Grade calculator (A, B, C, D, F) + // 2. Day of week name + // 3. Season determiner +} +``` + +**Solution:** +```kotlin +// Grade calculator +fun getGrade(score: Int): String = when { + score>= 90 -> "A" + score>= 80 -> "B" + score>= 70 -> "C" + score>= 60 -> "D" + else -> "F" +} + +// Day of week name +fun getDayName(day: Int): String = when (day) { + 1 -> "Monday" + 2 -> "Tuesday" + 3 -> "Wednesday" + 4 -> "Thursday" + 5 -> "Friday" + 6 -> "Saturday" + 7 -> "Sunday" + else -> "Invalid day" +} + +// Season determiner +fun getSeason(month: Int): String = when (month) { + in 3..5 -> "Spring" + in 6..8 -> "Summer" + in 9..11 -> "Fall" + 12, 1, 2 -> "Winter" + else -> "Invalid month" +} + +fun main(args: Array) { + println("Score 85: ${getGrade(85)}") + println("Day 3: ${getDayName(3)}") + println("Month 7: ${getSeason(7)}") +} +``` + +### **Exercise 4: Complex Expression Functions** +Create expression functions that combine multiple operations: + +```kotlin +fun main(args: Array) { + // TODO: Create these complex expression functions + // 1. Format currency amount + // 2. Validate email format (simplified) + // 3. Calculate BMI category +} +``` + +**Solution:** +```kotlin +// Format currency amount +fun formatCurrency(amount: Double, currency: String = "$"): String = + "$currency${String.format("%.2f", amount)}" + +// Validate email format (simplified) +fun isValidEmail(email: String): Boolean = + email.contains("@") && email.contains(".") && email.length> 5 + +// Calculate BMI category +fun getBMICategory(weight: Double, height: Double): String { + val bmi = weight / (height * height) + return when { + bmi < 18.5 -> "Underweight" + bmi < 25 -> "Normal" + bmi < 30 -> "Overweight" + else -> "Obese" + } +} + +fun main(args: Array) { + println("Price: ${formatCurrency(29.99)}") + println("Email valid? ${isValidEmail("user@example.com")}") + println("BMI Category: ${getBMICategory(70.0, 1.75)}") +} +``` + +## ๐Ÿ” Advanced Expression Function Patterns + +### **1. Nested Expression Functions** + +```kotlin +fun processNumber(n: Int): String = + if (n> 0) { + if (n % 2 == 0) "Positive even" else "Positive odd" + } else if (n < 0) { + if (n % 2 == 0) "Negative even" else "Negative odd" + } else "Zero" +``` + +### **2. Expression Functions with Collections** + +```kotlin +fun getFirstEven(numbers: List): Int? = + numbers.firstOrNull { it % 2 == 0 } + +fun getSumOfEvens(numbers: List): Int = + numbers.filter { it % 2 == 0 }.sum() + +fun hasDuplicates(numbers: List): Boolean = + numbers.size != numbers.distinct().size +``` + +### **3. Expression Functions with Extension Functions** + +```kotlin +fun String.isValidPassword(): Boolean = + length>= 8 && any { it.isUpperCase() } && any { it.isDigit() } + +fun Int.isInRange(min: Int, max: Int): Boolean = + this in min..max + +fun List.getStats(): String = + "Count: $size, Sum: ${sum()}, Average: ${average()}" +``` + +## ๐Ÿ” When to Use Each Approach + +### **โœ… Use Expression Functions When:** + +1. **Single operation**: + ```kotlin + fun double(x: Int) = x * 2 + fun isEven(x: Int) = x % 2 == 0 + ``` + +2. **Simple conditional logic**: + ```kotlin + fun max(a: Int, b: Int) = if (a> b) a else b + fun getStatus(age: Int) = if (age>= 18) "Adult" else "Minor" + ``` + +3. **Property-like behavior**: + ```kotlin + fun getFullName() = "$firstName $lastName" + fun getDisplayPrice() = "$$price" + ``` + +### **โœ… Use Traditional Functions When:** + +1. **Multiple statements**: + ```kotlin + fun processUser(user: User): Boolean { + val isValid = validateUser(user) + if (isValid) { + saveUser(user) + sendWelcomeEmail(user) + } + return isValid + } + ``` + +2. **Complex logic**: + ```kotlin + fun calculateTax(income: Double): Double { + var tax = 0.0 + when { + income <= 50000 -> tax = income * 0.1 + income <= 100000 -> tax = 5000 + (income - 50000) * 0.2 + else -> tax = 15000 + (income - 100000) * 0.3 + } + return tax + } + ``` + +3. **Side effects**: + ```kotlin + fun logAndProcess(data: String): String { + println("Processing: $data") + val result = data.trim().toLowerCase() + println("Result: $result") + return result + } + ``` + +## ๐Ÿšจ Common Mistakes to Avoid + +### **1. Forgetting Return Type for Complex Expressions** + +```kotlin +// โŒ Type inference may fail for complex expressions +fun processData(data: String) = data.trim().toLowerCase().replace(" ", "_") + +// โœ… Explicit type helps compiler and readability +fun processData(data: String): String = data.trim().toLowerCase().replace(" ", "_") +``` + +### **2. Using Expression Functions for Side Effects** + +```kotlin +// โŒ Side effects in expression function +fun validateAndLog(data: String) = data.isNotEmpty() && println("Data is valid") + +// โœ… Traditional function for side effects +fun validateAndLog(data: String): Boolean { + val isValid = data.isNotEmpty() + if (isValid) println("Data is valid") + return isValid +} +``` + +### **3. Overly Complex Expression Functions** + +```kotlin +// โŒ Too complex - hard to read +fun getComplexResult(a: Int, b: Int, c: Int) = + if (a> b) if (a> c) a else c else if (b> c) b else c + +// โœ… Better as traditional function +fun getComplexResult(a: Int, b: Int, c: Int): Int { + return when { + a> b && a> c -> a + b> c -> b + else -> c + } +} +``` + +## ๐Ÿ” Best Practices + +### **โœ… Do's** + +1. **Use expression functions for simple operations**: + ```kotlin + fun square(x: Int) = x * x + fun isPositive(x: Int) = x> 0 + ``` + +2. **Keep expressions readable**: + ```kotlin + fun getStatus(age: Int) = if (age>= 18) "Adult" else "Minor" + ``` + +3. **Use explicit types when helpful**: + ```kotlin + fun processString(input: String): String = input.trim().toLowerCase() + ``` + +### **โŒ Don'ts** + +1. **Don't sacrifice readability for brevity**: + ```kotlin + // โŒ Hard to read + fun f(x:Int,y:Int)=if(x>y)x else y + + // โœ… Clear and readable + fun max(x: Int, y: Int) = if (x> y) x else y + ``` + +2. **Don't use expressions for complex logic**: + ```kotlin + // โŒ Too complex for expression + fun calculateComplex(a: Int, b: Int, c: Int) = + if (a> 0) if (b> 0) if (c> 0) a + b + c else a + b else if (c> 0) a + c else a else 0 + ``` + +## ๐ŸŽฏ What's Next? + +Excellent! You've mastered functions as expressions in Kotlin. Now you're ready to: + +1. **Learn named parameters** โ†’ [Named Parameters](03-named-parameters.md) +2. **Explore extension functions** โ†’ [Extension Functions](04-extension-functions.md) +3. **Understand infix functions** โ†’ [Infix Functions](05-infix-functions.md) +4. **Master tailrec functions** โ†’ [Tailrec Functions](06-tailrec-functions.md) + +## ๐Ÿ“š Additional Resources + +- [Kotlin Functions](https://kotlinlang.org/docs/functions.html) +- [Function Expressions](https://kotlinlang.org/docs/functions.html#single-expression-functions) +- [Type Inference](https://kotlinlang.org/docs/basic-types.html#type-inference) + +## ๐Ÿ† Summary + +- โœ… You can write single-expression functions +- โœ… You understand when to use expression syntax +- โœ… You can convert traditional functions to expression syntax +- โœ… You can use expression functions with control flow +- โœ… You can write more concise and readable code +- โœ… You're ready to create elegant, functional Kotlin code! + +**Keep practicing and happy coding! ๐ŸŽ‰** diff --git a/docs/functions/03-named-parameters.md b/docs/functions/03-named-parameters.md new file mode 100644 index 0000000..2719b3b --- /dev/null +++ b/docs/functions/03-named-parameters.md @@ -0,0 +1,645 @@ +# ๐Ÿท๏ธ Named Parameters in Kotlin + +Welcome to the world of named parameters! Named parameters make your function calls more readable and self-documenting. They allow you to specify which parameter each argument corresponds to, eliminating confusion about parameter order and making your code more maintainable. + +## ๐Ÿ“‹ Learning Objectives + +By the end of this lesson, you will be able to: +- โœ… Use named parameters in function calls +- โœ… Understand the benefits of named parameters +- โœ… Combine named and positional parameters +- โœ… Use default values with named parameters +- โœ… Write more readable and maintainable code + +## ๐Ÿ” What You'll Learn + +- **Named parameter syntax** - How to specify parameter names +- **Parameter order flexibility** - Freedom from strict ordering +- **Default values** - Combining with named parameters +- **Best practices** - When and how to use named parameters +- **Real-world applications** - Practical use cases + +## ๐Ÿ“ Prerequisites + +- Basic understanding of functions +- Completed [Functions as Expressions](02-functions-as-expressions.md) lesson +- Knowledge of function parameters and default values + +## ๐Ÿ’ป The Code + +Let's examine the named parameters example: + +```kotlin +fun findTheVolume(length: Int, breadth: Int, height: Int = 10): Int { + return length * breadth * height +} + +fun main(args: Array) { + var result = findTheVolume(breadth = 2, length = 3) + print(result) +} +``` + +**๐Ÿ“ File:** [21_named_parameters.kt](src/21_named_parameters.kt) + +## ๐Ÿ” Code Breakdown + +### **Function Definition** + +```kotlin +fun findTheVolume(length: Int, breadth: Int, height: Int = 10): Int { + return length * breadth * height +} +``` + +- **`length: Int`**: First parameter for length +- **`breadth: Int`**: Second parameter for breadth +- **`height: Int = 10`**: Third parameter with default value 10 +- **Return type**: `Int` (volume calculation) + +### **Function Call with Named Parameters** + +```kotlin +var result = findTheVolume(breadth = 2, length = 3) +``` + +- **`breadth = 2`**: Explicitly assigns 2 to breadth parameter +- **`length = 3`**: Explicitly assigns 3 to length parameter +- **`height`**: Not specified, uses default value 10 +- **Result**: 3 ร—ใฐใคใ‹ใ‘ใ‚‹ 2 ร—ใฐใคใ‹ใ‘ใ‚‹ 10 =ใ‚ 60 + +## ๐ŸŽฏ Key Concepts Explained + +### **1. Named Parameter Syntax** + +#### **Basic Named Parameter Usage** + +```kotlin +fun greet(firstName: String, lastName: String, title: String = "Mr.") { + println("Hello, $title $firstName $lastName!") +} + +// Using named parameters +greet(firstName = "John", lastName = "Doe") +greet(lastName = "Smith", firstName = "Jane", title = "Dr.") +``` + +**Benefits:** +- **Clear intent**: Each argument's purpose is obvious +- **Order flexibility**: Parameters can be in any order +- **Self-documenting**: Code reads like natural language + +#### **Positional vs Named Parameters** + +```kotlin +// Positional parameters (traditional) +greet("John", "Doe", "Mr.") + +// Named parameters +greet(firstName = "John", lastName = "Doe", title = "Mr.") + +// Mixed approach +greet("John", lastName = "Doe", title = "Dr.") +``` + +### **2. Default Values with Named Parameters** + +#### **Function with Default Values** + +```kotlin +fun createUser( + username: String, + email: String, + age: Int = 18, + isActive: Boolean = true, + role: String = "user" +) { + println("User: $username, Email: $email, Age: $age, Active: $isActive, Role: $role") +} +``` + +#### **Calling with Named Parameters** + +```kotlin +// Specify only required parameters +createUser(username = "john_doe", email = "john@example.com") + +// Override some defaults +createUser( + username = "admin", + email = "admin@example.com", + role = "administrator" +) + +// Override all defaults +createUser( + username = "moderator", + email = "mod@example.com", + age = 25, + isActive = false, + role = "moderator" +) +``` + +### **3. Parameter Order Flexibility** + +#### **Traditional Positional Call** + +```kotlin +fun calculateDistance(x1: Double, y1: Double, x2: Double, y2: Double): Double { + return Math.sqrt((x2 - x1).pow(2) + (y2 - y1).pow(2)) +} + +// Must remember exact order +val distance = calculateDistance(0.0, 0.0, 3.0, 4.0) +``` + +#### **Named Parameter Call** + +```kotlin +// Clear and readable +val distance = calculateDistance( + x1 = 0.0, + y1 = 0.0, + x2 = 3.0, + y2 = 4.0 +) + +// Order doesn't matter +val distance2 = calculateDistance( + y2 = 4.0, + x1 = 0.0, + y1 = 0.0, + x2 = 3.0 +) +``` + +## ๐Ÿงช Hands-On Exercises + +### **Exercise 1: Basic Named Parameters** +Create a function that builds a URL with named parameters: + +```kotlin +fun main(args: Array) { + // TODO: Create a buildUrl function and call it with named parameters + // Parameters: protocol, host, port, path, query + // Default values: protocol="https", port=443, path="/", query="" +} +``` + +**Solution:** +```kotlin +fun buildUrl( + protocol: String = "https", + host: String, + port: Int = 443, + path: String = "/", + query: String = "" +): String { + val url = "$protocol://$host" + val portPart = if (port != 443) ":$port" else "" + val queryPart = if (query.isNotEmpty()) "?$query" else "" + return "$url$portPart$path$queryPart" +} + +fun main(args: Array) { + // Using named parameters + val url1 = buildUrl( + host = "example.com", + path = "/api/users" + ) + + val url2 = buildUrl( + protocol = "http", + host = "localhost", + port = 8080, + path = "/admin", + query = "debug=true" + ) + + println("URL 1: $url1") + println("URL 2: $url2") +} +``` + +### **Exercise 2: Configuration Object Builder** +Create a function that builds a configuration object: + +```kotlin +fun main(args: Array) { + // TODO: Create a buildConfig function with named parameters + // Parameters: database, cache, logging, timeout + // Use sensible defaults +} +``` + +**Solution:** +```kotlin +fun buildConfig( + database: String = "localhost:5432", + cache: Boolean = true, + logging: String = "INFO", + timeout: Int = 30 +): String { + return """ + Configuration: + - Database: $database + - Cache: $cache + - Logging: $logging + - Timeout: ${timeout}s + """.trimIndent() +} + +fun main(args: Array) { + // Minimal configuration + val config1 = buildConfig(database = "prod-db:5432") + + // Custom configuration + val config2 = buildConfig( + database = "test-db:5432", + cache = false, + logging = "DEBUG", + timeout = 60 + ) + + println("Config 1:\n$config1") + println("\nConfig 2:\n$config2") +} +``` + +### **Exercise 3: Mixed Parameter Styles** +Create a function that demonstrates mixing named and positional parameters: + +```kotlin +fun main(args: Array) { + // TODO: Create a formatMessage function that can be called in different ways + // First parameter should be positional, others can be named +} +``` + +**Solution:** +```kotlin +fun formatMessage( + message: String, + prefix: String = "", + suffix: String = "", + uppercase: Boolean = false, + repeat: Int = 1 +): String { + var result = prefix + message + suffix + if (uppercase) result = result.uppercase() + return result.repeat(repeat) +} + +fun main(args: Array) { + // Positional first parameter, named others + val msg1 = formatMessage( + "Hello World", + prefix = ">>> ", + suffix = " <<<" + ) + + // All named parameters + val msg2 = formatMessage( + message = "Goodbye", + uppercase = true, + repeat = 3 + ) + + // Mixed style + val msg3 = formatMessage( + "Test", + suffix = "!", + repeat = 2 + ) + + println(msg1) + println(msg2) + println(msg3) +} +``` + +### **Exercise 4: Complex Named Parameters** +Create a function for building a complex data structure: + +```kotlin +fun main(args: Array) { + // TODO: Create a buildPerson function with many optional parameters + // Use named parameters to make it readable +} +``` + +**Solution:** +```kotlin +fun buildPerson( + firstName: String, + lastName: String, + age: Int = 0, + email: String = "", + phone: String = "", + address: String = "", + occupation: String = "", + hobbies: List = emptyList(), + isActive: Boolean = true +): String { + return """ + Person Details: + Name: $firstName $lastName + Age: ${if (age> 0) age else "Not specified"} + Email: ${if (email.isNotEmpty()) email else "Not specified"} + Phone: ${if (phone.isNotEmpty()) phone else "Not specified"} + Address: ${if (address.isNotEmpty()) address else "Not specified"} + Occupation: ${if (occupation.isNotEmpty()) occupation else "Not specified"} + Hobbies: ${if (hobbies.isNotEmpty()) hobbies.joinToString(", ") else "None"} + Status: ${if (isActive) "Active" else "Inactive"} + """.trimIndent() +} + +fun main(args: Array) { + // Minimal person + val person1 = buildPerson( + firstName = "John", + lastName = "Doe" + ) + + // Complete person + val person2 = buildPerson( + firstName = "Jane", + lastName = "Smith", + age = 30, + email = "jane@example.com", + phone = "+1-555-0123", + address = "123 Main St, City", + occupation = "Software Engineer", + hobbies = listOf("Reading", "Hiking", "Cooking"), + isActive = true + ) + + println("Person 1:\n$person1") + println("\nPerson 2:\n$person2") +} +``` + +## ๐Ÿ” Advanced Named Parameter Patterns + +### **1. Builder Pattern with Named Parameters** + +```kotlin +class DatabaseConfig private constructor( + val host: String, + val port: Int, + val username: String, + val password: String, + val database: String +) { + companion object { + fun build( + host: String = "localhost", + port: Int = 5432, + username: String = "postgres", + password: String = "", + database: String = "mydb" + ) = DatabaseConfig(host, port, username, password, database) + } +} + +// Usage +val config = DatabaseConfig.build( + host = "prod-server.com", + username = "admin", + password = "secret123" +) +``` + +### **2. Function Overloading with Named Parameters** + +```kotlin +fun createConnection( + host: String, + port: Int = 3306, + database: String = "default" +) = "mysql://$host:$port/$database" + +fun createConnection( + host: String, + database: String, + ssl: Boolean = false +) = "postgresql://$host/$database${if (ssl) "?sslmode=require" else ""}" + +// Usage +val mysqlConn = createConnection(host = "localhost", port = 3306) +val pgConn = createConnection(host = "localhost", database = "users", ssl = true) +``` + +### **3. DSL-like Function Calls** + +```kotlin +fun html( + head: String = "", + body: String = "", + title: String = "", + charset: String = "UTF-8" +): String { + return """ + + + + + ${if (title.isNotEmpty()) "$title" else ""} + $head + +
+ $body +

AltStyle ใซใ‚ˆใฃใฆๅค‰ๆ›ใ•ใ‚ŒใŸใƒšใƒผใ‚ธ (->ใ‚ชใƒชใ‚ธใƒŠใƒซ) /

+ + """.trimIndent() +} + +// Usage +val page = html( + title = "My Page", + head = "", + body = "

Hello World

" +) +``` + +## ๐Ÿ” When to Use Named Parameters + +### **โœ… Use Named Parameters When:** + +1. **Many parameters**: + ```kotlin + // โœ… Clear with named parameters + createUser( + username = "john", + email = "john@example.com", + firstName = "John", + lastName = "Doe", + age = 25, + isActive = true + ) + + // โŒ Confusing with positional + createUser("john", "john@example.com", "John", "Doe", 25, true) + ``` + +2. **Boolean parameters**: + ```kotlin + // โœ… Clear meaning + sendEmail( + to = "user@example.com", + subject = "Welcome", + html = true, + attachments = false + ) + ``` + +3. **Default values**: + ```kotlin + // โœ… Only specify what you need + connect( + host = "localhost", + database = "users" + // port and timeout use defaults + ) + ``` + +### **โœ… Use Positional Parameters When:** + +1. **Few parameters**: + ```kotlin + // โœ… Simple and clear + add(5, 3) + greet("John") + ``` + +2. **Standard order**: + ```kotlin + // โœ… Conventional order + substring(0, 5) + sublist(1, 4) + ``` + +## ๐Ÿšจ Common Mistakes to Avoid + +### **1. Mixing Named and Positional Incorrectly** + +```kotlin +// โŒ Named parameters must come after positional +fun example(a: Int, b: Int, c: Int) { } + +// โŒ Wrong order +example(a = 1, 2, c = 3) + +// โœ… Correct order +example(1, b = 2, c = 3) +``` + +### **2. Forgetting Default Values** + +```kotlin +// โŒ Function expects 3 parameters +fun process(name: String, age: Int, city: String) { } + +// โŒ Missing required parameter +process(name = "John", city = "New York") + +// โœ… Provide all required parameters +process(name = "John", age = 25, city = "New York") +``` + +### **3. Overusing Named Parameters** + +```kotlin +// โŒ Overkill for simple calls +val result = add(a = 5, b = 3) + +// โœ… Simple and clear +val result = add(5, 3) +``` + +## ๐Ÿ” Best Practices + +### **โœ… Do's** + +1. **Use named parameters for clarity**: + ```kotlin + // โœ… Clear intent + createUser( + username = "john_doe", + email = "john@example.com", + isActive = true + ) + ``` + +2. **Group related parameters**: + ```kotlin + // โœ… Logical grouping + sendEmail( + to = "user@example.com", + subject = "Welcome", + body = "Welcome to our service!", + // Email options + html = true, + attachments = false, + priority = "high" + ) + ``` + +3. **Use meaningful parameter names**: + ```kotlin + // โœ… Descriptive names + fun calculateArea( + rectangleWidth: Double, + rectangleHeight: Double + ): Double = rectangleWidth * rectangleHeight + ``` + +### **โŒ Don'ts** + +1. **Don't use named parameters for simple calls**: + ```kotlin + // โŒ Unnecessary complexity + val sum = add(a = 5, b = 3) + + // โœ… Simple and clear + val sum = add(5, 3) + ``` + +2. **Don't mix styles inconsistently**: + ```kotlin + // โŒ Inconsistent style + fun example(a: Int, b: Int, c: Int) { } + example(1, b = 2, 3) // Mixed style + + // โœ… Consistent style + example(1, 2, 3) // All positional + // OR + example(a = 1, b = 2, c = 3) // All named + ``` + +## ๐ŸŽฏ What's Next? + +Excellent! You've mastered named parameters in Kotlin. Now you're ready to: + +1. **Explore extension functions** โ†’ [Extension Functions](04-extension-functions.md) +2. **Understand infix functions** โ†’ [Infix Functions](05-infix-functions.md) +3. **Master tailrec functions** โ†’ [Tailrec Functions](06-tailrec-functions.md) +4. **Learn about lambdas** โ†’ [Lambdas](../functional-programming/01-lambdas.md) + +## ๐Ÿ“š Additional Resources + +- [Kotlin Functions](https://kotlinlang.org/docs/functions.html) +- [Named Arguments](https://kotlinlang.org/docs/functions.html#named-arguments) +- [Default Arguments](https://kotlinlang.org/docs/functions.html#default-arguments) + +## ๐Ÿ† Summary + +- โœ… You can use named parameters in function calls +- โœ… You understand the benefits of named parameters +- โœ… You can combine named and positional parameters +- โœ… You can use default values with named parameters +- โœ… You can write more readable and maintainable code +- โœ… You're ready to create self-documenting function calls! + +**Keep practicing and happy coding! ๐ŸŽ‰** diff --git a/docs/functions/04-extension-functions.md b/docs/functions/04-extension-functions.md new file mode 100644 index 0000000..84387cf --- /dev/null +++ b/docs/functions/04-extension-functions.md @@ -0,0 +1,609 @@ +# ๐Ÿ”ง Extension Functions in Kotlin + +Welcome to the powerful world of extension functions! Extension functions allow you to add new functionality to existing classes without modifying their source code. This is one of Kotlin's most powerful features, enabling you to write more expressive and readable code. + +## ๐Ÿ“‹ Learning Objectives + +By the end of this lesson, you will be able to: +- โœ… Create extension functions for existing classes +- โœ… Understand how extension functions work +- โœ… Use extension functions to enhance readability +- โœ… Apply extension functions to different data types +- โœ… Write more expressive and maintainable code + +## ๐Ÿ” What You'll Learn + +- **Extension function syntax** - How to define and use them +- **Receiver types** - Understanding the `this` context +- **Scope and visibility** - Where extension functions can be used +- **Best practices** - When and how to use extension functions +- **Real-world applications** - Practical use cases + +## ๐Ÿ“ Prerequisites + +- Basic understanding of functions +- Completed [Named Parameters](03-named-parameters.md) lesson +- Knowledge of classes and objects + +## ๐Ÿ’ป The Code + +Let's examine the extension functions examples: + +```kotlin +// Example 1: Extension function on custom class +fun Studentt.isScholar(marks: Int): Boolean { + return marks> 95 +} + +class Studentt { + fun hasPassed(marks: Int): Boolean { + return marks> 40 + } +} + +// Example 2: Extension functions on built-in types +fun String.add(s1: String, s2: String): String { + return this + s1 + s2 +} + +fun Int.greaterValue(other: Int): Int { + if (this> other) + return this + else + return other +} +``` + +**๐Ÿ“ Files:** +- [22_extension_function_one.kt](src/22_extension_function_one.kt) +- [23_extension_function_two.kt](src/23_extension_function_two.kt) + +## ๐Ÿ” Code Breakdown + +### **Extension Function on Custom Class** + +```kotlin +fun Studentt.isScholar(marks: Int): Boolean { + return marks> 95 +} +``` + +- **`fun Studentt.isScholar`**: Extension function declaration +- **`Studentt`**: Receiver type (the class being extended) +- **`isScholar`**: Function name +- **`marks: Int`**: Parameter +- **`Boolean`**: Return type + +### **Extension Function on Built-in Types** + +```kotlin +fun String.add(s1: String, s2: String): String { + return this + s1 + s2 +} +``` + +- **`fun String.add`**: Extension function on String class +- **`this`**: Refers to the String instance being extended +- **`s1 + s2`**: Concatenates additional strings + +## ๐ŸŽฏ Key Concepts Explained + +### **1. Extension Function Syntax** + +#### **Basic Syntax** + +```kotlin +fun ReceiverType.functionName(parameters): ReturnType { + // Function body + // 'this' refers to the receiver object +} +``` + +**Components:** +- **`fun`**: Function keyword +- **`ReceiverType`**: The type being extended +- **`.`**: Dot separator +- **`functionName`**: Name of the extension function +- **`parameters`**: Function parameters (optional) +- **`ReturnType`**: Return type of the function + +#### **Example with Different Types** + +```kotlin +// Extension on Int +fun Int.isEven(): Boolean = this % 2 == 0 + +// Extension on String +fun String.capitalizeFirst(): String = + if (isNotEmpty()) this[0].uppercase() + substring(1) else this + +// Extension on List +fun List.sumOfEvens(): Int = + filter { it % 2 == 0 }.sum() + +// Extension on nullable types +fun String?.orEmpty(): String = this ?: "" +``` + +### **2. How Extension Functions Work** + +#### **The `this` Context** + +```kotlin +fun String.repeat(times: Int): String { + var result = "" + repeat(times) { + result += this // 'this' refers to the String instance + } + return result +} + +// Usage +val result = "Hello".repeat(3) // "HelloHelloHello" +``` + +**Key Points:** +- **`this`** refers to the receiver object +- Extension functions are **static** under the hood +- They don't modify the original class +- They're resolved **statically** at compile time + +#### **Extension vs Member Functions** + +```kotlin +class Person(val name: String) { + // Member function + fun greet(): String = "Hello, I'm $name" +} + +// Extension function +fun Person.formalGreet(): String = "Good day, my name is $name" + +// Usage +val person = Person("John") +println(person.greet()) // "Hello, I'm John" +println(person.formalGreet()) // "Good day, my name is John" +``` + +### **3. Extension Function Scope and Visibility** + +#### **Top-level Extensions** + +```kotlin +// File: StringExtensions.kt +fun String.reverse(): String = this.reversed() + +fun String.isPalindrome(): Boolean = this == this.reversed() + +// Can be used anywhere the file is imported +``` + +#### **Local Extensions** + +```kotlin +fun main() { + // Local extension function + fun String.shout(): String = this.uppercase() + "!" + + val message = "hello" + println(message.shout()) // "HELLO!" +} + +// This extension is only available within main() +``` + +#### **Member Extensions** + +```kotlin +class StringProcessor { + fun String.process(): String = this.trim().uppercase() + + fun processText(text: String): String { + return text.process() // Can use the extension + } +} + +// Usage +val processor = StringProcessor() +val result = processor.processText(" hello world ") // "HELLO WORLD" +``` + +## ๐Ÿงช Hands-On Exercises + +### **Exercise 1: Basic Extension Functions** +Create extension functions for common operations: + +```kotlin +fun main(args: Array) { + // TODO: Create these extension functions + // 1. Int.isPositive() + // 2. String.isEmail() + // 3. List.average() +} +``` + +**Solution:** +```kotlin +// Extension function to check if Int is positive +fun Int.isPositive(): Boolean = this> 0 + +// Extension function to check if String is valid email (simplified) +fun String.isEmail(): Boolean = + this.contains("@") && this.contains(".") && this.length> 5 + +// Extension function to calculate average of List +fun List.average(): Double = + if (isEmpty()) 0.0 else sum().toDouble() / size + +fun main(args: Array) { + val number = 42 + println("Is $number positive? ${number.isPositive()}") + + val email = "user@example.com" + println("Is '$email' a valid email? ${email.isEmail()}") + + val numbers = listOf(1, 2, 3, 4, 5) + println("Average of $numbers is ${numbers.average()}") +} +``` + +### **Exercise 2: String Extensions** +Create useful string extension functions: + +```kotlin +fun main(args: Array) { + // TODO: Create these string extension functions + // 1. String.toTitleCase() + // 2. String.wordCount() + // 3. String.truncate(maxLength: Int) +} +``` + +**Solution:** +```kotlin +// Convert string to title case +fun String.toTitleCase(): String = + split(" ").joinToString(" ") { word -> + if (word.isNotEmpty()) word[0].uppercase() + word.substring(1).lowercase() else word + } + +// Count words in string +fun String.wordCount(): Int = + if (trim().isEmpty()) 0 else trim().split("\\s+".toRegex()).size + +// Truncate string to specified length +fun String.truncate(maxLength: Int): String = + if (length <= maxLength) this else substring(0, maxLength) + "..." + +fun main(args: Array) { + val text = "hello world example" + println("Original: '$text'") + println("Title case: '${text.toTitleCase()}'") + println("Word count: ${text.wordCount()}") + println("Truncated (10): '${text.truncate(10)}'") +} +``` + +### **Exercise 3: Collection Extensions** +Create extension functions for collections: + +```kotlin +fun main(args: Array) { + // TODO: Create these collection extension functions + // 1. List.second() + // 2. List.product() + // 3. List.longest() +} +``` + +**Solution:** +```kotlin +// Get second element safely +fun List.second(): T? = if (size>= 2) this[1] else null + +// Calculate product of all integers +fun List.product(): Long = + if (isEmpty()) 0L else fold(1L) { acc, num -> acc * num } + +// Find longest string +fun List.longest(): String? = + maxByOrNull { it.length } + +fun main(args: Array) { + val numbers = listOf(1, 2, 3, 4, 5) + println("Second element: ${numbers.second()}") + println("Product: ${numbers.product()}") + + val words = listOf("apple", "banana", "cherry", "date") + println("Longest word: ${words.longest()}") +} +``` + +### **Exercise 4: Custom Class Extensions** +Create extension functions for a custom class: + +```kotlin +fun main(args: Array) { + // TODO: Create a Rectangle class and extension functions + // 1. Rectangle.area() + // 2. Rectangle.isSquare() + // 3. Rectangle.scale(factor: Double) +} +``` + +**Solution:** +```kotlin +data class Rectangle(val width: Double, val height: Double) + +// Extension function to calculate area +fun Rectangle.area(): Double = width * height + +// Extension function to check if rectangle is square +fun Rectangle.isSquare(): Boolean = width == height + +// Extension function to scale rectangle +fun Rectangle.scale(factor: Double): Rectangle = + Rectangle(width * factor, height * factor) + +fun main(args: Array) { + val rect = Rectangle(5.0, 3.0) + println("Rectangle: $rect") + println("Area: ${rect.area()}") + println("Is square? ${rect.isSquare()}") + + val scaled = rect.scale(2.0) + println("Scaled (2x): $scaled") + println("New area: ${scaled.area()}") +} +``` + +## ๐Ÿ” Advanced Extension Function Patterns + +### **1. Nullable Type Extensions** + +```kotlin +// Safe string operations +fun String?.orEmpty(): String = this ?: "" +fun String?.isNullOrBlank(): Boolean = this?.isBlank() ?: true + +// Safe number operations +fun Int?.orZero(): Int = this ?: 0 +fun Double?.orDefault(default: Double): Double = this ?: default + +// Usage +val nullableString: String? = null +val safeString = nullableString.orEmpty() // "" +val length = nullableString?.length ?: 0 // 0 +``` + +### **2. Generic Extensions** + +```kotlin +// Generic list extensions +fun List.secondOrNull(): T? = if (size>= 2) this[1] else null +fun List.lastOrNull(): T? = if (isNotEmpty()) last() else null + +// Generic comparison extensions +fun > T.isBetween(min: T, max: T): Boolean = + this in min..max + +// Usage +val numbers = listOf(1, 2, 3, 4, 5) +val second = numbers.secondOrNull() // 2 +val last = numbers.lastOrNull() // 5 + +val value = 42 +val inRange = value.isBetween(0, 100) // true +``` + +### **3. Infix Extension Functions** + +```kotlin +// Infix extension for range checking +infix fun Int.inRange(range: IntRange): Boolean = this in range + +// Infix extension for string concatenation +infix fun String.concat(other: String): String = this + other + +// Usage +val age = 25 +val isAdult = age inRange 18..65 // true + +val greeting = "Hello" concat " World" // "Hello World" +``` + +### **4. Extension Properties** + +```kotlin +// Extension properties +val String.isEmail: Boolean + get() = contains("@") && contains(".") && length> 5 + +val String.isPalindrome: Boolean + get() = this == reversed() + +val List.sum: Int + get() = fold(0) { acc, num -> acc + num } + +// Usage +val email = "user@example.com" +println("Is email? ${email.isEmail}") + +val text = "racecar" +println("Is palindrome? ${text.isPalindrome}") + +val numbers = listOf(1, 2, 3, 4, 5) +println("Sum: ${numbers.sum}") +``` + +## ๐Ÿ” When to Use Extension Functions + +### **โœ… Perfect for Extension Functions:** + +1. **Utility functions**: + ```kotlin + fun String.capitalizeFirst() = + if (isNotEmpty()) this[0].uppercase() + substring(1) else this + ``` + +2. **Domain-specific operations**: + ```kotlin + fun LocalDate.isWeekend(): Boolean = + dayOfWeek in listOf(DayOfWeek.SATURDAY, DayOfWeek.SUNDAY) + ``` + +3. **Readability improvements**: + ```kotlin + fun List.adults(): List = + filter { it.age>= 18 } + ``` + +### **โŒ Avoid Extension Functions When:** + +1. **Modifying internal state**: + ```kotlin + // โŒ Don't do this - modifies internal state + fun MutableList.addOne() { + for (i in indices) this[i] += 1 + } + ``` + +2. **Breaking encapsulation**: + ```kotlin + // โŒ Don't do this - breaks encapsulation + fun Person.setPassword(password: String) { + this.password = password // Assuming password is private + } + ``` + +3. **Overriding existing functionality**: + ```kotlin + // โŒ Don't do this - confusing + fun String.length(): Int = this.count() // Overrides existing length property + ``` + +## ๐Ÿšจ Common Mistakes to Avoid + +### **1. Confusing Extension with Member Functions** + +```kotlin +class Person(val name: String) { + fun greet() = "Hello, $name" // Member function +} + +fun Person.formalGreet() = "Good day, $name" // Extension function + +// โŒ Don't expect extension to override member +val person = Person("John") +person.greet() // Calls member function +person.formalGreet() // Calls extension function +``` + +### **2. Extension Function Resolution** + +```kotlin +// Extension functions are resolved statically +open class Animal +class Dog : Animal() + +fun Animal.speak() = "Animal sound" +fun Dog.speak() = "Woof!" + +fun Animal.makeSound() = speak() // Always calls Animal.speak() + +// Usage +val dog: Animal = Dog() +dog.makeSound() // "Animal sound" (not "Woof!") +``` + +### **3. Nullable Extensions** + +```kotlin +// โŒ This won't work as expected +fun String?.safeLength(): Int = this?.length ?: 0 + +// โœ… Better approach +fun String?.safeLength(): Int = this?.length ?: 0 +fun String?.orEmpty(): String = this ?: "" + +// Usage +val nullableString: String? = null +val length = nullableString.safeLength() // 0 +val text = nullableString.orEmpty() // "" +``` + +## ๐Ÿ” Best Practices + +### **โœ… Do's** + +1. **Use descriptive names**: + ```kotlin + // โœ… Clear and descriptive + fun String.capitalizeFirst(): String = ... + fun List.sumOfEvens(): Int = ... + + // โŒ Unclear names + fun String.cap(): String = ... + fun List.sum(): Int = ... + ``` + +2. **Keep extensions focused**: + ```kotlin + // โœ… Single responsibility + fun String.isValidEmail(): Boolean = ... + fun String.formatAsPhone(): String = ... + + // โŒ Too many responsibilities + fun String.process(): String = ... // What does it process? + ``` + +3. **Use extension functions for utilities**: + ```kotlin + // โœ… Good for utility functions + fun String.reverse(): String = this.reversed() + fun Int.isEven(): Boolean = this % 2 == 0 + ``` + +### **โŒ Don'ts** + +1. **Don't abuse extension functions**: + ```kotlin + // โŒ Don't create extensions for everything + fun String.hello(): String = "Hello, $this" + fun Int.addOne(): Int = this + 1 + ``` + +2. **Don't create confusing extensions**: + ```kotlin + // โŒ Confusing - what does it do? + fun String.process(): String = this.trim().uppercase().replace(" ", "_") + + // โœ… Clear and focused + fun String.toSnakeCase(): String = this.trim().uppercase().replace(" ", "_") + ``` + +## ๐ŸŽฏ What's Next? + +Excellent! You've mastered extension functions in Kotlin. Now you're ready to: + +1. **Understand infix functions** โ†’ [Infix Functions](05-infix-functions.md) +2. **Master tailrec functions** โ†’ [Tailrec Functions](06-tailrec-functions.md) +3. **Learn about lambdas** โ†’ [Lambdas](../functional-programming/01-lambdas.md) +4. **Explore collections** โ†’ [Arrays](../collections/01-arrays.md) + +## ๐Ÿ“š Additional Resources + +- [Kotlin Extension Functions](https://kotlinlang.org/docs/extensions.html) +- [Extension Functions](https://kotlinlang.org/docs/extensions.html#extension-functions) +- [Extension Properties](https://kotlinlang.org/docs/extensions.html#extension-properties) + +## ๐Ÿ† Summary + +- โœ… You can create extension functions for existing classes +- โœ… You understand how extension functions work +- โœ… You can use extension functions to enhance readability +- โœ… You can apply extension functions to different data types +- โœ… You can write more expressive and maintainable code +- โœ… You're ready to extend any class with new functionality! + +**Keep practicing and happy coding! ๐ŸŽ‰** From ddbe157fd4c69463e570d818e6f17875e51e052c Mon Sep 17 00:00:00 2001 From: gpl-gowthamchand Date: 2025ๅนด8ๆœˆ24ๆ—ฅ 19:35:35 +0530 Subject: [PATCH 05/23] feat: Add comprehensive OOP documentation - Add Classes and Constructors guide with primary/secondary constructors - Add Inheritance guide with open classes and hierarchies - Include hands-on exercises for each topic - Provide clear learning paths and best practices - Cover advanced patterns and common pitfalls --- docs/oop/01-classes-constructors.md | 795 +++++++++++++++++++++++++ docs/oop/02-inheritance.md | 866 ++++++++++++++++++++++++++++ 2 files changed, 1661 insertions(+) create mode 100644 docs/oop/01-classes-constructors.md create mode 100644 docs/oop/02-inheritance.md diff --git a/docs/oop/01-classes-constructors.md b/docs/oop/01-classes-constructors.md new file mode 100644 index 0000000..d1b8638 --- /dev/null +++ b/docs/oop/01-classes-constructors.md @@ -0,0 +1,795 @@ +# ๐Ÿ—๏ธ Classes and Constructors in Kotlin + +Welcome to the world of Object-Oriented Programming in Kotlin! Classes are the building blocks of OOP, and constructors are how we create instances of these classes. Kotlin provides powerful and flexible ways to define classes with primary and secondary constructors, along with initialization blocks. + +## ๐Ÿ“‹ Learning Objectives + +By the end of this lesson, you will be able to: +- โœ… Create classes with properties and methods +- โœ… Use primary constructors effectively +- โœ… Implement secondary constructors +- โœ… Understand initialization blocks (init) +- โœ… Create and use class instances + +## ๐Ÿ” What You'll Learn + +- **Class declaration** - Basic class syntax and structure +- **Primary constructors** - Main way to initialize classes +- **Secondary constructors** - Alternative initialization methods +- **Init blocks** - Code that runs during object creation +- **Properties and methods** - Class members and behavior + +## ๐Ÿ“ Prerequisites + +- Basic understanding of functions +- Completed [Extension Functions](04-extension-functions.md) lesson +- Knowledge of variables and data types + +## ๐Ÿ’ป The Code + +Let's examine the classes and constructors example: + +```kotlin +class Student(var name: String) { + var id: Int = -1 + + init { + println("Student has got a name as $name and id is $id") + } + + constructor(n: String, id: Int): this(n) { + // The body of the secondary constructor is called after init block + this.id = id + } +} + +fun main(args: Array) { + var student = Student("Steve", 10) + println(student.id) +} +``` + +**๐Ÿ“ File:** [26_class_and_constructor.kt](src/26_class_and_constructor.kt) + +## ๐Ÿ” Code Breakdown + +### **Class Declaration** + +```kotlin +class Student(var name: String) { + // Class body +} +``` + +- **`class Student`**: Class declaration with name +- **`(var name: String)`**: Primary constructor parameter +- **`var name: String`**: Property declaration in constructor + +### **Properties** + +```kotlin +var id: Int = -1 +``` + +- **`var id: Int`**: Mutable property declaration +- **`= -1`**: Default value assignment + +### **Init Block** + +```kotlin +init { + println("Student has got a name as $name and id is $id") +} +``` + +- **`init`**: Initialization block keyword +- **Executes** during object creation +- **Access** to constructor parameters + +### **Secondary Constructor** + +```kotlin +constructor(n: String, id: Int): this(n) { + this.id = id +} +``` + +- **`constructor`**: Secondary constructor keyword +- **`: this(n)`**: Calls primary constructor +- **`this.id = id`**: Assigns to instance property + +## ๐ŸŽฏ Key Concepts Explained + +### **1. Class Declaration** + +#### **Basic Class Syntax** + +```kotlin +class ClassName { + // Properties + // Methods + // Constructors +} +``` + +**Components:** +- **`class`**: Keyword to declare a class +- **`ClassName`**: Name of the class (PascalCase convention) +- **`{ }`**: Class body containing members + +#### **Class with Primary Constructor** + +```kotlin +class Person( + val name: String, + var age: Int, + private val email: String +) { + // Class body +} +``` + +**Constructor Parameters:** +- **`val name: String`**: Immutable property (read-only) +- **`var age: Int`**: Mutable property (read-write) +- **`private val email: String`**: Private immutable property + +### **2. Primary Constructor** + +#### **Primary Constructor Syntax** + +```kotlin +class Rectangle( + val width: Double, + val height: Double +) { + // Class body +} +``` + +**Key Points:** +- **Primary constructor** is part of class header +- **Parameters** become class properties +- **No explicit constructor keyword** needed +- **Executes first** during object creation + +#### **Property Declarations in Constructor** + +```kotlin +class User( + val id: Int, // Immutable property + var name: String, // Mutable property + private var email: String, // Private mutable property + var isActive: Boolean = true // Property with default value +) { + // Class body +} +``` + +### **3. Secondary Constructors** + +#### **Secondary Constructor Syntax** + +```kotlin +class Person(val name: String) { + var age: Int = 0 + var email: String = "" + + // Secondary constructor + constructor(name: String, age: Int): this(name) { + this.age = age + } + + // Another secondary constructor + constructor(name: String, age: Int, email: String): this(name, age) { + this.email = email + } +} +``` + +**Key Points:** +- **`constructor`** keyword required +- **Must call primary constructor** with `this()` +- **Can have multiple** secondary constructors +- **Execute after** primary constructor and init blocks + +#### **Constructor Delegation** + +```kotlin +class Student(val name: String) { + var id: Int = -1 + var grade: String = "" + + // Secondary constructor calls primary + constructor(name: String, id: Int): this(name) { + this.id = id + } + + // Secondary constructor calls another secondary + constructor(name: String, id: Int, grade: String): this(name, id) { + this.grade = grade + } +} +``` + +### **4. Init Blocks** + +#### **Init Block Syntax** + +```kotlin +class BankAccount(val accountNumber: String) { + var balance: Double = 0.0 + var isActive: Boolean = false + + init { + // Validation logic + if (accountNumber.length < 8) { + throw IllegalArgumentException("Account number too short") + } + + // Initialize properties + isActive = true + println("Account $accountNumber created successfully") + } + + // Another init block + init { + println("Account balance initialized to $balance") + } +} +``` + +**Key Points:** +- **Multiple init blocks** allowed +- **Execute in order** they appear +- **Access to constructor parameters** +- **Run before** secondary constructor bodies + +#### **Init Block Execution Order** + +```kotlin +class Example(val param: String) { + init { + println("1. First init block") + } + + var property = "Property".also { println("2. Property initialization") } + + init { + println("3. Second init block") + } + + constructor(param: String, extra: Int): this(param) { + println("4. Secondary constructor body") + } +} + +// Execution order: 1, 2, 3, 4 +``` + +## ๐Ÿงช Hands-On Exercises + +### **Exercise 1: Basic Class with Primary Constructor** +Create a simple class for a Book: + +```kotlin +fun main(args: Array) { + // TODO: Create a Book class and use it + // Properties: title, author, year, isAvailable + // Methods: checkOut(), returnBook() +} +``` + +**Solution:** +```kotlin +class Book( + val title: String, + val author: String, + val year: Int, + var isAvailable: Boolean = true +) { + fun checkOut(): Boolean { + return if (isAvailable) { + isAvailable = false + println("'$title' has been checked out") + true + } else { + println("'$title' is already checked out") + false + } + } + + fun returnBook(): Boolean { + return if (!isAvailable) { + isAvailable = true + println("'$title' has been returned") + true + } else { + println("'$title' is already available") + false + } + } +} + +fun main(args: Array) { + val book = Book("The Kotlin Guide", "John Doe", 2023) + println("Book: ${book.title} by ${book.author} (${book.year})") + + book.checkOut() + book.checkOut() // Already checked out + book.returnBook() + book.returnBook() // Already returned +} +``` + +### **Exercise 2: Class with Secondary Constructor** +Create a Person class with multiple constructors: + +```kotlin +fun main(args: Array) { + // TODO: Create a Person class with primary and secondary constructors + // Primary: name, age + // Secondary: name, age, email + // Secondary: name, age, email, phone +} +``` + +**Solution:** +```kotlin +class Person(val name: String, var age: Int) { + var email: String = "" + var phone: String = "" + + init { + println("Person '$name' created with age $age") + } + + // Secondary constructor with email + constructor(name: String, age: Int, email: String): this(name, age) { + this.email = email + println("Email set to: $email") + } + + // Secondary constructor with email and phone + constructor(name: String, age: Int, email: String, phone: String): this(name, age, email) { + this.phone = phone + println("Phone set to: $phone") + } + + fun displayInfo() { + println("Name: $name, Age: $age") + if (email.isNotEmpty()) println("Email: $email") + if (phone.isNotEmpty()) println("Phone: $phone") + } +} + +fun main(args: Array) { + val person1 = Person("Alice", 25) + val person2 = Person("Bob", 30, "bob@email.com") + val person3 = Person("Charlie", 35, "charlie@email.com", "+1-555-0123") + + println("\nPerson 1:") + person1.displayInfo() + + println("\nPerson 2:") + person2.displayInfo() + + println("\nPerson 3:") + person3.displayInfo() +} +``` + +### **Exercise 3: Class with Init Blocks** +Create a BankAccount class with validation: + +```kotlin +fun main(args: Array) { + // TODO: Create a BankAccount class with init blocks for validation + // Properties: accountNumber, balance, owner + // Validation: account number length, positive balance +} +``` + +**Solution:** +```kotlin +class BankAccount( + val accountNumber: String, + var balance: Double, + val owner: String +) { + var isActive: Boolean = false + + init { + // Validate account number + if (accountNumber.length < 8) { + throw IllegalArgumentException("Account number must be at least 8 characters") + } + + // Validate balance + if (balance < 0) { + throw IllegalArgumentException("Initial balance cannot be negative") + } + + // Validate owner name + if (owner.trim().isEmpty()) { + throw IllegalArgumentException("Owner name cannot be empty") + } + + isActive = true + println("Account validation passed") + } + + init { + println("Account $accountNumber created for $owner with balance $${balance}") + } + + fun deposit(amount: Double): Boolean { + if (amount> 0) { + balance += amount + println("Deposited $${amount}. New balance: $${balance}") + return true + } else { + println("Invalid deposit amount") + return false + } + } + + fun withdraw(amount: Double): Boolean { + if (amount> 0 && amount <= balance) { + balance -= amount + println("Withdrew $${amount}. New balance: $${balance}") + return true + } else { + println("Invalid withdrawal amount or insufficient funds") + return false + } + } +} + +fun main(args: Array) { + try { + val account = BankAccount("12345678", 1000.0, "John Doe") + + account.deposit(500.0) + account.withdraw(200.0) + account.withdraw(2000.0) // Insufficient funds + + } catch (e: IllegalArgumentException) { + println("Error: ${e.message}") + } +} +``` + +### **Exercise 4: Complex Class with Multiple Constructors** +Create a Student class with comprehensive constructors: + +```kotlin +fun main(args: Array) { + // TODO: Create a Student class with multiple constructors and init blocks + // Properties: name, id, grade, subjects + // Different constructors for different scenarios +} +``` + +**Solution:** +```kotlin +class Student(val name: String) { + var id: Int = -1 + var grade: String = "Not Assigned" + var subjects: MutableList = mutableListOf() + + init { + println("Student '$name' created") + } + + // Secondary constructor with ID + constructor(name: String, id: Int): this(name) { + this.id = id + println("Student ID set to: $id") + } + + // Secondary constructor with ID and grade + constructor(name: String, id: Int, grade: String): this(name, id) { + this.grade = grade + println("Student grade set to: $grade") + } + + // Secondary constructor with ID, grade, and subjects + constructor(name: String, id: Int, grade: String, subjects: List): this(name, id, grade) { + this.subjects.addAll(subjects) + println("Student subjects set to: ${subjects.joinToString(", ")}") + } + + fun addSubject(subject: String) { + if (subject !in subjects) { + subjects.add(subject) + println("Subject '$subject' added") + } else { + println("Subject '$subject' already exists") + } + } + + fun removeSubject(subject: String) { + if (subjects.remove(subject)) { + println("Subject '$subject' removed") + } else { + println("Subject '$subject' not found") + } + } + + fun displayInfo() { + println("\nStudent Information:") + println("Name: $name") + println("ID: $id") + println("Grade: $grade") + println("Subjects: ${if (subjects.isEmpty()) "None" else subjects.joinToString(", ")}") + } +} + +fun main(args: Array) { + val student1 = Student("Alice") + val student2 = Student("Bob", 1001) + val student3 = Student("Charlie", 1002, "A") + val student4 = Student("Diana", 1003, "B", listOf("Math", "Science", "English")) + + student1.displayInfo() + student2.displayInfo() + student3.displayInfo() + student4.displayInfo() + + // Add subjects to students + student1.addSubject("History") + student2.addSubject("Math") + student2.addSubject("Math") // Duplicate + + student1.displayInfo() + student2.displayInfo() +} +``` + +## ๐Ÿ” Advanced Class Patterns + +### **1. Class with Companion Object** + +```kotlin +class DatabaseConnection private constructor( + val host: String, + val port: Int, + val database: String +) { + companion object { + fun create(host: String = "localhost", port: Int = 5432, database: String = "default"): DatabaseConnection { + return DatabaseConnection(host, port, database) + } + + fun createLocal(): DatabaseConnection = create() + fun createProduction(): DatabaseConnection = create("prod-server.com", 5432, "production") + } + + fun connect(): String = "Connected to $host:$port/$database" +} + +// Usage +val localDb = DatabaseConnection.createLocal() +val prodDb = DatabaseConnection.createProduction() +``` + +### **2. Class with Nested Classes** + +```kotlin +class Car(val brand: String, val model: String) { + class Engine(val type: String, val horsepower: Int) { + fun start(): String = "Starting $type engine with $horsepower HP" + } + + inner class Dashboard { + fun showInfo(): String = "Car: $brand $model" + } + + val engine = Engine("V8", 400) + val dashboard = Dashboard() +} + +// Usage +val car = Car("Toyota", "Camry") +println(car.engine.start()) +println(car.dashboard.showInfo()) +``` + +### **3. Class with Object Declaration** + +```kotlin +class Logger { + companion object { + private var logLevel = "INFO" + + fun setLogLevel(level: String) { + logLevel = level + } + + fun log(message: String) { + println("[$logLevel] $message") + } + } +} + +// Usage +Logger.setLogLevel("DEBUG") +Logger.log("Application started") +Logger.log("User logged in") +``` + +## ๐Ÿ” When to Use Each Constructor Type + +### **โœ… Use Primary Constructor When:** + +1. **Simple initialization**: + ```kotlin + class Point(val x: Double, val y: Double) + ``` + +2. **Most common use case**: + ```kotlin + class User(val username: String, var isActive: Boolean = true) + ``` + +3. **Immutable properties**: + ```kotlin + class Configuration(val apiKey: String, val baseUrl: String) + ``` + +### **โœ… Use Secondary Constructor When:** + +1. **Multiple initialization patterns**: + ```kotlin + class Person(val name: String) { + constructor(name: String, age: Int): this(name) + constructor(name: String, age: Int, email: String): this(name, age) + } + ``` + +2. **Complex initialization logic**: + ```kotlin + class DatabaseConnection(val url: String) { + constructor(host: String, port: Int, database: String): + this("jdbc:mysql://$host:$port/$database") + } + ``` + +3. **Factory methods**: + ```kotlin + class NetworkRequest(val url: String) { + constructor(host: String, path: String): + this("https://$host$path") + } + ``` + +## ๐Ÿšจ Common Mistakes to Avoid + +### **1. Forgetting to Call Primary Constructor** + +```kotlin +// โŒ Missing primary constructor call +class Student(val name: String) { + constructor(name: String, age: Int) { + // Missing this(name) call + } +} + +// โœ… Correct - calls primary constructor +class Student(val name: String) { + constructor(name: String, age: Int): this(name) { + // Constructor body + } +} +``` + +### **2. Accessing Properties Before Initialization** + +```kotlin +// โŒ Property accessed before initialization +class Example(val param: String) { + init { + println(property) // property not initialized yet + } + + val property = "value" +} + +// โœ… Correct order +class Example(val param: String) { + val property = "value" + + init { + println(property) // property is initialized + } +} +``` + +### **3. Complex Logic in Primary Constructor** + +```kotlin +// โŒ Too complex for primary constructor +class ComplexClass( + val name: String, + val age: Int, + val email: String = if (age>= 18) "$name@adult.com" else "$name@minor.com" +) + +// โœ… Better approach +class ComplexClass(val name: String, val age: Int) { + val email: String = if (age>= 18) "$name@adult.com" else "$name@minor.com" +} +``` + +## ๐Ÿ” Best Practices + +### **โœ… Do's** + +1. **Use primary constructor for simple cases**: + ```kotlin + class User(val name: String, var age: Int) + ``` + +2. **Keep constructors focused**: + ```kotlin + class Rectangle(val width: Double, val height: Double) + ``` + +3. **Use init blocks for validation**: + ```kotlin + class BankAccount(val accountNumber: String) { + init { + require(accountNumber.length>= 8) { "Account number too short" } + } + } + ``` + +### **โŒ Don'ts** + +1. **Don't create too many constructors**: + ```kotlin + // โŒ Too many constructors + class Person(val name: String) { + constructor(name: String, age: Int): this(name) + constructor(name: String, age: Int, email: String): this(name, age) + constructor(name: String, age: Int, email: String, phone: String): this(name, age, email) + constructor(name: String, age: Int, email: String, phone: String, address: String): this(name, age, email, phone) + } + ``` + +2. **Don't put complex logic in constructors**: + ```kotlin + // โŒ Complex logic in constructor + constructor(name: String, age: Int): this(name) { + if (age < 0) throw IllegalArgumentException("Age cannot be negative") + if (age> 150) throw IllegalArgumentException("Age too high") + this.age = age + } + ``` + +## ๐ŸŽฏ What's Next? + +Excellent! You've mastered classes and constructors in Kotlin. Now you're ready to: + +1. **Learn about inheritance** โ†’ [Inheritance](02-inheritance.md) +2. **Understand method overriding** โ†’ [Method Overriding](03-method-overriding.md) +3. **Explore abstract classes** โ†’ [Abstract Classes](04-abstract-classes.md) +4. **Master interfaces** โ†’ [Interfaces](05-interfaces.md) + +## ๐Ÿ“š Additional Resources + +- [Kotlin Classes](https://kotlinlang.org/docs/classes.html) +- [Constructors](https://kotlinlang.org/docs/classes.html#constructors) +- [Initialization](https://kotlinlang.org/docs/classes.html#creating-instances-of-classes) + +## ๐Ÿ† Summary + +- โœ… You can create classes with properties and methods +- โœ… You can use primary constructors effectively +- โœ… You can implement secondary constructors +- โœ… You understand initialization blocks (init) +- โœ… You can create and use class instances +- โœ… You're ready to build object-oriented applications! + +**Keep practicing and happy coding! ๐ŸŽ‰** diff --git a/docs/oop/02-inheritance.md b/docs/oop/02-inheritance.md new file mode 100644 index 0000000..0b2710e --- /dev/null +++ b/docs/oop/02-inheritance.md @@ -0,0 +1,866 @@ +# ๐Ÿงฌ Inheritance in Kotlin + +Welcome to the world of inheritance! Inheritance is a fundamental concept in Object-Oriented Programming that allows you to create a hierarchy of classes, where child classes inherit properties and methods from their parent classes. This promotes code reuse and establishes relationships between classes. + +## ๐Ÿ“‹ Learning Objectives + +By the end of this lesson, you will be able to: +- โœ… Understand the concept of inheritance +- โœ… Create parent and child classes +- โœ… Use the `open` keyword for inheritance +- โœ… Implement single inheritance properly +- โœ… Create class hierarchies + +## ๐Ÿ” What You'll Learn + +- **Inheritance basics** - Parent-child class relationships +- **Open classes** - Making classes inheritable +- **Single inheritance** - Kotlin's inheritance model +- **Class hierarchies** - Building inheritance chains +- **Code reuse** - Leveraging inherited functionality + +## ๐Ÿ“ Prerequisites + +- Basic understanding of classes and constructors +- Completed [Classes and Constructors](01-classes-constructors.md) lesson +- Knowledge of properties and methods + +## ๐Ÿ’ป The Code + +Let's examine the inheritance example: + +```kotlin +open class Animal { + var color: String = "" + + fun eat() { + println("Eat") + } +} + +class Dog : Animal() { + var bread: String = "" + + fun bark() { + println("Bark") + } +} + +class Cat : Animal() { + var age: Int = -1 + + fun meow() { + println("Meow") + } +} + +fun main(args: Array) { + var dog = Dog() + dog.bread = "labra" + dog.color = "black" + dog.bark() + dog.eat() + + var cat = Cat() + cat.age = 7 + cat.color = "brown" + cat.meow() + cat.eat() + + var animal = Animal() + animal.color = "white" + animal.eat() +} +``` + +**๐Ÿ“ File:** [27_inheritance.kt](src/27_inheritance.kt) + +## ๐Ÿ” Code Breakdown + +### **Parent Class (Base Class)** + +```kotlin +open class Animal { + var color: String = "" + + fun eat() { + println("Eat") + } +} +``` + +- **`open class Animal`**: `open` keyword makes class inheritable +- **`var color: String`**: Property that child classes will inherit +- **`fun eat()`**: Method that child classes will inherit + +### **Child Class (Derived Class)** + +```kotlin +class Dog : Animal() { + var bread: String = "" + + fun bark() { + println("Bark") + } +} +``` + +- **`class Dog : Animal()`**: `Dog` inherits from `Animal` +- **`: Animal()`**: Inheritance syntax with constructor call +- **`var bread: String`**: New property specific to `Dog` +- **`fun bark()`**: New method specific to `Dog` + +### **Using Inherited Members** + +```kotlin +var dog = Dog() +dog.bread = "labra" // Dog-specific property +dog.color = "black" // Inherited property +dog.bark() // Dog-specific method +dog.eat() // Inherited method +``` + +## ๐ŸŽฏ Key Concepts Explained + +### **1. Inheritance Basics** + +#### **What is Inheritance?** + +Inheritance is a mechanism that allows a class to inherit properties and methods from another class. The class that is inherited from is called the **parent class** (or base class, superclass), and the class that inherits is called the **child class** (or derived class, subclass). + +#### **Benefits of Inheritance** + +1. **Code Reuse**: Common functionality is defined once in the parent class +2. **Hierarchy**: Logical organization of related classes +3. **Polymorphism**: Child classes can be treated as parent class instances +4. **Maintainability**: Changes in parent class affect all child classes + +### **2. Open Classes** + +#### **The `open` Keyword** + +By default, all classes in Kotlin are **final** (cannot be inherited). To make a class inheritable, you must use the `open` keyword. + +```kotlin +// โŒ This class cannot be inherited (final by default) +class FinalClass { + // Class members +} + +// โœ… This class can be inherited +open class OpenClass { + // Class members +} + +// โœ… Child class can inherit from OpenClass +class ChildClass : OpenClass() { + // Inherits from OpenClass +} +``` + +#### **Open vs Final Classes** + +```kotlin +// Final class (default behavior) +class Vehicle { + var brand: String = "" + fun start() = println("Vehicle starting") +} + +// Open class (can be inherited) +open class Car { + var model: String = "" + open fun start() = println("Car starting") +} + +// Can inherit from Car +class ElectricCar : Car() { + var batteryLevel: Int = 100 + override fun start() = println("Electric car starting silently") +} + +// Cannot inherit from Vehicle +// class SportsCar : Vehicle() // Compilation error! +``` + +### **3. Single Inheritance** + +#### **Kotlin's Inheritance Model** + +Kotlin supports **single inheritance**, meaning a class can inherit from only one parent class. However, a class can implement multiple interfaces. + +```kotlin +open class Animal { + var name: String = "" +} + +open class LivingThing { + var isAlive: Boolean = true +} + +// โŒ Multiple inheritance not allowed +// class Dog : Animal(), LivingThing() // Compilation error! + +// โœ… Single inheritance +class Dog : Animal() { + var breed: String = "" +} + +// โœ… Multiple interface implementation +interface Movable { + fun move() +} + +interface Soundable { + fun makeSound() +} + +class Cat : Animal(), Movable, Soundable { + override fun move() = println("Cat is moving") + override fun makeSound() = println("Meow!") +} +``` + +### **4. Class Hierarchies** + +#### **Building Inheritance Chains** + +You can create deep inheritance hierarchies where classes inherit from other child classes. + +```kotlin +open class LivingThing { + var isAlive: Boolean = true +} + +open class Animal : LivingThing() { + var name: String = "" + var age: Int = 0 +} + +open class Mammal : Animal() { + var hasFur: Boolean = true + var isWarmBlooded: Boolean = true +} + +class Dog : Mammal() { + var breed: String = "" + var isDomestic: Boolean = true +} + +class Cat : Mammal() { + var isIndoor: Boolean = true + var huntingSkill: Int = 8 +} + +// Usage +val dog = Dog() +dog.isAlive = true // From LivingThing +dog.name = "Buddy" // From Animal +dog.hasFur = true // From Mammal +dog.breed = "Golden" // From Dog +``` + +## ๐Ÿงช Hands-On Exercises + +### **Exercise 1: Basic Inheritance** +Create a simple inheritance hierarchy for shapes: + +```kotlin +fun main(args: Array) { + // TODO: Create a Shape class and Rectangle/Circle classes that inherit from it + // Properties: color, area + // Methods: calculateArea(), displayInfo() +} +``` + +**Solution:** +```kotlin +open class Shape { + var color: String = "White" + + open fun calculateArea(): Double = 0.0 + + fun displayInfo() { + println("Shape color: $color, Area: ${calculateArea()}") + } +} + +class Rectangle : Shape() { + var width: Double = 0.0 + var height: Double = 0.0 + + override fun calculateArea(): Double = width * height + + fun setDimensions(w: Double, h: Double) { + width = w + height = h + } +} + +class Circle : Shape() { + var radius: Double = 0.0 + + override fun calculateArea(): Double = Math.PI * radius * radius + + fun setRadius(r: Double) { + radius = r + } +} + +fun main(args: Array) { + val rectangle = Rectangle() + rectangle.color = "Blue" + rectangle.setDimensions(5.0, 3.0) + rectangle.displayInfo() + + val circle = Circle() + circle.color = "Red" + circle.setRadius(4.0) + circle.displayInfo() +} +``` + +### **Exercise 2: Animal Hierarchy** +Create a more complex animal inheritance hierarchy: + +```kotlin +fun main(args: Array) { + // TODO: Create an Animal hierarchy with different types of animals + // Include properties and methods specific to each type +} +``` + +**Solution:** +```kotlin +open class Animal { + var name: String = "" + var age: Int = 0 + var weight: Double = 0.0 + + open fun makeSound() = println("Some animal sound") + + open fun move() = println("Moving") + + fun eat() = println("Eating") + + fun sleep() = println("Sleeping") +} + +open class Mammal : Animal() { + var hasFur: Boolean = true + var isWarmBlooded: Boolean = true + + override fun move() = println("Walking on land") +} + +open class Bird : Animal() { + var canFly: Boolean = true + var wingSpan: Double = 0.0 + + override fun move() = println("Flying") +} + +class Dog : Mammal() { + var breed: String = "" + var isDomestic: Boolean = true + + override fun makeSound() = println("Woof!") + + fun fetch() = println("Fetching ball") +} + +class Cat : Mammal() { + var isIndoor: Boolean = true + var huntingSkill: Int = 8 + + override fun makeSound() = println("Meow!") + + fun climb() = println("Climbing tree") +} + +class Eagle : Bird() { + var visionRange: Double = 5.0 + + override fun makeSound() = println("Screech!") + + fun hunt() = println("Hunting prey") +} + +fun main(args: Array) { + val dog = Dog() + dog.name = "Buddy" + dog.breed = "Golden Retriever" + dog.makeSound() + dog.move() + dog.fetch() + + val cat = Cat() + cat.name = "Whiskers" + cat.isIndoor = true + cat.makeSound() + cat.climb() + + val eagle = Eagle() + eagle.name = "Freedom" + eagle.canFly = true + eagle.makeSound() + eagle.move() + eagle.hunt() +} +``` + +### **Exercise 3: Employee Hierarchy** +Create an employee management system with inheritance: + +```kotlin +fun main(args: Array) { + // TODO: Create an Employee hierarchy with different types of employees + // Include salary calculation and role-specific methods +} +``` + +**Solution:** +```kotlin +open class Employee { + var id: Int = 0 + var name: String = "" + var email: String = "" + var baseSalary: Double = 0.0 + + open fun calculateSalary(): Double = baseSalary + + open fun getRole(): String = "Employee" + + fun displayInfo() { + println("ID: $id, Name: $name, Role: ${getRole()}, Salary: $${calculateSalary()}") + } +} + +class Developer : Employee() { + var programmingLanguage: String = "" + var experienceYears: Int = 0 + + override fun calculateSalary(): Double = baseSalary + (experienceYears * 1000) + + override fun getRole(): String = "Developer" + + fun code() = println("Coding in $programmingLanguage") + + fun debug() = println("Debugging code") +} + +class Manager : Employee() { + var teamSize: Int = 0 + var department: String = "" + + override fun calculateSalary(): Double = baseSalary + (teamSize * 500) + + override fun getRole(): String = "Manager" + + fun leadTeam() = println("Leading team of $teamSize people") + + fun conductMeeting() = println("Conducting team meeting") +} + +class SalesPerson : Employee() { + var salesTarget: Double = 0.0 + var commissionRate: Double = 0.05 + + override fun calculateSalary(): Double = baseSalary + (salesTarget * commissionRate) + + override fun getRole(): String = "Sales Person" + + fun makeSale(amount: Double) { + salesTarget += amount + println("Sale made: $${amount}") + } + + fun generateReport() = println("Generating sales report") +} + +fun main(args: Array) { + val developer = Developer() + developer.id = 1 + developer.name = "Alice" + developer.baseSalary = 60000.0 + developer.programmingLanguage = "Kotlin" + developer.experienceYears = 3 + developer.displayInfo() + developer.code() + + val manager = Manager() + manager.id = 2 + manager.name = "Bob" + manager.baseSalary = 80000.0 + manager.teamSize = 5 + manager.department = "Engineering" + manager.displayInfo() + manager.leadTeam() + + val salesPerson = SalesPerson() + salesPerson.id = 3 + salesPerson.name = "Charlie" + salesPerson.baseSalary = 50000.0 + salesPerson.salesTarget = 100000.0 + salesPerson.displayInfo() + salesPerson.makeSale(5000.0) + salesPerson.displayInfo() // Updated salary after sale +} +``` + +### **Exercise 4: Vehicle Hierarchy** +Create a vehicle management system: + +```kotlin +fun main(args: Array) { + // TODO: Create a Vehicle hierarchy with different types of vehicles + // Include fuel efficiency and maintenance methods +} +``` + +**Solution:** +```kotlin +open class Vehicle { + var brand: String = "" + var model: String = "" + var year: Int = 0 + var fuelType: String = "Gasoline" + + open fun startEngine() = println("Engine starting") + + open fun stopEngine() = println("Engine stopping") + + open fun getFuelEfficiency(): Double = 0.0 + + fun displayInfo() { + println("$year $brand $model ($fuelType)") + println("Fuel Efficiency: ${getFuelEfficiency()} MPG") + } +} + +open class Car : Vehicle() { + var numberOfDoors: Int = 4 + var transmissionType: String = "Automatic" + + override fun getFuelEfficiency(): Double = 25.0 + + fun openTrunk() = println("Opening trunk") +} + +open class Motorcycle : Vehicle() { + var engineSize: Int = 0 + var hasSidecar: Boolean = false + + override fun getFuelEfficiency(): Double = 45.0 + + fun wheelie() = println("Performing wheelie") +} + +class ElectricCar : Car() { + var batteryCapacity: Int = 0 + var range: Int = 0 + + init { + fuelType = "Electric" + } + + override fun getFuelEfficiency(): Double = 100.0 // MPGe + + override fun startEngine() = println("Electric motor starting silently") + + fun charge() = println("Charging battery") +} + +class SportBike : Motorcycle() { + var topSpeed: Int = 0 + + override fun getFuelEfficiency(): Double = 35.0 + + fun accelerate() = println("Accelerating to high speed") +} + +fun main(args: Array) { + val electricCar = ElectricCar() + electricCar.brand = "Tesla" + electricCar.model = "Model 3" + electricCar.year = 2023 + electricCar.batteryCapacity = 75 + electricCar.range = 350 + electricCar.displayInfo() + electricCar.startEngine() + electricCar.charge() + + val sportBike = SportBike() + sportBike.brand = "Yamaha" + sportBike.model = "R1" + sportBike.year = 2023 + sportBike.engineSize = 1000 + sportBike.topSpeed = 186 + sportBike.displayInfo() + sportBike.startEngine() + sportBike.accelerate() +} +``` + +## ๐Ÿ” Advanced Inheritance Patterns + +### **1. Constructor Inheritance** + +```kotlin +open class Animal(val name: String) { + var age: Int = 0 + + constructor(name: String, age: Int): this(name) { + this.age = age + } +} + +class Dog : Animal { + var breed: String = "" + + constructor(name: String, age: Int, breed: String): super(name, age) { + this.breed = breed + } +} + +// Usage +val dog = Dog("Buddy", 5, "Golden Retriever") +``` + +### **2. Abstract Classes with Inheritance** + +```kotlin +abstract class Shape { + abstract fun calculateArea(): Double + abstract fun calculatePerimeter(): Double + + fun displayInfo() { + println("Area: ${calculateArea()}, Perimeter: ${calculatePerimeter()}") + } +} + +class Rectangle(val width: Double, val height: Double) : Shape() { + override fun calculateArea(): Double = width * height + + override fun calculatePerimeter(): Double = 2 * (width + height) +} + +// Usage +val rectangle = Rectangle(5.0, 3.0) +rectangle.displayInfo() +``` + +### **3. Interface Implementation with Inheritance** + +```kotlin +interface Movable { + fun move() +} + +interface Soundable { + fun makeSound() +} + +open class Animal(val name: String) { + fun eat() = println("Eating") +} + +class Dog(name: String) : Animal(name), Movable, Soundable { + override fun move() = println("Walking") + override fun makeSound() = println("Woof!") +} + +// Usage +val dog = Dog("Buddy") +dog.eat() // From Animal +dog.move() // From Movable +dog.makeSound() // From Soundable +``` + +## ๐Ÿ” When to Use Inheritance + +### **โœ… Use Inheritance When:** + +1. **Code reuse**: + ```kotlin + open class DatabaseConnection { + fun connect() = println("Connecting to database") + fun disconnect() = println("Disconnecting from database") + } + + class MySQLConnection : DatabaseConnection() { + fun executeQuery() = println("Executing MySQL query") + } + ``` + +2. **Hierarchical relationships**: + ```kotlin + open class Shape + class Circle : Shape() + class Rectangle : Shape() + class Triangle : Shape() + ``` + +3. **Polymorphism**: + ```kotlin + open class Animal + class Dog : Animal() + class Cat : Animal() + + fun processAnimal(animal: Animal) { + // Can accept any Animal subclass + } + ``` + +### **โŒ Avoid Inheritance When:** + +1. **Composition is better**: + ```kotlin + // โŒ Don't inherit just for code reuse + class Car : Engine() // Car is not an Engine + + // โœ… Use composition instead + class Car { + private val engine = Engine() + fun start() = engine.start() + } + ``` + +2. **Multiple inheritance needed**: + ```kotlin + // โŒ Multiple inheritance not allowed + // class Dog : Animal(), Pet(), Guard() + + // โœ… Use interfaces instead + class Dog : Animal(), Pet, Guard + ``` + +## ๐Ÿšจ Common Mistakes to Avoid + +### **1. Forgetting the `open` Keyword** + +```kotlin +// โŒ Class cannot be inherited +class Animal { + var name: String = "" +} + +// โŒ Compilation error +class Dog : Animal() // Error: This type is final + +// โœ… Make class inheritable +open class Animal { + var name: String = "" +} + +class Dog : Animal() // โœ… Works +``` + +### **2. Deep Inheritance Hierarchies** + +```kotlin +// โŒ Too deep inheritance +open class A +open class B : A() +open class C : B() +open class D : C() +open class E : D() +class F : E() // Too many levels + +// โœ… Keep inheritance shallow +open class Animal +class Mammal : Animal() +class Dog : Mammal() // Only 2 levels deep +``` + +### **3. Inheriting for Implementation** + +```kotlin +// โŒ Inheriting just to reuse methods +class FileLogger : DatabaseConnection() // FileLogger is not a DatabaseConnection + +// โœ… Use composition instead +class FileLogger { + private val dbConnection = DatabaseConnection() + fun log(message: String) { + dbConnection.connect() + // Log to file + dbConnection.disconnect() + } +} +``` + +## ๐Ÿ” Best Practices + +### **โœ… Do's** + +1. **Use inheritance for "is-a" relationships**: + ```kotlin + // โœ… Dog is an Animal + open class Animal + class Dog : Animal() + ``` + +2. **Keep inheritance hierarchies shallow**: + ```kotlin + // โœ… Maximum 2-3 levels + open class Vehicle + class Car : Vehicle() + class ElectricCar : Car() + ``` + +3. **Use abstract classes for common behavior**: + ```kotlin + abstract class Shape { + abstract fun calculateArea(): Double + fun displayInfo() = println("Area: ${calculateArea()}") + } + ``` + +### **โŒ Don'ts** + +1. **Don't inherit for code reuse only**: + ```kotlin + // โŒ Bad inheritance + class Logger : DatabaseConnection() + + // โœ… Use composition + class Logger { + private val db = DatabaseConnection() + } + ``` + +2. **Don't create deep hierarchies**: + ```kotlin + // โŒ Too many levels + A -> B -> C -> D -> E -> F + + // โœ… Keep it shallow + A -> B -> C + ``` + +## ๐ŸŽฏ What's Next? + +Excellent! You've mastered inheritance in Kotlin. Now you're ready to: + +1. **Understand method overriding** โ†’ [Method Overriding](03-method-overriding.md) +2. **Explore abstract classes** โ†’ [Abstract Classes](04-abstract-classes.md) +3. **Master interfaces** โ†’ [Interfaces](05-interfaces.md) +4. **Learn about data classes** โ†’ [Data Classes](06-data-classes.md) + +## ๐Ÿ“š Additional Resources + +- [Kotlin Inheritance](https://kotlinlang.org/docs/inheritance.html) +- [Open Classes](https://kotlinlang.org/docs/inheritance.html#open-classes) +- [Class Hierarchies](https://kotlinlang.org/docs/inheritance.html#overriding-methods) + +## ๐Ÿ† Summary + +- โœ… You understand the concept of inheritance +- โœ… You can create parent and child classes +- โœ… You can use the `open` keyword for inheritance +- โœ… You can implement single inheritance properly +- โœ… You can create class hierarchies +- โœ… You're ready to build complex object-oriented systems! + +**Keep practicing and happy coding! ๐ŸŽ‰** From 587bebfe24cbb764c221ac7f4d004600b09f87c7 Mon Sep 17 00:00:00 2001 From: gpl-gowthamchand Date: 2025ๅนด8ๆœˆ24ๆ—ฅ 19:47:31 +0530 Subject: [PATCH 06/23] feat: Add method overriding and abstract classes documentation - Add Method Overriding guide with property and method overrides - Add Abstract Classes guide with abstract vs concrete members - Include hands-on exercises for each topic - Provide clear learning paths and best practices - Cover advanced patterns and common pitfalls --- docs/oop/03-method-overriding.md | 871 ++++++++++++++++++++++++++++ docs/oop/04-abstract-classes.md | 952 +++++++++++++++++++++++++++++++ 2 files changed, 1823 insertions(+) create mode 100644 docs/oop/03-method-overriding.md create mode 100644 docs/oop/04-abstract-classes.md diff --git a/docs/oop/03-method-overriding.md b/docs/oop/03-method-overriding.md new file mode 100644 index 0000000..4cb97a9 --- /dev/null +++ b/docs/oop/03-method-overriding.md @@ -0,0 +1,871 @@ +# ๐Ÿ”„ Method Overriding in Kotlin + +Welcome to the world of method overriding! Method overriding allows child classes to provide their own implementation of methods inherited from parent classes. This is a powerful feature that enables polymorphism and allows you to customize behavior while maintaining a consistent interface. + +## ๐Ÿ“‹ Learning Objectives + +By the end of this lesson, you will be able to: +- โœ… Override methods from parent classes +- โœ… Use the `override` keyword properly +- โœ… Call parent class methods with `super` +- โœ… Override properties and their accessors +- โœ… Understand the rules of method overriding + +## ๐Ÿ” What You'll Learn + +- **Method overriding basics** - How to override inherited methods +- **The `override` keyword** - Required for method overriding +- **Property overriding** - Overriding properties and accessors +- **Super calls** - Accessing parent class implementations +- **Override rules** - What can and cannot be overridden + +## ๐Ÿ“ Prerequisites + +- Basic understanding of inheritance +- Completed [Inheritance](02-inheritance.md) lesson +- Knowledge of classes, properties, and methods + +## ๐Ÿ’ป The Code + +Let's examine the method overriding example: + +```kotlin +open class MyAnimal { + open var color: String = "White" + + open fun eat() { + println("Animal Eating") + } +} + +class MyDog : MyAnimal() { + var bread: String = "" + + override var color: String = "Black" + + fun bark() { + println("Bark") + } + + override fun eat() { + super.eat() + println("Dog is eating") + } +} + +fun main(args: Array) { + var dog = MyDog() + println(dog.color) + dog.eat() +} +``` + +**๐Ÿ“ File:** [28_overriding_methods_properties.kt](src/28_overriding_methods_properties.kt) + +## ๐Ÿ” Code Breakdown + +### **Parent Class with Open Members** + +```kotlin +open class MyAnimal { + open var color: String = "White" + + open fun eat() { + println("Animal Eating") + } +} +``` + +- **`open class MyAnimal`**: Class that can be inherited from +- **`open var color: String`**: Property that can be overridden +- **`open fun eat()`**: Method that can be overridden + +### **Child Class with Overrides** + +```kotlin +class MyDog : MyAnimal() { + var bread: String = "" + + override var color: String = "Black" + + override fun eat() { + super.eat() + println("Dog is eating") + } +} +``` + +- **`override var color: String`**: Overrides parent's color property +- **`override fun eat()`**: Overrides parent's eat method +- **`super.eat()`**: Calls parent's eat method + +## ๐ŸŽฏ Key Concepts Explained + +### **1. Method Overriding Basics** + +#### **What is Method Overriding?** + +Method overriding allows a child class to provide a specific implementation of a method that is already defined in its parent class. The overridden method must have the same signature (name, parameters, and return type) as the parent method. + +#### **The `override` Keyword** + +In Kotlin, you must explicitly use the `override` keyword when overriding methods. This makes the code more readable and prevents accidental overrides. + +```kotlin +open class Parent { + open fun display() = println("Parent display") +} + +class Child : Parent() { + override fun display() = println("Child display") +} + +// Usage +val parent = Parent() +val child = Child() +parent.display() // "Parent display" +child.display() // "Child display" +``` + +### **2. Open vs Final Members** + +#### **Open Members (Can be Overridden)** + +```kotlin +open class Shape { + open var color: String = "White" + open fun calculateArea(): Double = 0.0 + open fun display() = println("Shape with color $color") +} +``` + +**Key Points:** +- **`open`** keyword allows overriding +- **Properties** can be overridden +- **Methods** can be overridden +- **Default behavior** is final (cannot be overridden) + +#### **Final Members (Cannot be Overridden)** + +```kotlin +open class Vehicle { + var brand: String = "" // Final by default + + fun start() = println("Starting vehicle") // Final by default + + open fun accelerate() = println("Accelerating") // Can be overridden +} + +class Car : Vehicle() { + // โŒ Cannot override final members + // override var brand: String = "Custom" // Error + // override fun start() = println("Custom start") // Error + + // โœ… Can override open members + override fun accelerate() = println("Car accelerating") +} +``` + +### **3. Property Overriding** + +#### **Overriding Properties** + +```kotlin +open class Animal { + open var name: String = "Unknown" + open var age: Int = 0 + open val species: String = "Animal" +} + +class Dog : Animal() { + override var name: String = "Dog" + override var age: Int = 1 + override val species: String = "Canine" +} + +// Usage +val dog = Dog() +println(dog.name) // "Dog" +println(dog.age) // 1 +println(dog.species) // "Canine" +``` + +#### **Overriding Property Accessors** + +```kotlin +open class Person { + open var name: String = "" + open val age: Int = 0 +} + +class Student : Person() { + private var _age: Int = 0 + + override var name: String = "" + get() = "Student: $field" + set(value) { + field = value.uppercase() + } + + override val age: Int + get() = _age + 1 // Always return age + 1 +} + +// Usage +val student = Student() +student.name = "john" +println(student.name) // "Student: JOHN" +student._age = 20 +println(student.age) // 21 +``` + +### **4. Method Overriding with Super Calls** + +#### **Calling Parent Methods** + +```kotlin +open class Animal { + open fun makeSound() = println("Some animal sound") + open fun move() = println("Moving") +} + +class Dog : Animal() { + override fun makeSound() { + super.makeSound() // Call parent's makeSound + println("Woof!") // Add dog-specific sound + } + + override fun move() { + println("Dog is walking") // Completely replace parent's move + } +} + +// Usage +val dog = Dog() +dog.makeSound() // "Some animal sound" then "Woof!" +dog.move() // "Dog is walking" +``` + +#### **Multiple Inheritance with Super Calls** + +```kotlin +interface A { + fun foo() = println("A.foo") +} + +interface B { + fun foo() = println("B.foo") +} + +class C : A, B { + override fun foo() { + super.foo() // Call A's foo + super.foo() // Call B's foo + println("C.foo") + } +} + +// Usage +val c = C() +c.foo() // "A.foo", "B.foo", "C.foo" +``` + +## ๐Ÿงช Hands-On Exercises + +### **Exercise 1: Basic Method Overriding** +Create a simple inheritance hierarchy with method overriding: + +```kotlin +fun main(args: Array) { + // TODO: Create a Vehicle class and Car/Motorcycle classes that override methods + // Methods to override: start(), stop(), getInfo() +} +``` + +**Solution:** +```kotlin +open class Vehicle { + var brand: String = "" + var model: String = "" + + open fun start() = println("Vehicle starting") + + open fun stop() = println("Vehicle stopping") + + open fun getInfo(): String = "$brand $model" +} + +class Car : Vehicle() { + var numberOfDoors: Int = 4 + + override fun start() = println("Car engine starting") + + override fun stop() = println("Car engine stopping") + + override fun getInfo(): String = "${super.getInfo()} (Car with $numberOfDoors doors)" +} + +class Motorcycle : Vehicle() { + var engineSize: Int = 0 + + override fun start() = println("Motorcycle engine starting") + + override fun stop() = println("Motorcycle engine stopping") + + override fun getInfo(): String = "${super.getInfo()} (Motorcycle with ${engineSize}cc engine)" +} + +fun main(args: Array) { + val car = Car() + car.brand = "Toyota" + car.model = "Camry" + car.start() + car.stop() + println(car.getInfo()) + + val motorcycle = Motorcycle() + motorcycle.brand = "Yamaha" + motorcycle.model = "R1" + motorcycle.engineSize = 1000 + motorcycle.start() + motorcycle.stop() + println(motorcycle.getInfo()) +} +``` + +### **Exercise 2: Property Overriding** +Create classes with property overriding: + +```kotlin +fun main(args: Array) { + // TODO: Create a BankAccount class and different account types with property overriding + // Override: interestRate, accountType, displayInfo() +} +``` + +**Solution:** +```kotlin +open class BankAccount { + open var accountNumber: String = "" + open var balance: Double = 0.0 + open val interestRate: Double = 0.01 + open val accountType: String = "Basic" + + open fun displayInfo() { + println("Account: $accountNumber") + println("Type: $accountType") + println("Balance: $${balance}") + println("Interest Rate: ${interestRate * 100}%") + } + + open fun calculateInterest(): Double = balance * interestRate +} + +class SavingsAccount : BankAccount() { + override val interestRate: Double = 0.025 + override val accountType: String = "Savings" + + override fun displayInfo() { + super.displayInfo() + println("Interest Earned: $${calculateInterest()}") + } +} + +class CheckingAccount : BankAccount() { + override val interestRate: Double = 0.005 + override val accountType: String = "Checking" + + override fun displayInfo() { + super.displayInfo() + println("No monthly fees") + } +} + +class PremiumAccount : BankAccount() { + override val interestRate: Double = 0.035 + override val accountType: String = "Premium" + + override fun displayInfo() { + super.displayInfo() + println("Premium benefits included") + println("24/7 customer support") + } +} + +fun main(args: Array) { + val savings = SavingsAccount() + savings.accountNumber = "SAV001" + savings.balance = 5000.0 + savings.displayInfo() + + println() + + val checking = CheckingAccount() + checking.accountNumber = "CHK001" + checking.balance = 2000.0 + checking.displayInfo() + + println() + + val premium = PremiumAccount() + premium.accountNumber = "PRE001" + premium.balance = 10000.0 + premium.displayInfo() +} +``` + +### **Exercise 3: Complex Method Overriding** +Create a shape hierarchy with method overriding: + +```kotlin +fun main(args: Array) { + // TODO: Create a Shape class with abstract methods and concrete implementations + // Override: calculateArea(), calculatePerimeter(), displayInfo() +} +``` + +**Solution:** +```kotlin +abstract class Shape { + open var color: String = "White" + + abstract fun calculateArea(): Double + + abstract fun calculatePerimeter(): Double + + open fun displayInfo() { + println("Shape: ${this::class.simpleName}") + println("Color: $color") + println("Area: ${calculateArea()}") + println("Perimeter: ${calculatePerimeter()}") + } +} + +class Rectangle : Shape() { + var width: Double = 0.0 + var height: Double = 0.0 + + override fun calculateArea(): Double = width * height + + override fun calculatePerimeter(): Double = 2 * (width + height) + + override fun displayInfo() { + super.displayInfo() + println("Width: $width, Height: $height") + } +} + +class Circle : Shape() { + var radius: Double = 0.0 + + override fun calculateArea(): Double = Math.PI * radius * radius + + override fun calculatePerimeter(): Double = 2 * Math.PI * radius + + override fun displayInfo() { + super.displayInfo() + println("Radius: $radius") + } +} + +class Triangle : Shape() { + var sideA: Double = 0.0 + var sideB: Double = 0.0 + var sideC: Double = 0.0 + + override fun calculateArea(): Double { + val s = (sideA + sideB + sideC) / 2 + return Math.sqrt(s * (s - sideA) * (s - sideB) * (s - sideC)) + } + + override fun calculatePerimeter(): Double = sideA + sideB + sideC + + override fun displayInfo() { + super.displayInfo() + println("Sides: $sideA, $sideB, $sideC") + } +} + +fun main(args: Array) { + val rectangle = Rectangle() + rectangle.color = "Blue" + rectangle.width = 5.0 + rectangle.height = 3.0 + rectangle.displayInfo() + + println() + + val circle = Circle() + circle.color = "Red" + circle.radius = 4.0 + circle.displayInfo() + + println() + + val triangle = Triangle() + triangle.color = "Green" + triangle.sideA = 3.0 + triangle.sideB = 4.0 + triangle.sideC = 5.0 + triangle.displayInfo() +} +``` + +### **Exercise 4: Employee Hierarchy with Overriding** +Create an employee system with method and property overriding: + +```kotlin +fun main(args: Array) { + // TODO: Create an Employee hierarchy with different roles and salary calculations + // Override: calculateSalary(), getRole(), displayInfo() +} +``` + +**Solution:** +```kotlin +open class Employee { + open var id: Int = 0 + open var name: String = "" + open var baseSalary: Double = 0.0 + + open fun calculateSalary(): Double = baseSalary + + open fun getRole(): String = "Employee" + + open fun displayInfo() { + println("ID: $id") + println("Name: $name") + println("Role: ${getRole()}") + println("Salary: $${calculateSalary()}") + } +} + +class Developer : Employee() { + var programmingLanguage: String = "" + var experienceYears: Int = 0 + + override fun calculateSalary(): Double = baseSalary + (experienceYears * 1000) + + override fun getRole(): String = "Developer" + + override fun displayInfo() { + super.displayInfo() + println("Language: $programmingLanguage") + println("Experience: $experienceYears years") + } +} + +class Manager : Employee() { + var teamSize: Int = 0 + var department: String = "" + + override fun calculateSalary(): Double = baseSalary + (teamSize * 500) + + override fun getRole(): String = "Manager" + + override fun displayInfo() { + super.displayInfo() + println("Team Size: $teamSize") + println("Department: $department") + } +} + +class SalesPerson : Employee() { + var salesTarget: Double = 0.0 + var commissionRate: Double = 0.05 + + override fun calculateSalary(): Double = baseSalary + (salesTarget * commissionRate) + + override fun getRole(): String = "Sales Person" + + override fun displayInfo() { + super.displayInfo() + println("Sales Target: $${salesTarget}") + println("Commission Rate: ${commissionRate * 100}%") + } +} + +fun main(args: Array) { + val developer = Developer() + developer.id = 1 + developer.name = "Alice" + developer.baseSalary = 60000.0 + developer.programmingLanguage = "Kotlin" + developer.experienceYears = 3 + developer.displayInfo() + + println() + + val manager = Manager() + manager.id = 2 + manager.name = "Bob" + manager.baseSalary = 80000.0 + manager.teamSize = 5 + manager.department = "Engineering" + manager.displayInfo() + + println() + + val salesPerson = SalesPerson() + salesPerson.id = 3 + salesPerson.name = "Charlie" + salesPerson.baseSalary = 50000.0 + salesPerson.salesTarget = 100000.0 + salesPerson.displayInfo() +} +``` + +## ๐Ÿ” Advanced Overriding Patterns + +### **1. Covariant Return Types** + +```kotlin +open class Animal { + open fun createOffspring(): Animal = Animal() +} + +class Dog : Animal() { + override fun createOffspring(): Dog = Dog() // Covariant return type +} + +// Usage +val dog = Dog() +val offspring = dog.createOffspring() // Type is Dog, not Animal +``` + +### **2. Overriding with Different Visibility** + +```kotlin +open class Parent { + protected open fun internalMethod() = println("Parent internal") +} + +class Child : Parent() { + public override fun internalMethod() = println("Child internal") // Can increase visibility +} + +// Usage +val child = Child() +child.internalMethod() // Now accessible from outside +``` + +### **3. Overriding with Default Parameters** + +```kotlin +open class Parent { + open fun greet(name: String = "World") = println("Hello, $name!") +} + +class Child : Parent() { + override fun greet(name: String) = println("Hi, $name!") // Default parameter not inherited +} + +// Usage +val child = Child() +child.greet() // Error: parameter required +child.greet("Alice") // "Hi, Alice!" +``` + +## ๐Ÿ” When to Use Method Overriding + +### **โœ… Use Method Overriding When:** + +1. **Customizing behavior**: + ```kotlin + open class Animal { + open fun makeSound() = println("Some sound") + } + + class Dog : Animal() { + override fun makeSound() = println("Woof!") // Custom sound + } + ``` + +2. **Extending functionality**: + ```kotlin + open class DatabaseConnection { + open fun connect() = println("Connecting") + } + + class MySQLConnection : DatabaseConnection() { + override fun connect() { + super.connect() // Call parent + println("MySQL specific connection") // Add more + } + } + ``` + +3. **Polymorphic behavior**: + ```kotlin + open class Shape { + open fun calculateArea(): Double = 0.0 + } + + class Circle : Shape() { + override fun calculateArea(): Double = Math.PI * radius * radius + } + + class Rectangle : Shape() { + override fun calculateArea(): Double = width * height + } + ``` + +### **โŒ Avoid Method Overriding When:** + +1. **Breaking the contract**: + ```kotlin + // โŒ Don't change expected behavior + open class FileReader { + open fun read(): String = "file content" + } + + class NetworkReader : FileReader() { + override fun read(): String = "network content" // Unexpected behavior + } + ``` + +2. **Overriding for side effects only**: + ```kotlin + // โŒ Don't override just for logging + open class Calculator { + open fun add(a: Int, b: Int): Int = a + b + } + + class LoggingCalculator : Calculator() { + override fun add(a: Int, b: Int): Int { + println("Adding $a + $b") // Just logging + return super.add(a, b) + } + } + ``` + +## ๐Ÿšจ Common Mistakes to Avoid + +### **1. Forgetting the `override` Keyword** + +```kotlin +open class Parent { + open fun method() = println("Parent") +} + +class Child : Parent() { + // โŒ Missing override keyword + fun method() = println("Child") // This creates a new method, doesn't override +} + +// Usage +val child = Child() +child.method() // "Child" (but this is not overriding) +``` + +### **2. Changing Method Signature** + +```kotlin +open class Parent { + open fun method(param: String) = println("Parent: $param") +} + +class Child : Parent() { + // โŒ Different signature + override fun method(param: Int) = println("Child: $param") // Error! +} +``` + +### **3. Overriding Final Members** + +```kotlin +open class Parent { + fun method() = println("Parent") // Final by default +} + +class Child : Parent() { + // โŒ Cannot override final method + override fun method() = println("Child") // Error! +} +``` + +## ๐Ÿ” Best Practices + +### **โœ… Do's** + +1. **Always use `override` keyword**: + ```kotlin + class Child : Parent() { + override fun method() = println("Child implementation") + } + ``` + +2. **Call parent methods when appropriate**: + ```kotlin + override fun method() { + super.method() // Call parent first + // Add child-specific behavior + } + ``` + +3. **Maintain the contract**: + ```kotlin + open class Database { + open fun connect(): Boolean = true + } + + class MySQLDatabase : Database() { + override fun connect(): Boolean { + // Always return Boolean as expected + return try { + // Connection logic + true + } catch (e: Exception) { + false + } + } + } + ``` + +### **โŒ Don'ts** + +1. **Don't override without good reason**: + ```kotlin + // โŒ Unnecessary override + class Child : Parent() { + override fun method() = super.method() // Just calls parent + } + ``` + +2. **Don't change method behavior unexpectedly**: + ```kotlin + // โŒ Unexpected behavior change + open class Calculator { + open fun add(a: Int, b: Int): Int = a + b + } + + class ChildCalculator : Calculator() { + override fun add(a: Int, b: Int): Int = a * b // Changed from add to multiply! + } + ``` + +## ๐ŸŽฏ What's Next? + +Excellent! You've mastered method overriding in Kotlin. Now you're ready to: + +1. **Explore abstract classes** โ†’ [Abstract Classes](04-abstract-classes.md) +2. **Master interfaces** โ†’ [Interfaces](05-interfaces.md) +3. **Learn about data classes** โ†’ [Data Classes](06-data-classes.md) +4. **Understand sealed classes** โ†’ [Sealed Classes](07-sealed-classes.md) + +## ๐Ÿ“š Additional Resources + +- [Kotlin Overriding](https://kotlinlang.org/docs/inheritance.html#overriding-methods) +- [Property Overriding](https://kotlinlang.org/docs/inheritance.html#overriding-properties) +- [Override Rules](https://kotlinlang.org/docs/inheritance.html#overriding-rules) + +## ๐Ÿ† Summary + +- โœ… You can override methods from parent classes +- โœ… You can use the `override` keyword properly +- โœ… You can call parent class methods with `super` +- โœ… You can override properties and their accessors +- โœ… You understand the rules of method overriding +- โœ… You're ready to create polymorphic class hierarchies! + +**Keep practicing and happy coding! ๐ŸŽ‰** diff --git a/docs/oop/04-abstract-classes.md b/docs/oop/04-abstract-classes.md new file mode 100644 index 0000000..fb4b5b5 --- /dev/null +++ b/docs/oop/04-abstract-classes.md @@ -0,0 +1,952 @@ +# ๐ŸŽญ Abstract Classes in Kotlin + +Welcome to the world of abstract classes! Abstract classes are a powerful feature in Object-Oriented Programming that allows you to create base classes that cannot be instantiated directly but provide a common interface and implementation for their subclasses. They're perfect for creating templates that child classes must implement. + +## ๐Ÿ“‹ Learning Objectives + +By the end of this lesson, you will be able to: +- โœ… Create abstract classes with abstract and concrete members +- โœ… Understand the difference between abstract and open classes +- โœ… Implement abstract methods in concrete subclasses +- โœ… Use abstract classes as base classes +- โœ… Create flexible class hierarchies + +## ๐Ÿ” What You'll Learn + +- **Abstract class basics** - How to define and use abstract classes +- **Abstract vs concrete members** - Understanding what must be implemented +- **Abstract class inheritance** - Creating concrete subclasses +- **Abstract class design patterns** - When and how to use abstract classes +- **Real-world applications** - Practical use cases + +## ๐Ÿ“ Prerequisites + +- Basic understanding of inheritance and method overriding +- Completed [Method Overriding](03-method-overriding.md) lesson +- Knowledge of classes, properties, and methods + +## ๐Ÿ’ป The Code + +Let's examine the abstract class example: + +```kotlin +abstract class MyPerson { + abstract var name: String + + abstract fun eat() + + open fun getHeight() {} + + fun goToSchool() {} +} + +class Indian: MyPerson() { + override var name: String = "dummy_indian_name" + + override fun eat() { + // Our own code + } +} + +fun main(args: Array) { + // var person = MyPerson() // Not allowed. You cannot create instance of abstract class + var person = Indian() // Allowed. Abstract Super class reference variable + // pointing to child class object. + person.name = "Steve" + person.eat() + person.goToSchool() +} +``` + +**๐Ÿ“ File:** [30_abstract_class.kt](src/30_abstract_class.kt) + +## ๐Ÿ” Code Breakdown + +### **Abstract Class Declaration** + +```kotlin +abstract class MyPerson { + abstract var name: String + + abstract fun eat() + + open fun getHeight() {} + + fun goToSchool() {} +} +``` + +- **`abstract class MyPerson`**: Abstract class declaration +- **`abstract var name: String`**: Abstract property that must be implemented +- **`abstract fun eat()`**: Abstract method that must be implemented +- **`open fun getHeight()`**: Open method that can be overridden +- **`fun goToSchool()`**: Concrete method that cannot be overridden + +### **Concrete Subclass Implementation** + +```kotlin +class Indian: MyPerson() { + override var name: String = "dummy_indian_name" + + override fun eat() { + // Our own code + } +} +``` + +- **`class Indian: MyPerson()`**: Concrete class inheriting from abstract class +- **`override var name: String`**: Implementation of abstract property +- **`override fun eat()`**: Implementation of abstract method + +## ๐ŸŽฏ Key Concepts Explained + +### **1. Abstract Class Basics** + +#### **What is an Abstract Class?** + +An abstract class is a class that cannot be instantiated directly. It serves as a base class for other classes and can contain both abstract and concrete members. Abstract classes are perfect for creating common functionality that subclasses can inherit and extend. + +#### **Abstract Class Characteristics** + +```kotlin +abstract class Shape { + abstract fun calculateArea(): Double // Must be implemented + abstract fun calculatePerimeter(): Double // Must be implemented + + fun displayInfo() { // Concrete method + println("Area: ${calculateArea()}") + println("Perimeter: ${calculatePerimeter()}") + } +} +``` + +**Key Points:** +- **Cannot be instantiated** directly +- **Can contain abstract members** (properties and methods) +- **Can contain concrete members** (implemented properties and methods) +- **Must be inherited** by concrete subclasses + +### **2. Abstract vs Concrete Members** + +#### **Abstract Members (Must be Implemented)** + +```kotlin +abstract class Animal { + abstract var name: String // Abstract property + abstract fun makeSound() // Abstract method + abstract fun move(): String // Abstract method with return type +} +``` + +**Abstract Member Rules:** +- **Must be implemented** in concrete subclasses +- **Cannot have implementation** in abstract class +- **Can have different access modifiers** (public, protected, private) +- **Properties can be abstract** (var or val) + +#### **Concrete Members (Already Implemented)** + +```kotlin +abstract class Vehicle { + var brand: String = "" // Concrete property + + fun start() = println("Starting") // Concrete method + + open fun accelerate() = println("Accelerating") // Open concrete method +} +``` + +**Concrete Member Rules:** +- **Already implemented** in abstract class +- **Can be inherited** by subclasses +- **Can be overridden** if marked as `open` +- **Provide default behavior** for subclasses + +### **3. Abstract Class Inheritance** + +#### **Creating Concrete Subclasses** + +```kotlin +abstract class Database { + abstract fun connect(): Boolean + abstract fun disconnect(): Boolean + abstract fun executeQuery(query: String): String + + fun isConnected(): Boolean = true // Concrete method +} + +class MySQLDatabase : Database() { + override fun connect(): Boolean { + println("Connecting to MySQL database") + return true + } + + override fun disconnect(): Boolean { + println("Disconnecting from MySQL database") + return true + } + + override fun executeQuery(query: String): String { + println("Executing MySQL query: $query") + return "MySQL result" + } +} + +class PostgreSQLDatabase : Database() { + override fun connect(): Boolean { + println("Connecting to PostgreSQL database") + return true + } + + override fun disconnect(): Boolean { + println("Disconnecting from PostgreSQL database") + return true + } + + override fun executeQuery(query: String): String { + println("Executing PostgreSQL query: $query") + return "PostgreSQL result" + } +} +``` + +#### **Using Abstract Classes as Types** + +```kotlin +fun processDatabase(database: Database) { + if (database.connect()) { + val result = database.executeQuery("SELECT * FROM users") + println("Query result: $result") + database.disconnect() + } +} + +// Usage +val mysql = MySQLDatabase() +val postgres = PostgreSQLDatabase() + +processDatabase(mysql) // Works with MySQL +processDatabase(postgres) // Works with PostgreSQL +``` + +### **4. Abstract Class Design Patterns** + +#### **Template Method Pattern** + +```kotlin +abstract class DataProcessor { + abstract fun readData(): String + abstract fun processData(data: String): String + abstract fun writeData(data: String): Boolean + + fun execute() { + val data = readData() + val processedData = processData(data) + val success = writeData(processedData) + + if (success) { + println("Data processing completed successfully") + } else { + println("Data processing failed") + } + } +} + +class FileProcessor : DataProcessor() { + override fun readData(): String = "Reading from file" + + override fun processData(data: String): String = "Processing: $data" + + override fun writeData(data: String): Boolean = true +} + +class NetworkProcessor : DataProcessor() { + override fun readData(): String = "Reading from network" + + override fun processData(data: String): String = "Processing: $data" + + override fun writeData(data: String): Boolean = true +} +``` + +## ๐Ÿงช Hands-On Exercises + +### **Exercise 1: Basic Abstract Class** +Create a simple abstract class for shapes: + +```kotlin +fun main(args: Array) { + // TODO: Create an abstract Shape class and concrete implementations + // Abstract methods: calculateArea(), calculatePerimeter() + // Concrete method: displayInfo() +} +``` + +**Solution:** +```kotlin +abstract class Shape { + abstract var color: String + + abstract fun calculateArea(): Double + + abstract fun calculatePerimeter(): Double + + fun displayInfo() { + println("Shape: ${this::class.simpleName}") + println("Color: $color") + println("Area: ${calculateArea()}") + println("Perimeter: ${calculatePerimeter()}") + } +} + +class Rectangle : Shape() { + override var color: String = "White" + var width: Double = 0.0 + var height: Double = 0.0 + + override fun calculateArea(): Double = width * height + + override fun calculatePerimeter(): Double = 2 * (width + height) +} + +class Circle : Shape() { + override var color: String = "White" + var radius: Double = 0.0 + + override fun calculateArea(): Double = Math.PI * radius * radius + + override fun calculatePerimeter(): Double = 2 * Math.PI * radius +} + +fun main(args: Array) { + val rectangle = Rectangle() + rectangle.color = "Blue" + rectangle.width = 5.0 + rectangle.height = 3.0 + rectangle.displayInfo() + + println() + + val circle = Circle() + circle.color = "Red" + circle.radius = 4.0 + circle.displayInfo() +} +``` + +### **Exercise 2: Abstract Class with Properties** +Create an abstract class for different types of accounts: + +```kotlin +fun main(args: Array) { + // TODO: Create an abstract BankAccount class with different account types + // Abstract properties: accountType, interestRate + // Abstract methods: calculateInterest(), displayInfo() +} +``` + +**Solution:** +```kotlin +abstract class BankAccount { + abstract val accountType: String + abstract val interestRate: Double + + var accountNumber: String = "" + var balance: Double = 0.0 + + abstract fun calculateInterest(): Double + + abstract fun displayInfo() + + fun deposit(amount: Double): Boolean { + if (amount> 0) { + balance += amount + println("Deposited $${amount}. New balance: $${balance}") + return true + } + return false + } + + fun withdraw(amount: Double): Boolean { + if (amount> 0 && amount <= balance) { + balance -= amount + println("Withdrew $${amount}. New balance: $${balance}") + return true + } + return false + } +} + +class SavingsAccount : BankAccount() { + override val accountType: String = "Savings" + override val interestRate: Double = 0.025 + + override fun calculateInterest(): Double = balance * interestRate + + override fun displayInfo() { + println("Account Type: $accountType") + println("Account Number: $accountNumber") + println("Balance: $${balance}") + println("Interest Rate: ${interestRate * 100}%") + println("Interest Earned: $${calculateInterest()}") + } +} + +class CheckingAccount : BankAccount() { + override val accountType: String = "Checking" + override val interestRate: Double = 0.005 + + override fun calculateInterest(): Double = balance * interestRate + + override fun displayInfo() { + println("Account Type: $accountType") + println("Account Number: $accountNumber") + println("Balance: $${balance}") + println("Interest Rate: ${interestRate * 100}%") + println("No monthly fees") + } +} + +fun main(args: Array) { + val savings = SavingsAccount() + savings.accountNumber = "SAV001" + savings.deposit(5000.0) + savings.displayInfo() + + println() + + val checking = CheckingAccount() + checking.accountNumber = "CHK001" + checking.deposit(2000.0) + checking.withdraw(500.0) + checking.displayInfo() +} +``` + +### **Exercise 3: Abstract Class with Template Method** +Create an abstract class for different data processors: + +```kotlin +fun main(args: Array) { + // TODO: Create an abstract DataProcessor class with template method pattern + // Abstract methods: readData(), processData(), writeData() + // Template method: execute() +} +``` + +**Solution:** +```kotlin +abstract class DataProcessor { + abstract fun readData(): String + abstract fun processData(data: String): String + abstract fun writeData(data: String): Boolean + + fun execute() { + println("Starting data processing...") + + val data = readData() + println("Data read: $data") + + val processedData = processData(data) + println("Data processed: $processedData") + + val success = writeData(processedData) + + if (success) { + println("Data processing completed successfully") + } else { + println("Data processing failed") + } + } +} + +class FileProcessor : DataProcessor() { + override fun readData(): String = "Reading data from file system" + + override fun processData(data: String): String = "File: $data" + + override fun writeData(data: String): Boolean { + println("Writing to file: $data") + return true + } +} + +class DatabaseProcessor : DataProcessor() { + override fun readData(): String = "Reading data from database" + + override fun processData(data: String): String = "Database: $data" + + override fun writeData(data: String): Boolean { + println("Writing to database: $data") + return true + } +} + +class NetworkProcessor : DataProcessor() { + override fun readData(): String = "Reading data from network" + + override fun processData(data: String): String = "Network: $data" + + override fun writeData(data: String): Boolean { + println("Writing to network: $data") + return true + } +} + +fun main(args: Array) { + val fileProcessor = FileProcessor() + fileProcessor.execute() + + println() + + val dbProcessor = DatabaseProcessor() + dbProcessor.execute() + + println() + + val networkProcessor = NetworkProcessor() + networkProcessor.execute() +} +``` + +### **Exercise 4: Complex Abstract Class Hierarchy** +Create an abstract class for different types of vehicles: + +```kotlin +fun main(args: Array) { + // TODO: Create an abstract Vehicle class with different vehicle types + // Abstract properties: fuelType, maxSpeed + // Abstract methods: startEngine(), stopEngine(), getFuelEfficiency() +} +``` + +**Solution:** +```kotlin +abstract class Vehicle { + abstract val fuelType: String + abstract val maxSpeed: Int + + var brand: String = "" + var model: String = "" + var year: Int = 0 + + abstract fun startEngine(): String + + abstract fun stopEngine(): String + + abstract fun getFuelEfficiency(): Double + + fun displayInfo() { + println("Vehicle: $year $brand $model") + println("Fuel Type: $fuelType") + println("Max Speed: $maxSpeed mph") + println("Fuel Efficiency: ${getFuelEfficiency()} MPG") + } + + fun accelerate() = println("Accelerating to $maxSpeed mph") +} + +class Car : Vehicle() { + override val fuelType: String = "Gasoline" + override val maxSpeed: Int = 120 + + var numberOfDoors: Int = 4 + + override fun startEngine(): String = "Car engine starting with key" + + override fun stopEngine(): String = "Car engine stopping" + + override fun getFuelEfficiency(): Double = 25.0 + + override fun displayInfo() { + super.displayInfo() + println("Number of Doors: $numberOfDoors") + } +} + +class ElectricCar : Vehicle() { + override val fuelType: String = "Electric" + override val maxSpeed: Int = 150 + + var batteryCapacity: Int = 75 + + override fun startEngine(): String = "Electric motor starting silently" + + override fun stopEngine(): String = "Electric motor stopping" + + override fun getFuelEfficiency(): Double = 100.0 // MPGe + + override fun displayInfo() { + super.displayInfo() + println("Battery Capacity: ${batteryCapacity} kWh") + } +} + +class Motorcycle : Vehicle() { + override val fuelType: String = "Gasoline" + override val maxSpeed: Int = 180 + + var engineSize: Int = 1000 + + override fun startEngine(): String = "Motorcycle engine starting" + + override fun stopEngine(): String = "Motorcycle engine stopping" + + override fun getFuelEfficiency(): Double = 45.0 + + override fun displayInfo() { + super.displayInfo() + println("Engine Size: ${engineSize}cc") + } +} + +fun main(args: Array) { + val car = Car() + car.brand = "Toyota" + car.model = "Camry" + car.year = 2023 + car.displayInfo() + println("Start: ${car.startEngine()}") + println("Stop: ${car.stopEngine()}") + + println() + + val electricCar = ElectricCar() + electricCar.brand = "Tesla" + electricCar.model = "Model 3" + electricCar.year = 2023 + electricCar.displayInfo() + println("Start: ${electricCar.startEngine()}") + println("Stop: ${electricCar.stopEngine()}") + + println() + + val motorcycle = Motorcycle() + motorcycle.brand = "Yamaha" + motorcycle.model = "R1" + motorcycle.year = 2023 + motorcycle.displayInfo() + println("Start: ${motorcycle.startEngine()}") + println("Stop: ${motorcycle.stopEngine()}") +} +``` + +## ๐Ÿ” Advanced Abstract Class Patterns + +### **1. Abstract Class with Companion Object** + +```kotlin +abstract class DatabaseConnection { + abstract fun connect(): Boolean + abstract fun disconnect(): Boolean + + companion object { + fun create(type: String): DatabaseConnection { + return when (type.lowercase()) { + "mysql" -> MySQLConnection() + "postgresql" -> PostgreSQLConnection() + else -> throw IllegalArgumentException("Unknown database type: $type") + } + } + } +} + +class MySQLConnection : DatabaseConnection() { + override fun connect(): Boolean = true + override fun disconnect(): Boolean = true +} + +class PostgreSQLConnection : DatabaseConnection() { + override fun connect(): Boolean = true + override fun disconnect(): Boolean = true +} + +// Usage +val mysql = DatabaseConnection.create("mysql") +val postgres = DatabaseConnection.create("postgresql") +``` + +### **2. Abstract Class with Sealed Classes** + +```kotlin +abstract class Result { + abstract fun isSuccess(): Boolean + abstract fun getOrNull(): T? + + sealed class Success(val data: T) : Result() { + override fun isSuccess(): Boolean = true + override fun getOrNull(): T = data + } + + sealed class Error(val message: String) : Result() { + override fun isSuccess(): Boolean = false + override fun getOrNull(): Nothing? = null + } +} + +// Usage +val success: Result = Result.Success("Data loaded successfully") +val error: Result = Result.Error("Failed to load data") + +println(success.isSuccess()) // true +println(error.isSuccess()) // false +``` + +### **3. Abstract Class with Generic Types** + +```kotlin +abstract class Repository { + abstract fun save(item: T): Boolean + abstract fun findById(id: String): T? + abstract fun delete(id: String): Boolean + + fun exists(id: String): Boolean = findById(id) != null + + fun saveIfNotExists(item: T, id: String): Boolean { + return if (!exists(id)) { + save(item) + } else { + false + } + } +} + +data class User(val id: String, val name: String, val email: String) + +class UserRepository : Repository() { + private val users = mutableMapOf() + + override fun save(item: User): Boolean { + users[item.id] = item + return true + } + + override fun findById(id: String): User? = users[id] + + override fun delete(id: String): Boolean { + return users.remove(id) != null + } +} + +// Usage +val userRepo = UserRepository() +val user = User("1", "John Doe", "john@example.com") +userRepo.save(user) +println(userRepo.exists("1")) // true +``` + +## ๐Ÿ” When to Use Abstract Classes + +### **โœ… Use Abstract Classes When:** + +1. **Common implementation needed**: + ```kotlin + abstract class Animal { + fun eat() = println("Eating") // Common behavior + abstract fun makeSound() // Must be implemented + } + ``` + +2. **Template method pattern**: + ```kotlin + abstract class DataProcessor { + fun execute() { // Template method + val data = readData() // Abstract + val processed = processData(data) // Abstract + writeData(processed) // Abstract + } + + abstract fun readData(): String + abstract fun processData(data: String): String + abstract fun writeData(data: String): Boolean + } + ``` + +3. **State sharing**: + ```kotlin + abstract class Connection { + protected var isConnected = false // Shared state + + abstract fun connect(): Boolean + abstract fun disconnect(): Boolean + + fun getStatus() = if (isConnected) "Connected" else "Disconnected" + } + ``` + +### **โŒ Avoid Abstract Classes When:** + +1. **Only interface needed**: + ```kotlin + // โŒ Don't use abstract class for pure interface + abstract class Movable { + abstract fun move() + abstract fun stop() + } + + // โœ… Use interface instead + interface Movable { + fun move() + fun stop() + } + ``` + +2. **Multiple inheritance needed**: + ```kotlin + // โŒ Abstract classes don't support multiple inheritance + // abstract class A + // abstract class B + // class C : A(), B() // Error! + + // โœ… Use interfaces for multiple inheritance + interface A + interface B + class C : A, B // Works! + ``` + +## ๐Ÿšจ Common Mistakes to Avoid + +### **1. Trying to Instantiate Abstract Classes** + +```kotlin +abstract class Animal { + abstract fun makeSound() +} + +// โŒ Cannot create instance of abstract class +val animal = Animal() // Compilation error! + +// โœ… Create instance of concrete subclass +class Dog : Animal() { + override fun makeSound() = println("Woof!") +} + +val dog = Dog() // Works! +``` + +### **2. Forgetting to Implement Abstract Members** + +```kotlin +abstract class Shape { + abstract fun calculateArea(): Double +} + +// โŒ Missing implementation +class Rectangle : Shape() { + // Error: must implement calculateArea +} + +// โœ… Correct implementation +class Rectangle : Shape() { + override fun calculateArea(): Double = 0.0 +} +``` + +### **3. Mixing Abstract and Final Members** + +```kotlin +abstract class Base { + abstract fun method1() // Must be implemented + + fun method2() = println("Final method") // Cannot be overridden +} + +class Derived : Base() { + override fun method1() = println("Implemented") + + // โŒ Cannot override final method + // override fun method2() = println("Custom") // Error! +} +``` + +## ๐Ÿ” Best Practices + +### **โœ… Do's** + +1. **Use abstract classes for common behavior**: + ```kotlin + abstract class Database { + fun connect() = println("Connecting") // Common behavior + abstract fun executeQuery(query: String): String // Must implement + } + ``` + +2. **Keep abstract methods focused**: + ```kotlin + abstract class Shape { + abstract fun calculateArea(): Double // Single responsibility + abstract fun calculatePerimeter(): Double // Single responsibility + } + ``` + +3. **Provide meaningful default implementations**: + ```kotlin + abstract class Animal { + open fun sleep() = println("Sleeping") // Default behavior + abstract fun makeSound() // Must implement + } + ``` + +### **โŒ Don'ts** + +1. **Don't create abstract classes with no abstract members**: + ```kotlin + // โŒ No abstract members - use regular class instead + abstract class Utility { + fun helper1() = println("Helper 1") + fun helper2() = println("Helper 2") + } + + // โœ… Regular class + class Utility { + fun helper1() = println("Helper 1") + fun helper2() = println("Helper 2") + } + ``` + +2. **Don't make everything abstract**: + ```kotlin + // โŒ Too many abstract members + abstract class Person { + abstract var name: String + abstract var age: Int + abstract var email: String + abstract fun walk() + abstract fun talk() + abstract fun eat() + } + + // โœ… Mix of abstract and concrete + abstract class Person { + abstract var name: String + var age: Int = 0 // Concrete with default + fun walk() = println("Walking") // Common behavior + abstract fun talk() // Must implement + } + ``` + +## ๐ŸŽฏ What's Next? + +Excellent! You've mastered abstract classes in Kotlin. Now you're ready to: + +1. **Master interfaces** โ†’ [Interfaces](05-interfaces.md) +2. **Learn about data classes** โ†’ [Data Classes](06-data-classes.md) +3. **Understand sealed classes** โ†’ [Sealed Classes](07-sealed-classes.md) +4. **Explore object declarations** โ†’ [Object Declarations](08-object-declarations.md) + +## ๐Ÿ“š Additional Resources + +- [Kotlin Abstract Classes](https://kotlinlang.org/docs/classes.html#abstract-classes) +- [Abstract Members](https://kotlinlang.org/docs/classes.html#abstract-members) +- [Abstract Class Design](https://kotlinlang.org/docs/classes.html#abstract-classes) + +## ๐Ÿ† Summary + +- โœ… You can create abstract classes with abstract and concrete members +- โœ… You understand the difference between abstract and open classes +- โœ… You can implement abstract methods in concrete subclasses +- โœ… You can use abstract classes as base classes +- โœ… You can create flexible class hierarchies +- โœ… You're ready to design robust object-oriented systems! + +**Keep practicing and happy coding! ๐ŸŽ‰** From 99aab77cf0d61e2c1d60d6e29d76a23fc3535b25 Mon Sep 17 00:00:00 2001 From: gpl-gowthamchand Date: 2025ๅนด8ๆœˆ25ๆ—ฅ 08:48:14 +0530 Subject: [PATCH 07/23] feat: Add comprehensive Kotlin tutorial documentation - Add collections documentation (arrays, lists, maps, sets, functional operations) - Add null safety documentation (null safety, lateinit, lazy) - Add functional programming documentation (lambdas, scope functions) - Add coroutines documentation (introduction, launch, async) - Update README with new documentation links - Improve existing documentation files This creates a complete Kotlin learning path from basics to advanced topics. --- docs/README.md | 1 + docs/collections/01-arrays.md | 270 +++++++ docs/collections/02-lists.md | 351 +++++++++ docs/collections/03-maps.md | 412 ++++++++++ docs/collections/04-sets.md | 385 +++++++++ docs/collections/05-filter-map-sorting.md | 500 ++++++++++++ docs/control-flow/04-while-loops.md | 1 + docs/control-flow/05-break-continue.md | 1 + docs/coroutines/01-introduction.md | 544 +++++++++++++ docs/coroutines/02-launch-async.md | 676 ++++++++++++++++ docs/functional-programming/01-lambdas.md | 514 ++++++++++++ .../02-scope-functions.md | 512 ++++++++++++ docs/functions/02-functions-as-expressions.md | 1 + docs/functions/03-named-parameters.md | 1 + docs/functions/04-extension-functions.md | 1 + docs/null-safety/01-null-safety.md | 443 +++++++++++ docs/null-safety/02-lateinit-lazy.md | 731 ++++++++++++++++++ docs/oop/01-classes-constructors.md | 1 + docs/oop/02-inheritance.md | 1 + docs/oop/03-method-overriding.md | 1 + docs/oop/04-abstract-classes.md | 1 + 21 files changed, 5348 insertions(+) create mode 100644 docs/collections/01-arrays.md create mode 100644 docs/collections/02-lists.md create mode 100644 docs/collections/03-maps.md create mode 100644 docs/collections/04-sets.md create mode 100644 docs/collections/05-filter-map-sorting.md create mode 100644 docs/coroutines/01-introduction.md create mode 100644 docs/coroutines/02-launch-async.md create mode 100644 docs/functional-programming/01-lambdas.md create mode 100644 docs/functional-programming/02-scope-functions.md create mode 100644 docs/null-safety/01-null-safety.md create mode 100644 docs/null-safety/02-lateinit-lazy.md diff --git a/docs/README.md b/docs/README.md index 77e107e..262b251 100644 --- a/docs/README.md +++ b/docs/README.md @@ -57,6 +57,7 @@ Welcome to the comprehensive Kotlin learning guide! This documentation is organi - [Lists](collections/02-lists.md) โ†’ [Code](src/41_list.kt) - [Maps and HashMaps](collections/03-maps.md) โ†’ [Code](src/42_map_hashmap.kt) - [Sets and HashSets](collections/04-sets.md) โ†’ [Code](src/43_set_hashset.kt) +- [Filter, Map, and Sorting](collections/05-filter-map-sorting.md) โ†’ [Code](src/44_filter_map_sorting.kt) #### **Null Safety** - [Null Safety](null-safety/01-null-safety.md) โ†’ [Code](src/46_null_safety.kt) diff --git a/docs/collections/01-arrays.md b/docs/collections/01-arrays.md new file mode 100644 index 0000000..95614c6 --- /dev/null +++ b/docs/collections/01-arrays.md @@ -0,0 +1,270 @@ +# ๐Ÿ“š Arrays in Kotlin + +Arrays are one of the fundamental collection types in Kotlin. They store multiple values of the same type in a fixed-size, ordered collection. + +## ๐Ÿ“‹ Learning Objectives + +By the end of this lesson, you will be able to: +- โœ… Create and initialize arrays in different ways +- โœ… Access and modify array elements +- โœ… Use array properties and methods +- โœ… Understand array limitations and when to use them +- โœ… Work with different data types in arrays + +## ๐Ÿ” What You'll Learn + +- **Array creation** - Different ways to create arrays +- **Element access** - How to read and write array elements +- **Array properties** - Size, indices, and other useful properties +- **Array methods** - Built-in functions for array manipulation +- **Type safety** - How Kotlin ensures type consistency + +## ๐Ÿ“ Prerequisites + +- Basic understanding of [variables and data types](../basics/02-variables-data-types.md) +- Knowledge of [control flow](../control-flow/01-if-expressions.md) +- Familiarity with [functions](../functions/01-functions-basics.md) + +## ๐Ÿ’ป The Code + +Let's examine the arrays example: + +**๐Ÿ“ File:** [40_arrays.kt](../src/40_arrays.kt) + +## ๐Ÿ” Code Breakdown + +### **1. Array Creation** + +#### **Using `arrayOf()`** +```kotlin +val numbers = arrayOf(1, 2, 3, 4, 5) +val names = arrayOf("Alice", "Bob", "Charlie") +val mixed = arrayOf(1, "Hello", 3.14, true) +``` + +#### **Using `Array()` Constructor** +```kotlin +val squares = Array(5) { i -> i * i } // [0, 1, 4, 9, 16] +val zeros = Array(3) { 0 } // [0, 0, 0] +``` + +#### **Type-Specific Arrays** +```kotlin +val intArray = intArrayOf(1, 2, 3, 4, 5) +val doubleArray = doubleArrayOf(1.1, 2.2, 3.3) +val charArray = charArrayOf('a', 'b', 'c') +``` + +### **2. Accessing Array Elements** + +#### **Index-based Access** +```kotlin +val numbers = arrayOf(10, 20, 30, 40, 50) +val first = numbers[0] // 10 +val third = numbers[2] // 30 +val last = numbers[4] // 50 +``` + +#### **Safe Access with `get()`** +```kotlin +val numbers = arrayOf(10, 20, 30, 40, 50) +val element = numbers.get(2) // 30 +``` + +### **3. Modifying Array Elements** + +```kotlin +val numbers = arrayOf(1, 2, 3, 4, 5) +numbers[0] = 100 // Direct assignment +numbers.set(1, 200) // Using set() method +// Result: [100, 200, 3, 4, 5] +``` + +### **4. Array Properties** + +```kotlin +val numbers = arrayOf(1, 2, 3, 4, 5) +println("Size: ${numbers.size}") // 5 +println("Indices: ${numbers.indices}") // 0..4 +println("Last index: ${numbers.lastIndex}") // 4 +``` + +### **5. Iterating Through Arrays** + +#### **Using for loop with indices** +```kotlin +val numbers = arrayOf(10, 20, 30, 40, 50) +for (i in numbers.indices) { + println("Index $i: ${numbers[i]}") +} +``` + +#### **Using for-each loop** +```kotlin +val numbers = arrayOf(10, 20, 30, 40, 50) +for (number in numbers) { + println("Number: $number") +} +``` + +#### **Using forEach function** +```kotlin +val numbers = arrayOf(10, 20, 30, 40, 50) +numbers.forEach { number -> + println("Number: $number") +} +``` + +## ๐ŸŽฏ Key Concepts Explained + +### **1. Array Characteristics** +- **Fixed Size**: Once created, array size cannot change +- **Ordered**: Elements maintain their position +- **Indexed**: Access elements using zero-based indices +- **Type Consistent**: All elements must be of the same type + +### **2. Array vs List** +```kotlin +// Array - fixed size, mutable +val array = arrayOf(1, 2, 3) +array[0] = 10 // โœ… Can modify + +// List - dynamic size, immutable by default +val list = listOf(1, 2, 3) +// list[0] = 10 // โŒ Compilation error +``` + +### **3. Array Bounds** +```kotlin +val numbers = arrayOf(1, 2, 3, 4, 5) +// Valid indices: 0, 1, 2, 3, 4 +// numbers[5] // โŒ ArrayIndexOutOfBoundsException +// numbers[-1] // โŒ ArrayIndexOutOfBoundsException +``` + +## ๐Ÿงช Running the Examples + +### **Step 1: Open the File** +1. Navigate to `src/40_arrays.kt` +2. Open it in IntelliJ IDEA + +### **Step 2: Run the Program** +1. Right-click on the file +2. Select "Run '40_arraysKt'" +3. Observe the output + +## ๐ŸŽฎ Hands-On Exercises + +### **Exercise 1: Create and Modify Arrays** +Create an array of your favorite colors and change the second color. + +**Solution:** +```kotlin +fun main() { + val colors = arrayOf("Red", "Blue", "Green", "Yellow") + println("Original: ${colors.joinToString()}") + + colors[1] = "Purple" + println("Modified: ${colors.joinToString()}") +} +``` + +### **Exercise 2: Find Maximum Value** +Write a function to find the maximum value in an array of numbers. + +**Solution:** +```kotlin +fun findMax(numbers: Array): Int { + var max = numbers[0] + for (i in 1 until numbers.size) { + if (numbers[i]> max) { + max = numbers[i] + } + } + return max +} + +fun main() { + val numbers = arrayOf(23, 45, 12, 67, 34) + println("Maximum: ${findMax(numbers)}") +} +``` + +### **Exercise 3: Reverse Array** +Create a function to reverse the elements of an array. + +**Solution:** +```kotlin +fun reverseArray(numbers: Array): Array { + val reversed = Array(numbers.size) { 0 } + for (i in numbers.indices) { + reversed[i] = numbers[numbers.lastIndex - i] + } + return reversed +} + +fun main() { + val numbers = arrayOf(1, 2, 3, 4, 5) + val reversed = reverseArray(numbers) + println("Original: ${numbers.joinToString()}") + println("Reversed: ${reversed.joinToString()}") +} +``` + +### **Exercise 4: Array Statistics** +Calculate the sum, average, and count of even numbers in an array. + +**Solution:** +```kotlin +fun analyzeArray(numbers: Array) { + val sum = numbers.sum() + val average = sum.toDouble() / numbers.size + val evenCount = numbers.count { it % 2 == 0 } + + println("Sum: $sum") + println("Average: $average") + println("Even numbers: $evenCount") +} + +fun main() { + val numbers = arrayOf(1, 2, 3, 4, 5, 6, 7, 8, 9, 10) + analyzeArray(numbers) +} +``` + +## ๐Ÿšจ Common Mistakes to Avoid + +1. **Array bounds error**: `array[array.size]` โŒ + - **Correct**: `array[array.lastIndex]` โœ… + +2. **Type mismatch**: `arrayOf(1, "hello", 3.14)` โŒ + - **Correct**: Use specific types or `Array` โœ… + +3. **Modifying immutable arrays**: `arrayOf(1, 2, 3)[0] = 10` โŒ + - **Correct**: Store in variable first โœ… + +4. **Forgetting zero-based indexing**: `array[1]` is second element โœ… + +## ๐ŸŽฏ What's Next? + +Now that you understand arrays, continue with: + +1. **Lists** โ†’ [Lists and Collections](02-lists.md) +2. **Maps** โ†’ [Maps and HashMaps](03-maps.md) +3. **Sets** โ†’ [Sets and HashSets](04-sets.md) +4. **Functional operations** โ†’ [Filter, Map, and Sorting](05-filter-map-sorting.md) + +## ๐Ÿ“š Additional Resources + +- [Official Kotlin Arrays Documentation](https://kotlinlang.org/docs/arrays.html) +- [Kotlin Collections Overview](https://kotlinlang.org/docs/collections-overview.html) +- [Array Methods Reference](https://kotlinlang.org/api/latest/jvm/stdlib/kotlin/-array/) + +## ๐Ÿ† Summary + +- โœ… You can create arrays using different methods +- โœ… You understand how to access and modify elements +- โœ… You know array properties and iteration methods +- โœ… You're ready to work with more complex collections! + +**Arrays are the foundation of collections in Kotlin. Master them well! ๐ŸŽ‰** diff --git a/docs/collections/02-lists.md b/docs/collections/02-lists.md new file mode 100644 index 0000000..d287d8f --- /dev/null +++ b/docs/collections/02-lists.md @@ -0,0 +1,351 @@ +# ๐Ÿ“š Lists in Kotlin + +Lists are one of the most commonly used collection types in Kotlin. Unlike arrays, lists can grow or shrink dynamically and provide many useful methods for manipulation. + +## ๐Ÿ“‹ Learning Objectives + +By the end of this lesson, you will be able to: +- โœ… Create and initialize lists in different ways +- โœ… Understand the difference between immutable and mutable lists +- โœ… Add, remove, and modify list elements +- โœ… Use list methods for searching, filtering, and transformation +- โœ… Work with list indices and ranges + +## ๐Ÿ” What You'll Learn + +- **List creation** - Different ways to create lists +- **Immutability vs Mutability** - Understanding `List` vs `MutableList` +- **Element manipulation** - Adding, removing, and updating elements +- **List methods** - Built-in functions for list operations +- **Functional operations** - Using lambdas with lists + +## ๐Ÿ“ Prerequisites + +- Understanding of [Arrays](01-arrays.md) +- Knowledge of [Functions](../functions/01-functions-basics.md) +- Familiarity with [Control Flow](../control-flow/01-if-expressions.md) + +## ๐Ÿ’ป The Code + +Let's examine the lists example: + +**๐Ÿ“ File:** [41_list.kt](../src/41_list.kt) + +## ๐Ÿ” Code Breakdown + +### **1. List Creation** + +#### **Immutable Lists with `listOf()`** +```kotlin +val numbers = listOf(1, 2, 3, 4, 5) +val names = listOf("Alice", "Bob", "Charlie") +val empty = listOf() // Empty list with explicit type +``` + +#### **Mutable Lists with `mutableListOf()`** +```kotlin +val mutableNumbers = mutableListOf(1, 2, 3, 4, 5) +val mutableNames = mutableListOf("Alice", "Bob", "Charlie") +``` + +#### **Using `List()` Constructor** +```kotlin +val squares = List(5) { i -> i * i } // [0, 1, 4, 9, 16] +val repeated = List(3) { "Hello" } // ["Hello", "Hello", "Hello"] +``` + +### **2. Accessing List Elements** + +#### **Index-based Access** +```kotlin +val numbers = listOf(10, 20, 30, 40, 50) +val first = numbers[0] // 10 +val third = numbers[2] // 30 +val last = numbers.last() // 50 +``` + +#### **Safe Access Methods** +```kotlin +val numbers = listOf(10, 20, 30, 40, 50) +val element = numbers.getOrNull(10) // null (safe access) +val elementWithDefault = numbers.getOrElse(10) { -1 } // -1 (default value) +``` + +### **3. Modifying Mutable Lists** + +#### **Adding Elements** +```kotlin +val numbers = mutableListOf(1, 2, 3) +numbers.add(4) // Add to end +numbers.add(0, 0) // Add at specific index +numbers.addAll(listOf(5, 6)) // Add multiple elements +``` + +#### **Removing Elements** +```kotlin +val numbers = mutableListOf(1, 2, 3, 4, 5) +numbers.remove(3) // Remove by value +numbers.removeAt(1) // Remove by index +numbers.removeAll { it % 2 == 0 } // Remove all even numbers +``` + +#### **Updating Elements** +```kotlin +val numbers = mutableListOf(1, 2, 3, 4, 5) +numbers[0] = 100 // Direct assignment +numbers.set(1, 200) // Using set() method +``` + +### **4. List Properties** + +```kotlin +val numbers = listOf(1, 2, 3, 4, 5) +println("Size: ${numbers.size}") // 5 +println("Is empty: ${numbers.isEmpty()}") // false +println("Is not empty: ${numbers.isNotEmpty()}") // true +println("First element: ${numbers.first()}") // 1 +println("Last element: ${numbers.last()}") // 5 +``` + +### **5. Iterating Through Lists** + +#### **Using for loop with indices** +```kotlin +val numbers = listOf(10, 20, 30, 40, 50) +for (i in numbers.indices) { + println("Index $i: ${numbers[i]}") +} +``` + +#### **Using for-each loop** +```kotlin +val numbers = listOf(10, 20, 30, 40, 50) +for (number in numbers) { + println("Number: $number") +} +``` + +#### **Using forEach function** +```kotlin +val numbers = listOf(10, 20, 30, 40, 50) +numbers.forEach { number -> + println("Number: $number") +} +``` + +#### **Using forEachIndexed** +```kotlin +val numbers = listOf(10, 20, 30, 40, 50) +numbers.forEachIndexed { index, number -> + println("Index $index: $number") +} +``` + +### **6. List Searching and Filtering** + +#### **Finding Elements** +```kotlin +val numbers = listOf(1, 2, 3, 4, 5, 6, 7, 8, 9, 10) +val firstEven = numbers.find { it % 2 == 0 } // 2 +val lastEven = numbers.findLast { it % 2 == 0 } // 10 +val hasEven = numbers.any { it % 2 == 0 } // true +val allEven = numbers.all { it % 2 == 0 } // false +``` + +#### **Filtering Elements** +```kotlin +val numbers = listOf(1, 2, 3, 4, 5, 6, 7, 8, 9, 10) +val evenNumbers = numbers.filter { it % 2 == 0 } // [2, 4, 6, 8, 10] +val oddNumbers = numbers.filterNot { it % 2 == 0 } // [1, 3, 5, 7, 9] +val firstThree = numbers.take(3) // [1, 2, 3] +val lastThree = numbers.takeLast(3) // [8, 9, 10] +``` + +### **7. List Transformation** + +#### **Mapping Elements** +```kotlin +val numbers = listOf(1, 2, 3, 4, 5) +val doubled = numbers.map { it * 2 } // [2, 4, 6, 8, 10] +val squared = numbers.map { it * it } // [1, 4, 9, 16, 25] +val withIndex = numbers.mapIndexed { index, value -> "$index: $value" } +``` + +#### **Flattening Lists** +```kotlin +val listOfLists = listOf( + listOf(1, 2, 3), + listOf(4, 5, 6), + listOf(7, 8, 9) +) +val flattened = listOfLists.flatten() // [1, 2, 3, 4, 5, 6, 7, 8, 9] +``` + +## ๐ŸŽฏ Key Concepts Explained + +### **1. Immutable vs Mutable Lists** +```kotlin +// Immutable List - cannot be modified +val immutableList = listOf(1, 2, 3) +// immutableList.add(4) // โŒ Compilation error + +// Mutable List - can be modified +val mutableList = mutableListOf(1, 2, 3) +mutableList.add(4) // โœ… Can modify +``` + +### **2. List vs Array** +```kotlin +// Array - fixed size, mutable +val array = arrayOf(1, 2, 3) +array[0] = 10 // โœ… Can modify + +// List - dynamic size, immutable by default +val list = listOf(1, 2, 3) +// list[0] = 10 // โŒ Compilation error + +// MutableList - dynamic size, mutable +val mutableList = mutableListOf(1, 2, 3) +mutableList[0] = 10 // โœ… Can modify +``` + +### **3. List Performance Characteristics** +- **Access by index**: O(1) - very fast +- **Search by value**: O(n) - linear time +- **Add/Remove at end**: O(1) - very fast +- **Add/Remove at beginning/middle**: O(n) - slower + +## ๐Ÿงช Running the Examples + +### **Step 1: Open the File** +1. Navigate to `src/41_list.kt` +2. Open it in IntelliJ IDEA + +### **Step 2: Run the Program** +1. Right-click on the file +2. Select "Run '41_listKt'" +3. Observe the output + +## ๐ŸŽฎ Hands-On Exercises + +### **Exercise 1: List Manipulation** +Create a mutable list of fruits and perform various operations. + +**Solution:** +```kotlin +fun main() { + val fruits = mutableListOf("Apple", "Banana", "Orange") + println("Original: $fruits") + + fruits.add("Grape") + fruits.add(0, "Strawberry") + fruits.remove("Banana") + + println("Modified: $fruits") + println("Size: ${fruits.size}") +} +``` + +### **Exercise 2: List Filtering and Mapping** +Create a list of numbers and perform filtering and mapping operations. + +**Solution:** +```kotlin +fun main() { + val numbers = listOf(1, 2, 3, 4, 5, 6, 7, 8, 9, 10) + + val evenSquares = numbers + .filter { it % 2 == 0 } + .map { it * it } + + println("Original: $numbers") + println("Even squares: $evenSquares") + println("Sum of even squares: ${evenSquares.sum()}") +} +``` + +### **Exercise 3: List Statistics** +Calculate various statistics for a list of numbers. + +**Solution:** +```kotlin +fun analyzeList(numbers: List) { + val count = numbers.size + val sum = numbers.sum() + val average = sum.toDouble() / count + val min = numbers.minOrNull() ?: 0 + val max = numbers.maxOrNull() ?: 0 + val evenCount = numbers.count { it % 2 == 0 } + val oddCount = numbers.count { it % 2 != 0 } + + println("Count: $count") + println("Sum: $sum") + println("Average: $average") + println("Min: $min, Max: $max") + println("Even: $evenCount, Odd: $oddCount") +} + +fun main() { + val numbers = listOf(23, 45, 12, 67, 34, 89, 56, 78) + analyzeList(numbers) +} +``` + +### **Exercise 4: List Transformation** +Transform a list of strings in various ways. + +**Solution:** +```kotlin +fun main() { + val words = listOf("hello", "world", "kotlin", "programming", "language") + + val capitalized = words.map { it.capitalize() } + val longWords = words.filter { it.length> 5 } + val wordLengths = words.map { it.length } + val totalLength = wordLengths.sum() + + println("Original: $words") + println("Capitalized: $capitalized") + println("Long words: $longWords") + println("Word lengths: $wordLengths") + println("Total length: $totalLength") +} +``` + +## ๐Ÿšจ Common Mistakes to Avoid + +1. **Modifying immutable lists**: `listOf(1, 2, 3).add(4)` โŒ + - **Correct**: Use `mutableListOf()` for mutable lists โœ… + +2. **Index out of bounds**: `list[list.size]` โŒ + - **Correct**: Use `list.lastIndex` or check bounds โœ… + +3. **Forgetting list is immutable by default**: Always check if you need `mutableListOf()` โœ… + +4. **Inefficient operations**: `list.removeAt(0)` for large lists โŒ + - **Better**: Use `LinkedList` for frequent insertions/removals โœ… + +## ๐ŸŽฏ What's Next? + +Now that you understand lists, continue with: + +1. **Maps** โ†’ [Maps and HashMaps](03-maps.md) +2. **Sets** โ†’ [Sets and HashSets](04-sets.md) +3. **Functional operations** โ†’ [Filter, Map, and Sorting](05-filter-map-sorting.md) +4. **Advanced collections** โ†’ [Collections Overview](../README.md#collections) + +## ๐Ÿ“š Additional Resources + +- [Official Kotlin Lists Documentation](https://kotlinlang.org/docs/collections-overview.html#list) +- [Kotlin Collections API](https://kotlinlang.org/api/latest/jvm/stdlib/kotlin.collections/) +- [List Methods Reference](https://kotlinlang.org/api/latest/jvm/stdlib/kotlin.collections/-list/) + +## ๐Ÿ† Summary + +- โœ… You can create both immutable and mutable lists +- โœ… You understand how to add, remove, and modify elements +- โœ… You know list properties and iteration methods +- โœ… You can use functional operations like filter, map, and find +- โœ… You're ready to work with more complex collections! + +**Lists are powerful and flexible collections in Kotlin. Use them wisely! ๐ŸŽ‰** diff --git a/docs/collections/03-maps.md b/docs/collections/03-maps.md new file mode 100644 index 0000000..73e7be5 --- /dev/null +++ b/docs/collections/03-maps.md @@ -0,0 +1,412 @@ +# ๐Ÿ“š Maps and HashMaps in Kotlin + +Maps are key-value pair collections that allow you to store and retrieve data using unique keys. They're essential for creating lookup tables, caches, and organizing related data. + +## ๐Ÿ“‹ Learning Objectives + +By the end of this lesson, you will be able to: +- โœ… Create and initialize maps in different ways +- โœ… Understand the difference between immutable and mutable maps +- โœ… Add, remove, and modify map entries +- โœ… Use map methods for searching, filtering, and transformation +- โœ… Work with map keys, values, and entries + +## ๐Ÿ” What You'll Learn + +- **Map creation** - Different ways to create maps +- **Key-value pairs** - Understanding map structure +- **Element manipulation** - Adding, removing, and updating entries +- **Map methods** - Built-in functions for map operations +- **Functional operations** - Using lambdas with maps + +## ๐Ÿ“ Prerequisites + +- Understanding of [Lists](02-lists.md) +- Knowledge of [Functions](../functions/01-functions-basics.md) +- Familiarity with [Control Flow](../control-flow/01-if-expressions.md) + +## ๐Ÿ’ป The Code + +Let's examine the maps example: + +**๐Ÿ“ File:** [42_map_hashmap.kt](../src/42_map_hashmap.kt) + +## ๐Ÿ” Code Breakdown + +### **1. Map Creation** + +#### **Immutable Maps with `mapOf()`** +```kotlin +val countries = mapOf( + "US" to "United States", + "UK" to "United Kingdom", + "CA" to "Canada" +) + +val scores = mapOf( + "Alice" to 95, + "Bob" to 87, + "Charlie" to 92 +) +``` + +#### **Mutable Maps with `mutableMapOf()`** +```kotlin +val mutableCountries = mutableMapOf( + "US" to "United States", + "UK" to "United Kingdom" +) + +val mutableScores = mutableMapOf() +``` + +#### **Using `HashMap()` Constructor** +```kotlin +val hashMap = HashMap() +hashMap["Alice"] = 95 +hashMap["Bob"] = 87 +``` + +### **2. Accessing Map Elements** + +#### **Using Square Brackets** +```kotlin +val countries = mapOf("US" to "United States", "UK" to "United Kingdom") +val usName = countries["US"] // "United States" +val caName = countries["CA"] // null (key doesn't exist) +``` + +#### **Safe Access Methods** +```kotlin +val countries = mapOf("US" to "United States", "UK" to "United Kingdom") +val usName = countries.get("US") // "United States" +val caName = countries.getOrDefault("CA", "Unknown") // "Unknown" +val frName = countries.getOrElse("FR") { "France" } // "France" +``` + +### **3. Modifying Mutable Maps** + +#### **Adding and Updating Entries** +```kotlin +val scores = mutableMapOf() +scores["Alice"] = 95 // Add new entry +scores["Bob"] = 87 // Add new entry +scores["Alice"] = 98 // Update existing entry +scores.put("Charlie", 92) // Using put() method +``` + +#### **Removing Entries** +```kotlin +val scores = mutableMapOf("Alice" to 95, "Bob" to 87, "Charlie" to 92) +scores.remove("Bob") // Remove by key +scores.remove("Alice", 95) // Remove by key-value pair +scores.clear() // Remove all entries +``` + +### **4. Map Properties** + +```kotlin +val countries = mapOf("US" to "United States", "UK" to "United Kingdom") +println("Size: ${countries.size}") // 2 +println("Is empty: ${countries.isEmpty()}") // false +println("Keys: ${countries.keys}") // [US, UK] +println("Values: ${countries.values}") // [United States, United Kingdom] +``` + +### **5. Iterating Through Maps** + +#### **Using for loop with entries** +```kotlin +val countries = mapOf("US" to "United States", "UK" to "United Kingdom") +for ((code, name) in countries) { + println("$code -> $name") +} +``` + +#### **Using forEach function** +```kotlin +val countries = mapOf("US" to "United States", "UK" to "United Kingdom") +countries.forEach { (code, name) -> + println("$code -> $name") +} +``` + +#### **Iterating over keys and values separately** +```kotlin +val countries = mapOf("US" to "United States", "UK" to "United Kingdom") + +// Iterate over keys +for (code in countries.keys) { + println("Country code: $code") +} + +// Iterate over values +for (name in countries.values) { + println("Country name: $name") +} +``` + +### **6. Map Searching and Filtering** + +#### **Finding Entries** +```kotlin +val scores = mapOf("Alice" to 95, "Bob" to 87, "Charlie" to 92) +val aliceScore = scores["Alice"] // 95 +val hasHighScore = scores.any { it.value>= 90 } // true +val allHighScores = scores.all { it.value>= 80 } // true +``` + +#### **Filtering Entries** +```kotlin +val scores = mapOf("Alice" to 95, "Bob" to 87, "Charlie" to 92, "David" to 78) +val highScores = scores.filter { it.value>= 90 } // {Alice=95, Charlie=92} +val lowScores = scores.filterNot { it.value>= 80 } // {David=78} +val topThree = scores.toList().sortedByDescending { it.second }.take(3).toMap() +``` + +### **7. Map Transformation** + +#### **Transforming Keys and Values** +```kotlin +val scores = mapOf("Alice" to 95, "Bob" to 87, "Charlie" to 92) + +// Transform values +val letterGrades = scores.mapValues { (_, score) -> + when { + score>= 90 -> "A" + score>= 80 -> "B" + score>= 70 -> "C" + else -> "D" + } +} + +// Transform keys +val upperCaseNames = scores.mapKeys { (name, _) -> name.uppercase() } +``` + +#### **Creating Lists from Maps** +```kotlin +val scores = mapOf("Alice" to 95, "Bob" to 87, "Charlie" to 92) + +val names = scores.keys.toList() // [Alice, Bob, Charlie] +val scoreValues = scores.values.toList() // [95, 87, 92] +val entries = scores.toList() // [(Alice, 95), (Bob, 87), (Charlie, 92)] +``` + +## ๐ŸŽฏ Key Concepts Explained + +### **1. Immutable vs Mutable Maps** +```kotlin +// Immutable Map - cannot be modified +val immutableMap = mapOf("A" to 1, "B" to 2) +// immutableMap["C"] = 3 // โŒ Compilation error + +// Mutable Map - can be modified +val mutableMap = mutableMapOf("A" to 1, "B" to 2) +mutableMap["C"] = 3 // โœ… Can modify +``` + +### **2. Map vs List vs Array** +```kotlin +// Array - indexed by position, fixed size +val array = arrayOf("Alice", "Bob", "Charlie") +val name = array[0] // "Alice" + +// List - indexed by position, dynamic size +val list = listOf("Alice", "Bob", "Charlie") +val name = list[0] // "Alice" + +// Map - indexed by key, dynamic size +val map = mapOf("first" to "Alice", "second" to "Bob") +val name = map["first"] // "Alice" +``` + +### **3. Map Performance Characteristics** +- **Access by key**: O(1) - very fast (average case) +- **Search by value**: O(n) - linear time +- **Add/Remove**: O(1) - very fast (average case) +- **Memory usage**: Higher than lists due to key storage + +### **4. Key Requirements** +```kotlin +// Keys must be unique +val scores = mutableMapOf() +scores["Alice"] = 95 +scores["Alice"] = 98 // Overwrites previous value + +// Keys must be immutable (or at least stable) +val badMap = mutableMapOf, String>() // โŒ Don't do this +val goodMap = mutableMapOf() // โœ… Do this +``` + +## ๐Ÿงช Running the Examples + +### **Step 1: Open the File** +1. Navigate to `src/42_map_hashmap.kt` +2. Open it in IntelliJ IDEA + +### **Step 2: Run the Program** +1. Right-click on the file +2. Select "Run '42_map_hashmapKt'" +3. Observe the output + +## ๐ŸŽฎ Hands-On Exercises + +### **Exercise 1: Student Grades** +Create a map to store student grades and perform various operations. + +**Solution:** +```kotlin +fun main() { + val grades = mutableMapOf() + + // Add grades + grades["Alice"] = 95 + grades["Bob"] = 87 + grades["Charlie"] = 92 + grades["David"] = 78 + + println("All grades: $grades") + + // Update a grade + grades["Bob"] = 89 + println("After update: $grades") + + // Remove a student + grades.remove("David") + println("After removal: $grades") + + // Find average grade + val average = grades.values.average() + println("Average grade: $average") +} +``` + +### **Exercise 2: Word Frequency Counter** +Count the frequency of words in a sentence. + +**Solution:** +```kotlin +fun countWords(sentence: String): Map { + val words = sentence.lowercase().split(" ") + val frequency = mutableMapOf() + + for (word in words) { + val cleanWord = word.trim('.', ',', '!', '?') + frequency[cleanWord] = frequency.getOrDefault(cleanWord, 0) + 1 + } + + return frequency +} + +fun main() { + val sentence = "Hello world! Hello Kotlin! Welcome to the world of Kotlin programming." + val wordCount = countWords(sentence) + + println("Word frequencies:") + wordCount.forEach { (word, count) -> + println("$word: $count") + } +} +``` + +### **Exercise 3: Map Filtering and Transformation** +Create a map of products and prices, then filter and transform it. + +**Solution:** +```kotlin +fun main() { + val products = mapOf( + "Laptop" to 999.99, + "Mouse" to 29.99, + "Keyboard" to 79.99, + "Monitor" to 299.99, + "Headphones" to 149.99 + ) + + // Find expensive products (>100ใƒ‰ใƒซ) + val expensive = products.filter { it.value> 100 } + println("Expensive products: $expensive") + + // Apply 20% discount + val discounted = products.mapValues { (_, price) -> price * 0.8 } + println("Discounted prices: $discounted") + + // Find total value + val total = products.values.sum() + println("Total value: $total") +} +``` + +### **Exercise 4: Nested Maps** +Create a nested map structure for organizing data. + +**Solution:** +```kotlin +fun main() { + val students = mapOf( + "Alice" to mapOf( + "age" to 20, + "major" to "Computer Science", + "grades" to mapOf("Math" to 95, "Physics" to 88, "Programming" to 92) + ), + "Bob" to mapOf( + "age" to 22, + "major" to "Mathematics", + "grades" to mapOf("Math" to 98, "Physics" to 85, "Programming" to 78) + ) + ) + + // Access nested data + val aliceAge = students["Alice"]?.get("age") + val bobMathGrade = students["Bob"]?.get("grades")?.get("Math") + + println("Alice's age: $aliceAge") + println("Bob's Math grade: $bobMathGrade") + + // Print all student information + students.forEach { (name, info) -> + println("\n$name:") + info.forEach { (key, value) -> + println(" $key: $value") + } + } +} +``` + +## ๐Ÿšจ Common Mistakes to Avoid + +1. **Accessing non-existent keys**: `map["key"]` might return `null` โŒ + - **Better**: Use `getOrDefault()` or `getOrElse()` โœ… + +2. **Modifying immutable maps**: `mapOf("A" to 1)["B"] = 2` โŒ + - **Correct**: Use `mutableMapOf()` for mutable maps โœ… + +3. **Using mutable objects as keys**: Keys should be immutable โœ… + +4. **Forgetting map keys are unique**: Adding same key overwrites value โœ… + +## ๐ŸŽฏ What's Next? + +Now that you understand maps, continue with: + +1. **Sets** โ†’ [Sets and HashSets](04-sets.md) +2. **Functional operations** โ†’ [Filter, Map, and Sorting](05-filter-map-sorting.md) +3. **Advanced collections** โ†’ [Collections Overview](../README.md#collections) +4. **Null safety** โ†’ [Null Safety](../null-safety/01-null-safety.md) + +## ๐Ÿ“š Additional Resources + +- [Official Kotlin Maps Documentation](https://kotlinlang.org/docs/collections-overview.html#map) +- [Kotlin Collections API](https://kotlinlang.org/api/latest/jvm/stdlib/kotlin.collections/) +- [Map Methods Reference](https://kotlinlang.org/api/latest/jvm/stdlib/kotlin.collections/-map/) + +## ๐Ÿ† Summary + +- โœ… You can create both immutable and mutable maps +- โœ… You understand how to add, remove, and modify entries +- โœ… You know map properties and iteration methods +- โœ… You can use functional operations like filter, map, and transform +- โœ… You're ready to work with sets and other collections! + +**Maps are powerful for organizing data by keys. Use them to create efficient lookups! ๐ŸŽ‰** diff --git a/docs/collections/04-sets.md b/docs/collections/04-sets.md new file mode 100644 index 0000000..0df6da8 --- /dev/null +++ b/docs/collections/04-sets.md @@ -0,0 +1,385 @@ +# ๐Ÿ“š Sets and HashSets in Kotlin + +Sets are collections that store unique elements without any specific order. They're perfect for removing duplicates, checking membership, and performing mathematical set operations like union, intersection, and difference. + +## ๐Ÿ“‹ Learning Objectives + +By the end of this lesson, you will be able to: +- โœ… Create and initialize sets in different ways +- โœ… Understand the difference between immutable and mutable sets +- โœ… Add, remove, and check for elements in sets +- โœ… Use set methods for mathematical operations +- โœ… Work with set properties and iteration + +## ๐Ÿ” What You'll Learn + +- **Set creation** - Different ways to create sets +- **Uniqueness** - How sets ensure no duplicate elements +- **Element manipulation** - Adding, removing, and checking elements +- **Set operations** - Union, intersection, difference, and subset operations +- **Functional operations** - Using lambdas with sets + +## ๐Ÿ“ Prerequisites + +- Understanding of [Maps](03-maps.md) +- Knowledge of [Functions](../functions/01-functions-basics.md) +- Familiarity with [Control Flow](../control-flow/01-if-expressions.md) + +## ๐Ÿ’ป The Code + +Let's examine the sets example: + +**๐Ÿ“ File:** [43_set_hashset.kt](../src/43_set_hashset.kt) + +## ๐Ÿ” Code Breakdown + +### **1. Set Creation** + +#### **Immutable Sets with `setOf()`** +```kotlin +val numbers = setOf(1, 2, 3, 4, 5) +val names = setOf("Alice", "Bob", "Charlie") +val empty = setOf() // Empty set with explicit type +``` + +#### **Mutable Sets with `mutableSetOf()`** +```kotlin +val mutableNumbers = mutableSetOf(1, 2, 3, 4, 5) +val mutableNames = mutableSetOf("Alice", "Bob", "Charlie") +``` + +#### **Using `HashSet()` Constructor** +```kotlin +val hashSet = HashSet() +hashSet.add("Alice") +hashSet.add("Bob") +``` + +### **2. Adding and Removing Elements** + +#### **Adding Elements** +```kotlin +val names = mutableSetOf() +names.add("Alice") // Add single element +names.addAll(listOf("Bob", "Charlie")) // Add multiple elements +names += "David" // Using += operator +``` + +#### **Removing Elements** +```kotlin +val names = mutableSetOf("Alice", "Bob", "Charlie", "David") +names.remove("Bob") // Remove by value +names.removeAll { it.startsWith("A") } // Remove all names starting with "A" +names.clear() // Remove all elements +``` + +### **3. Checking Set Membership** + +#### **Basic Membership Tests** +```kotlin +val numbers = setOf(1, 2, 3, 4, 5) +val hasThree = 3 in numbers // true +val hasTen = 10 in numbers // false +val notHasTen = 10 !in numbers // true +``` + +#### **Using Methods** +```kotlin +val numbers = setOf(1, 2, 3, 4, 5) +val containsThree = numbers.contains(3) // true +val containsTen = numbers.contains(10) // false +``` + +### **4. Set Properties** + +```kotlin +val numbers = setOf(1, 2, 3, 4, 5) +println("Size: ${numbers.size}") // 5 +println("Is empty: ${numbers.isEmpty()}") // false +println("Is not empty: ${numbers.isNotEmpty()}") // true +``` + +### **5. Iterating Through Sets** + +#### **Using for loop** +```kotlin +val names = setOf("Alice", "Bob", "Charlie") +for (name in names) { + println("Name: $name") +} +``` + +#### **Using forEach function** +```kotlin +val names = setOf("Alice", "Bob", "Charlie") +names.forEach { name -> + println("Name: $name") +} +``` + +#### **Using forEachIndexed** +```kotlin +val names = setOf("Alice", "Bob", "Charlie") +names.forEachIndexed { index, name -> + println("Index $index: $name") +} +``` + +### **6. Set Mathematical Operations** + +#### **Union (Combining Sets)** +```kotlin +val set1 = setOf(1, 2, 3) +val set2 = setOf(3, 4, 5) +val union = set1.union(set2) // [1, 2, 3, 4, 5] +val unionOperator = set1 + set2 // Same result using + operator +``` + +#### **Intersection (Common Elements)** +```kotlin +val set1 = setOf(1, 2, 3, 4) +val set2 = setOf(3, 4, 5, 6) +val intersection = set1.intersect(set2) // [3, 4] +``` + +#### **Difference (Elements in First Set but Not in Second)** +```kotlin +val set1 = setOf(1, 2, 3, 4) +val set2 = setOf(3, 4, 5, 6) +val difference = set1.subtract(set2) // [1, 2] +``` + +#### **Symmetric Difference (Elements in Either Set but Not Both)** +```kotlin +val set1 = setOf(1, 2, 3, 4) +val set2 = setOf(3, 4, 5, 6) +val symmetricDiff = set1.union(set2).subtract(set1.intersect(set2)) // [1, 2, 5, 6] +``` + +### **7. Set Relationships** + +#### **Subset and Superset** +```kotlin +val set1 = setOf(1, 2, 3) +val set2 = setOf(1, 2, 3, 4, 5) +val isSubset = set1.all { it in set2 } // true +val isSuperset = set2.all { it in set1 } // false +``` + +#### **Disjoint Sets (No Common Elements)** +```kotlin +val set1 = setOf(1, 2, 3) +val set2 = setOf(4, 5, 6) +val areDisjoint = set1.none { it in set2 } // true +``` + +### **8. Set Filtering and Transformation** + +#### **Filtering Elements** +```kotlin +val numbers = setOf(1, 2, 3, 4, 5, 6, 7, 8, 9, 10) +val evenNumbers = numbers.filter { it % 2 == 0 } // [2, 4, 6, 8, 10] +val oddNumbers = numbers.filterNot { it % 2 == 0 } // [1, 3, 5, 7, 9] +``` + +#### **Transforming Elements** +```kotlin +val numbers = setOf(1, 2, 3, 4, 5) +val doubled = numbers.map { it * 2 } // [2, 4, 6, 8, 10] +val squared = numbers.map { it * it } // [1, 4, 9, 16, 25] +``` + +## ๐ŸŽฏ Key Concepts Explained + +### **1. Uniqueness Guarantee** +```kotlin +val numbers = mutableSetOf(1, 2, 3, 3, 4, 4, 5) +println(numbers) // [1, 2, 3, 4, 5] - duplicates automatically removed +``` + +### **2. Ordering** +```kotlin +// Sets don't guarantee order +val set1 = setOf(3, 1, 4, 1, 5, 9) +val set2 = setOf(9, 5, 1, 4, 3, 1) +println(set1 == set2) // true - order doesn't matter for equality +``` + +### **3. Set vs List vs Array** +```kotlin +// Array - indexed, can have duplicates, fixed size +val array = arrayOf(1, 2, 2, 3) +array[0] = 10 // โœ… Can modify + +// List - indexed, can have duplicates, dynamic size +val list = listOf(1, 2, 2, 3) +// list[0] = 10 // โŒ Compilation error + +// Set - no index, no duplicates, dynamic size +val set = setOf(1, 2, 3) // Note: 2 appears only once +// set[0] = 10 // โŒ No indexing +``` + +### **4. Performance Characteristics** +- **Add/Remove**: O(1) - very fast (average case) +- **Contains check**: O(1) - very fast (average case) +- **Iteration**: O(n) - linear time +- **Memory usage**: Similar to lists + +## ๐Ÿงช Running the Examples + +### **Step 1: Open the File** +1. Navigate to `src/43_set_hashset.kt` +2. Open it in IntelliJ IDEA + +### **Step 2: Run the Program** +1. Right-click on the file +2. Select "Run '43_set_hashsetKt'" +3. Observe the output + +## ๐ŸŽฎ Hands-On Exercises + +### **Exercise 1: Unique Word Counter** +Create a set to count unique words in a sentence. + +**Solution:** +```kotlin +fun main() { + val sentence = "Hello world! Hello Kotlin! Welcome to the world of Kotlin programming." + val words = sentence.lowercase().split(" ") + val uniqueWords = words.toSet() + + println("All words: $words") + println("Unique words: $uniqueWords") + println("Total words: ${words.size}") + println("Unique word count: ${uniqueWords.size}") + println("Duplicate words: ${words.size - uniqueWords.size}") +} +``` + +### **Exercise 2: Set Operations** +Perform various set operations on two sets of numbers. + +**Solution:** +```kotlin +fun main() { + val setA = setOf(1, 2, 3, 4, 5) + val setB = setOf(4, 5, 6, 7, 8) + + println("Set A: $setA") + println("Set B: $setB") + println("Union: ${setA.union(setB)}") + println("Intersection: ${setA.intersect(setB)}") + println("A - B: ${setA.subtract(setB)}") + println("B - A: ${setB.subtract(setA)}") + println("Symmetric difference: ${setA.union(setB).subtract(setA.intersect(setB))}") +} +``` + +### **Exercise 3: Student Course Registration** +Manage student course registrations using sets. + +**Solution:** +```kotlin +fun main() { + val mathStudents = mutableSetOf("Alice", "Bob", "Charlie") + val physicsStudents = mutableSetOf("Bob", "David", "Eve") + val programmingStudents = mutableSetOf("Alice", "Charlie", "Frank") + + println("Math students: $mathStudents") + println("Physics students: $physicsStudents") + println("Programming students: $programmingStudents") + + // Students taking multiple courses + val multiCourseStudents = mathStudents.intersect(physicsStudents) + .union(mathStudents.intersect(programmingStudents)) + .union(physicsStudents.intersect(programmingStudents)) + + println("Students taking multiple courses: $multiCourseStudents") + + // All unique students + val allStudents = mathStudents.union(physicsStudents).union(programmingStudents) + println("All students: $allStudents") + + // Students taking only one course + val singleCourseStudents = allStudents.subtract(multiCourseStudents) + println("Students taking only one course: $singleCourseStudents") +} +``` + +### **Exercise 4: Set Filtering and Analysis** +Create a set of numbers and perform various analyses. + +**Solution:** +```kotlin +fun analyzeNumberSet(numbers: Set) { + val evenNumbers = numbers.filter { it % 2 == 0 } + val oddNumbers = numbers.filter { it % 2 != 0 } + val primeNumbers = numbers.filter { isPrime(it) } + val perfectSquares = numbers.filter { isPerfectSquare(it) } + + println("Original set: $numbers") + println("Even numbers: $evenNumbers") + println("Odd numbers: $oddNumbers") + println("Prime numbers: $primeNumbers") + println("Perfect squares: $perfectSquares") + println("Sum: ${numbers.sum()}") + println("Average: ${numbers.average()}") +} + +fun isPrime(n: Int): Boolean { + if (n < 2) return false + for (i in 2 until n) { + if (n % i == 0) return false + } + return true +} + +fun isPerfectSquare(n: Int): Boolean { + val sqrt = kotlin.math.sqrt(n.toDouble()).toInt() + return sqrt * sqrt == n +} + +fun main() { + val numbers = setOf(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 16, 25) + analyzeNumberSet(numbers) +} +``` + +## ๐Ÿšจ Common Mistakes to Avoid + +1. **Expecting order**: Sets don't maintain insertion order โŒ + - **Correct**: Use `LinkedHashSet` if order matters โœ… + +2. **Using sets for indexed access**: Sets don't support indexing โŒ + - **Correct**: Use lists or arrays for indexed access โœ… + +3. **Forgetting uniqueness**: Adding duplicate elements has no effect โœ… + +4. **Modifying immutable sets**: `setOf(1, 2, 3).add(4)` โŒ + - **Correct**: Use `mutableSetOf()` for mutable sets โœ… + +## ๐ŸŽฏ What's Next? + +Now that you understand sets, continue with: + +1. **Functional operations** โ†’ [Filter, Map, and Sorting](05-filter-map-sorting.md) +2. **Advanced collections** โ†’ [Collections Overview](../README.md#collections) +3. **Null safety** โ†’ [Null Safety](../null-safety/01-null-safety.md) +4. **Functional programming** โ†’ [Functional Programming](../functional-programming/01-lambdas.md) + +## ๐Ÿ“š Additional Resources + +- [Official Kotlin Sets Documentation](https://kotlinlang.org/docs/collections-overview.html#set) +- [Kotlin Collections API](https://kotlinlang.org/api/latest/jvm/stdlib/kotlin.collections/) +- [Set Methods Reference](https://kotlinlang.org/api/latest/jvm/stdlib/kotlin.collections/-set/) + +## ๐Ÿ† Summary + +- โœ… You can create both immutable and mutable sets +- โœ… You understand how sets ensure uniqueness +- โœ… You know how to perform mathematical set operations +- โœ… You can use functional operations like filter and map +- โœ… You're ready to work with advanced collection operations! + +**Sets are perfect for managing unique collections and performing mathematical operations! ๐ŸŽ‰** diff --git a/docs/collections/05-filter-map-sorting.md b/docs/collections/05-filter-map-sorting.md new file mode 100644 index 0000000..114324a --- /dev/null +++ b/docs/collections/05-filter-map-sorting.md @@ -0,0 +1,500 @@ +# ๐Ÿ“š Filter, Map, and Sorting in Kotlin Collections + +Functional operations on collections allow you to transform, filter, and organize data in a clean and readable way. These operations are fundamental to functional programming and make your code more expressive and maintainable. + +## ๐Ÿ“‹ Learning Objectives + +By the end of this lesson, you will be able to: +- โœ… Use `filter` and `filterNot` to select elements based on conditions +- โœ… Transform collections with `map` and `mapIndexed` +- โœ… Sort collections using various sorting methods +- โœ… Chain multiple operations together for complex transformations +- โœ… Understand the difference between eager and lazy evaluation + +## ๐Ÿ” What You'll Learn + +- **Filtering operations** - Selecting elements based on predicates +- **Mapping operations** - Transforming elements from one form to another +- **Sorting operations** - Organizing elements in various orders +- **Chaining operations** - Combining multiple operations efficiently +- **Performance considerations** - Understanding when to use different approaches + +## ๐Ÿ“ Prerequisites + +- Understanding of [Lists](02-lists.md), [Maps](03-maps.md), and [Sets](04-sets.md) +- Knowledge of [Functions](../functions/01-functions-basics.md) +- Familiarity with [Lambdas](../functional-programming/01-lambdas.md) + +## ๐Ÿ’ป The Code + +Let's examine the functional operations examples: + +**๐Ÿ“ File:** [44_filter_map_sorting.kt](../src/44_filter_map_sorting.kt) + +## ๐Ÿ” Code Breakdown + +### **1. Filtering Operations** + +#### **Basic Filtering with `filter()`** +```kotlin +val numbers = listOf(1, 2, 3, 4, 5, 6, 7, 8, 9, 10) + +// Filter even numbers +val evenNumbers = numbers.filter { it % 2 == 0 } // [2, 4, 6, 8, 10] + +// Filter numbers greater than 5 +val largeNumbers = numbers.filter { it> 5 } // [6, 7, 8, 9, 10] + +// Filter numbers in a range +val rangeNumbers = numbers.filter { it in 3..7 } // [3, 4, 5, 6, 7] +``` + +#### **Filtering with `filterNot()`** +```kotlin +val numbers = listOf(1, 2, 3, 4, 5, 6, 7, 8, 9, 10) + +// Filter out even numbers (keep odd numbers) +val oddNumbers = numbers.filterNot { it % 2 == 0 } // [1, 3, 5, 7, 9] + +// Filter out numbers less than 5 +val highNumbers = numbers.filterNot { it < 5 } // [5, 6, 7, 8, 9, 10] +``` + +#### **Filtering with Index** +```kotlin +val names = listOf("Alice", "Bob", "Charlie", "David", "Eve") + +// Filter names with even indices +val evenIndexNames = names.filterIndexed { index, _ -> index % 2 == 0 } +// Result: ["Alice", "Charlie", "Eve"] + +// Filter names that start with a letter and have index> 1 +val filteredNames = names.filterIndexed { index, name -> + index> 1 && name.startsWith("C") +} +// Result: ["Charlie"] +``` + +#### **Filtering with Predicates** +```kotlin +val numbers = listOf(1, 2, 3, 4, 5, 6, 7, 8, 9, 10) + +// Using predefined predicates +val isEven = { n: Int -> n % 2 == 0 } +val isPositive = { n: Int -> n> 0 } +val isInRange = { n: Int -> n in 1..5 } + +val filtered = numbers.filter { isEven(it) && isPositive(it) && isInRange(it) } +// Result: [2, 4] +``` + +### **2. Mapping Operations** + +#### **Basic Mapping with `map()`** +```kotlin +val numbers = listOf(1, 2, 3, 4, 5) + +// Double each number +val doubled = numbers.map { it * 2 } // [2, 4, 6, 8, 10] + +// Square each number +val squared = numbers.map { it * it } // [1, 4, 9, 16, 25] + +// Convert to string +val strings = numbers.map { "Number: $it" } // ["Number: 1", "Number: 2", ...] +``` + +#### **Mapping with Index using `mapIndexed()`** +```kotlin +val names = listOf("Alice", "Bob", "Charlie") + +// Add index to each name +val indexedNames = names.mapIndexed { index, name -> + "${index + 1}. $name" +} +// Result: ["1. Alice", "2. Bob", "3. Charlie"] + +// Create pairs of index and name +val pairs = names.mapIndexed { index, name -> + index to name +} +// Result: [(0, "Alice"), (1, "Bob"), (2, "Charlie")] +``` + +#### **Mapping with Conditions** +```kotlin +val numbers = listOf(1, 2, 3, 4, 5, 6, 7, 8, 9, 10) + +// Map even numbers to their square, odd numbers to their double +val transformed = numbers.map { n -> + when { + n % 2 == 0 -> n * n // Even: square + else -> n * 2 // Odd: double + } +} +// Result: [2, 4, 6, 16, 10, 36, 14, 64, 18, 100] +``` + +### **3. Sorting Operations** + +#### **Basic Sorting with `sorted()`** +```kotlin +val numbers = listOf(3, 1, 4, 1, 5, 9, 2, 6) + +// Sort in ascending order +val ascending = numbers.sorted() // [1, 1, 2, 3, 4, 5, 6, 9] + +// Sort in descending order +val descending = numbers.sortedDescending() // [9, 6, 5, 4, 3, 2, 1, 1] +``` + +#### **Custom Sorting with `sortedBy()`** +```kotlin +val names = listOf("Alice", "Bob", "Charlie", "David", "Eve") + +// Sort by length +val byLength = names.sortedBy { it.length } // ["Bob", "Eve", "Alice", "David", "Charlie"] + +// Sort by length in descending order +val byLengthDesc = names.sortedByDescending { it.length } // ["Charlie", "David", "Alice", "Bob", "Eve"] + +// Sort by first letter +val byFirstLetter = names.sortedBy { it.first() } // ["Alice", "Bob", "Charlie", "David", "Eve"] +``` + +#### **Sorting with Multiple Criteria** +```kotlin +data class Person(val name: String, val age: Int, val city: String) + +val people = listOf( + Person("Alice", 25, "New York"), + Person("Bob", 30, "Boston"), + Person("Charlie", 25, "New York"), + Person("David", 30, "Chicago") +) + +// Sort by age first, then by name +val sortedPeople = people.sortedWith( + compareBy { it.age }.thenBy { it.name } +) +``` + +### **4. Chaining Operations** + +#### **Filter then Map** +```kotlin +val numbers = listOf(1, 2, 3, 4, 5, 6, 7, 8, 9, 10) + +// Filter even numbers, then square them +val evenSquares = numbers + .filter { it % 2 == 0 } + .map { it * it } +// Result: [4, 16, 36, 64, 100] +``` + +#### **Map then Filter** +```kotlin +val numbers = listOf(1, 2, 3, 4, 5) + +// Double numbers, then filter those greater than 5 +val doubledAndFiltered = numbers + .map { it * 2 } + .filter { it> 5 } +// Result: [6, 8, 10] +``` + +#### **Complex Chain** +```kotlin +val numbers = listOf(1, 2, 3, 4, 5, 6, 7, 8, 9, 10) + +val result = numbers + .filter { it % 2 == 0 } // Keep even numbers + .map { it * it } // Square them + .filter { it> 20 } // Keep squares> 20 + .sorted() // Sort in ascending order +// Result: [36, 64, 100] +``` + +### **5. Advanced Operations** + +#### **Flattening with `flatMap()`** +```kotlin +val words = listOf("hello", "world", "kotlin") + +// Split each word into characters and flatten +val characters = words.flatMap { it.toList() } +// Result: ['h', 'e', 'l', 'l', 'o', 'w', 'o', 'r', 'l', 'd', 'k', 'o', 't', 'l', 'i', 'n'] + +// Create all possible pairs +val pairs = words.flatMap { word1 -> + words.map { word2 -> "$word1-$word2" } +} +``` + +#### **Grouping with `groupBy()`** +```kotlin +val numbers = listOf(1, 2, 3, 4, 5, 6, 7, 8, 9, 10) + +// Group by even/odd +val groupedByParity = numbers.groupBy { it % 2 == 0 } +// Result: {false=[1, 3, 5, 7, 9], true=[2, 4, 6, 8, 10]} + +// Group by range +val groupedByRange = numbers.groupBy { + when { + it <= 3 -> "Small" + it <= 7 -> "Medium" + else -> "Large" + } +} +// Result: {Small=[1, 2, 3], Medium=[4, 5, 6, 7], Large=[8, 9, 10]} +``` + +#### **Partitioning with `partition()`** +```kotlin +val numbers = listOf(1, 2, 3, 4, 5, 6, 7, 8, 9, 10) + +// Partition into even and odd +val (even, odd) = numbers.partition { it % 2 == 0 } +// even: [2, 4, 6, 8, 10], odd: [1, 3, 5, 7, 9] + +// Partition into small and large +val (small, large) = numbers.partition { it <= 5 } +// small: [1, 2, 3, 4, 5], large: [6, 7, 8, 9, 10] +``` + +## ๐ŸŽฏ Key Concepts Explained + +### **1. Eager vs Lazy Evaluation** +```kotlin +val numbers = listOf(1, 2, 3, 4, 5) + +// Eager evaluation - operations are performed immediately +val eagerResult = numbers.filter { it % 2 == 0 }.map { it * it } + +// Lazy evaluation - operations are performed only when needed +val lazyResult = numbers.asSequence() + .filter { it % 2 == 0 } + .map { it * it } + .toList() +``` + +### **2. Performance Considerations** +```kotlin +val largeList = (1..1000000).toList() + +// Less efficient - creates intermediate collections +val result1 = largeList + .filter { it % 2 == 0 } + .map { it * it } + .take(10) + +// More efficient - uses sequences for lazy evaluation +val result2 = largeList.asSequence() + .filter { it % 2 == 0 } + .map { it * it } + .take(10) + .toList() +``` + +### **3. Immutability** +```kotlin +val original = listOf(1, 2, 3, 4, 5) + +// These operations don't modify the original list +val filtered = original.filter { it> 3 } // [4, 5] +val mapped = original.map { it * 2 } // [2, 4, 6, 8, 10] + +println(original) // Still [1, 2, 3, 4, 5] +``` + +## ๐Ÿงช Running the Examples + +### **Step 1: Open the File** +1. Navigate to `src/44_filter_map_sorting.kt` +2. Open it in IntelliJ IDEA + +### **Step 2: Run the Program** +1. Right-click on the file +2. Select "Run '44_filter_map_sortingKt'" +3. Observe the output + +## ๐ŸŽฎ Hands-On Exercises + +### **Exercise 1: Student Grade Analysis** +Create a list of student grades and perform various analyses. + +**Solution:** +```kotlin +data class Student(val name: String, val grade: Int, val subject: String) + +fun main() { + val students = listOf( + Student("Alice", 95, "Math"), + Student("Bob", 87, "Math"), + Student("Charlie", 92, "Math"), + Student("Alice", 88, "Physics"), + Student("Bob", 91, "Physics"), + Student("Charlie", 85, "Physics") + ) + + // Find top students in Math + val topMathStudents = students + .filter { it.subject == "Math" } + .sortedByDescending { it.grade } + .take(2) + .map { "${it.name}: ${it.grade}" } + + println("Top Math students: $topMathStudents") + + // Calculate average grade by subject + val avgBySubject = students + .groupBy { it.subject } + .mapValues { (_, students) -> students.map { it.grade }.average() } + + println("Average by subject: $avgBySubject") +} +``` + +### **Exercise 2: Text Processing Pipeline** +Process text through multiple transformation steps. + +**Solution:** +```kotlin +fun main() { + val text = "Hello World! Welcome to Kotlin Programming. Let's learn together!" + + val processedText = text + .lowercase() // Convert to lowercase + .split(" ") // Split into words + .filter { it.length> 3 } // Keep words longer than 3 characters + .map { it.trim('.', '!', ',') } // Remove punctuation + .filter { it.isNotEmpty() } // Remove empty strings + .distinct() // Remove duplicates + .sorted() // Sort alphabetically + + println("Original: $text") + println("Processed: $processedText") + + // Word frequency analysis + val wordFreq = text + .lowercase() + .split(" ") + .filter { it.isNotEmpty() } + .groupBy { it.trim('.', '!', ',') } + .mapValues { it.value.size } + .toList() + .sortedByDescending { it.second } + + println("Word frequencies: $wordFreq") +} +``` + +### **Exercise 3: Number Sequence Analysis** +Analyze a sequence of numbers using functional operations. + +**Solution:** +```kotlin +fun analyzeNumbers(numbers: List) { + val analysis = numbers + .filter { it> 0 } // Keep positive numbers + .let { positiveNumbers -> + mapOf( + "count" to positiveNumbers.size, + "sum" to positiveNumbers.sum(), + "average" to positiveNumbers.average(), + "even" to positiveNumbers.filter { it % 2 == 0 }, + "odd" to positiveNumbers.filter { it % 2 != 0 }, + "squares" to positiveNumbers.map { it * it }, + "sorted" to positiveNumbers.sorted(), + "reversed" to positiveNumbers.sortedDescending() + ) + } + + analysis.forEach { (key, value) -> + println("$key: $value") + } +} + +fun main() { + val numbers = listOf(-2, 1, 3, -4, 5, 6, -7, 8, 9, -10) + analyzeNumbers(numbers) +} +``` + +### **Exercise 4: Complex Data Transformation** +Transform complex data structures using functional operations. + +**Solution:** +```kotlin +data class Product(val name: String, val price: Double, val category: String, val rating: Double) + +fun main() { + val products = listOf( + Product("Laptop", 999.99, "Electronics", 4.5), + Product("Mouse", 29.99, "Electronics", 4.2), + Product("Book", 19.99, "Books", 4.8), + Product("Chair", 199.99, "Furniture", 4.1), + Product("Table", 299.99, "Furniture", 4.3), + Product("Pen", 2.99, "Office", 4.0) + ) + + // Find expensive electronics with high ratings + val expensiveElectronics = products + .filter { it.category == "Electronics" && it.price> 100 && it.rating>= 4.0 } + .sortedByDescending { it.rating } + .map { "${it.name} ($${it.price}) - Rating: ${it.rating}" } + + println("Expensive electronics: $expensiveElectronics") + + // Group by category and calculate statistics + val categoryStats = products + .groupBy { it.category } + .mapValues { (_, products) -> + mapOf( + "count" to products.size, + "avgPrice" to products.map { it.price }.average(), + "avgRating" to products.map { it.rating }.average(), + "totalValue" to products.map { it.price }.sum() + ) + } + + println("Category statistics: $categoryStats") +} +``` + +## ๐Ÿšจ Common Mistakes to Avoid + +1. **Chaining too many operations**: Can reduce readability โŒ + - **Better**: Break into multiple lines or extract functions โœ… + +2. **Forgetting immutability**: Operations don't modify original collections โœ… + +3. **Using sequences unnecessarily**: For small collections, regular operations are fine โœ… + +4. **Inefficient filtering**: Filter before map when possible โœ… + +## ๐ŸŽฏ What's Next? + +Now that you understand functional operations, continue with: + +1. **Advanced collections** โ†’ [Collections Overview](../README.md#collections) +2. **Functional programming** โ†’ [Functional Programming](../functional-programming/01-lambdas.md) +3. **Null safety** โ†’ [Null Safety](../null-safety/01-null-safety.md) +4. **Coroutines** โ†’ [Coroutines](../coroutines/01-introduction.md) + +## ๐Ÿ“š Additional Resources + +- [Official Kotlin Collections Documentation](https://kotlinlang.org/docs/collections-overview.html) +- [Kotlin Collections API](https://kotlinlang.org/api/latest/jvm/stdlib/kotlin.collections/) +- [Functional Programming in Kotlin](https://kotlinlang.org/docs/fun-interfaces.html) + +## ๐Ÿ† Summary + +- โœ… You can filter collections using various predicates +- โœ… You can transform collections with map operations +- โœ… You can sort collections in multiple ways +- โœ… You can chain operations for complex transformations +- โœ… You understand performance considerations and best practices + +**Functional operations make your code more readable and maintainable. Use them wisely! ๐ŸŽ‰** diff --git a/docs/control-flow/04-while-loops.md b/docs/control-flow/04-while-loops.md index 89921e2..b3161e7 100644 --- a/docs/control-flow/04-while-loops.md +++ b/docs/control-flow/04-while-loops.md @@ -527,3 +527,4 @@ Excellent! You've mastered while loops in Kotlin. Now you're ready to: - โœ… You're ready to handle dynamic repetition scenarios! **Keep practicing and happy coding! ๐ŸŽ‰** + diff --git a/docs/control-flow/05-break-continue.md b/docs/control-flow/05-break-continue.md index 14c7732..9fad418 100644 --- a/docs/control-flow/05-break-continue.md +++ b/docs/control-flow/05-break-continue.md @@ -579,3 +579,4 @@ Excellent! You've mastered break and continue statements in Kotlin. Now you're r - โœ… You're ready to create sophisticated loop control logic! **Keep practicing and happy coding! ๐ŸŽ‰** + diff --git a/docs/coroutines/01-introduction.md b/docs/coroutines/01-introduction.md new file mode 100644 index 0000000..92e8886 --- /dev/null +++ b/docs/coroutines/01-introduction.md @@ -0,0 +1,544 @@ +# โšก Introduction to Coroutines in Kotlin + +Coroutines are Kotlin's solution for asynchronous programming. They allow you to write asynchronous, non-blocking code in a sequential, readable manner. Coroutines are lightweight threads that can be suspended and resumed, making them perfect for handling long-running operations without blocking the main thread. + +## ๐Ÿ“‹ Learning Objectives + +By the end of this lesson, you will be able to: +- โœ… Understand what coroutines are and why they're useful +- โœ… Create and launch basic coroutines +- โœ… Use `runBlocking` to bridge between blocking and non-blocking code +- โœ… Understand the difference between coroutines and threads +- โœ… Write simple asynchronous code using coroutines + +## ๐Ÿ” What You'll Learn + +- **Coroutine basics** - What coroutines are and how they work +- **Coroutine builders** - Different ways to create coroutines +- **Suspending functions** - Functions that can pause execution +- **Coroutine context** - Understanding where coroutines run +- **Basic coroutine patterns** - Common use cases and examples + +## ๐Ÿ“ Prerequisites + +- Understanding of [Functions](../functions/01-functions-basics.md) +- Knowledge of [Control Flow](../control-flow/01-if-expressions.md) +- Familiarity with [Collections](../collections/01-arrays.md) +- Basic understanding of asynchronous programming concepts + +## ๐Ÿ’ป The Code + +Let's examine the coroutines examples: + +**๐Ÿ“ File:** [61_first_coroutine.kt](../src/61_first_coroutine.kt) + +## ๐Ÿ” Code Breakdown + +### **1. What Are Coroutines?** + +#### **Basic Definition** +```kotlin +// Coroutines are functions that can be suspended and resumed +// They allow you to write asynchronous code in a sequential manner + +// Traditional approach (blocking) +fun traditionalFunction() { + Thread.sleep(1000) // Blocks the thread + println("After delay") +} + +// Coroutine approach (non-blocking) +suspend fun coroutineFunction() { + delay(1000) // Suspends the coroutine, doesn't block the thread + println("After delay") +} +``` + +#### **Key Benefits** +```kotlin +// 1. Lightweight - Thousands of coroutines can run on a single thread +// 2. Non-blocking - Don't block the thread they run on +// 3. Sequential - Code looks like regular sequential code +// 4. Cancellable - Can be cancelled at any time +// 5. Exception handling - Built-in exception handling mechanisms +``` + +### **2. Your First Coroutine** + +#### **Using `runBlocking`** +```kotlin +fun main() { + println("Starting main function") + + runBlocking { + println("Inside coroutine") + delay(1000) // Suspend for 1 second + println("After delay in coroutine") + } + + println("Back in main function") +} +``` + +#### **Using `launch`** +```kotlin +fun main() { + println("Starting main function") + + // Launch a new coroutine + GlobalScope.launch { + println("Inside launched coroutine") + delay(1000) + println("After delay in launched coroutine") + } + + println("Main function continues immediately") + Thread.sleep(2000) // Wait for coroutine to complete +} +``` + +### **3. Coroutine Builders** + +#### **`runBlocking` - Bridge Between Blocking and Non-Blocking** +```kotlin +fun main() { + println("Main thread: ${Thread.currentThread().name}") + + runBlocking { + println("Coroutine thread: ${Thread.currentThread().name}") + delay(1000) + println("Coroutine completed") + } + + println("Main thread continues") +} +``` + +#### **`launch` - Fire and Forget** +```kotlin +fun main() { + println("Starting main") + + val job = GlobalScope.launch { + println("Coroutine started") + delay(1000) + println("Coroutine completed") + } + + println("Main continues") + job.join() // Wait for coroutine to complete + println("Main finished") +} +``` + +#### **`async` - Return a Result** +```kotlin +fun main() { + println("Starting main") + + val deferred = GlobalScope.async { + println("Async coroutine started") + delay(1000) + "Result from coroutine" + } + + println("Main continues") + val result = deferred.await() // Wait for result + println("Got result: $result") + println("Main finished") +} +``` + +### **4. Suspending Functions** + +#### **Creating Suspending Functions** +```kotlin +// Regular function +fun regularFunction() { + println("Regular function") +} + +// Suspending function +suspend fun suspendingFunction() { + println("Suspending function started") + delay(1000) // Can only be called from coroutines + println("Suspending function completed") +} + +// Suspending function with return value +suspend fun fetchData(): String { + delay(1000) // Simulate network call + return "Data from network" +} +``` + +#### **Using Suspending Functions** +```kotlin +fun main() { + runBlocking { + println("Calling suspending function") + val data = fetchData() + println("Received: $data") + + println("Calling another suspending function") + suspendingFunction() + println("All done!") + } +} +``` + +### **5. Coroutine Context and Dispatchers** + +#### **Understanding Dispatchers** +```kotlin +fun main() { + runBlocking { + // Default dispatcher (shared thread pool) + launch { + println("Default dispatcher: ${Thread.currentThread().name}") + } + + // IO dispatcher (for network/disk operations) + launch(Dispatchers.IO) { + println("IO dispatcher: ${Thread.currentThread().name}") + } + + // Main dispatcher (for UI operations in Android) + launch(Dispatchers.Main) { + println("Main dispatcher: ${Thread.currentThread().name}") + } + + // Unconfined dispatcher (inherits from parent) + launch(Dispatchers.Unconfined) { + println("Unconfined dispatcher: ${Thread.currentThread().name}") + } + } +} +``` + +#### **Custom Coroutine Context** +```kotlin +fun main() { + runBlocking { + // Create a custom context + val customContext = Dispatchers.IO + SupervisorJob() + + launch(customContext) { + println("Custom context: ${Thread.currentThread().name}") + delay(1000) + println("Custom context completed") + } + + println("Main coroutine continues") + } +} +``` + +### **6. Practical Examples** + +#### **Simulating Network Calls** +```kotlin +suspend fun fetchUserData(userId: Int): String { + delay(1000) // Simulate network delay + return "User data for ID: $userId" +} + +suspend fun fetchUserPosts(userId: Int): String { + delay(800) // Simulate network delay + return "Posts for user ID: $userId" +} + +fun main() { + runBlocking { + println("Fetching user data...") + + val userData = fetchUserData(123) + val userPosts = fetchUserPosts(123) + + println("User data: $userData") + println("User posts: $userPosts") + println("All data fetched!") + } +} +``` + +#### **Parallel Execution** +```kotlin +fun main() { + runBlocking { + println("Starting parallel execution") + + val startTime = System.currentTimeMillis() + + // Launch coroutines in parallel + val deferred1 = async { fetchUserData(1) } + val deferred2 = async { fetchUserData(2) } + val deferred3 = async { fetchUserData(3) } + + // Wait for all results + val result1 = deferred1.await() + val result2 = deferred2.await() + val result3 = deferred3.await() + + val endTime = System.currentTimeMillis() + + println("Results: $result1, $result2, $result3") + println("Total time: ${endTime - startTime}ms") + } +} +``` + +## ๐ŸŽฏ Key Concepts Explained + +### **1. Coroutines vs Threads** +```kotlin +// Threads are expensive - limited number can be created +// Coroutines are lightweight - thousands can run simultaneously + +// Thread approach +fun threadExample() { + repeat(1000) { + Thread { + Thread.sleep(1000) + println("Thread $it completed") + }.start() + } + // This would create 1000 threads - very expensive! +} + +// Coroutine approach +fun coroutineExample() { + runBlocking { + repeat(1000) { + launch { + delay(1000) + println("Coroutine $it completed") + } + } + } + // This creates 1000 coroutines - very lightweight! +} +``` + +### **2. Suspending vs Blocking** +```kotlin +// Blocking - stops the entire thread +fun blockingFunction() { + Thread.sleep(1000) // Thread is blocked for 1 second +} + +// Suspending - only suspends the coroutine +suspend fun suspendingFunction() { + delay(1000) // Only this coroutine is suspended + // Other coroutines on the same thread can continue +} +``` + +### **3. Coroutine Scopes** +```kotlin +// GlobalScope - lives as long as the application +GlobalScope.launch { /* ... */ } + +// runBlocking - blocks the current thread until completion +runBlocking { /* ... */ } + +// CoroutineScope - custom scope with lifecycle management +val scope = CoroutineScope(Dispatchers.Main) +scope.launch { /* ... */ } +``` + +## ๐Ÿงช Running the Examples + +### **Step 1: Open the File** +1. Navigate to `src/61_first_coroutine.kt` +2. Open it in IntelliJ IDEA + +### **Step 2: Run the Program** +1. Right-click on the file +2. Select "Run '61_first_coroutineKt'" +3. Observe the output + +## ๐ŸŽฎ Hands-On Exercises + +### **Exercise 1: Basic Coroutine Operations** +Create and run basic coroutines with different delays. + +**Solution:** +```kotlin +fun main() { + runBlocking { + println("Starting exercise") + + // Launch multiple coroutines + val job1 = launch { + delay(1000) + println("Coroutine 1 completed") + } + + val job2 = launch { + delay(500) + println("Coroutine 2 completed") + } + + val job3 = launch { + delay(1500) + println("Coroutine 3 completed") + } + + // Wait for all to complete + job1.join() + job2.join() + job3.join() + + println("All coroutines completed") + } +} +``` + +### **Exercise 2: Suspending Functions** +Create and use your own suspending functions. + +**Solution:** +```kotlin +suspend fun processItem(item: String): String { + delay(500) // Simulate processing time + return "Processed: $item" +} + +suspend fun validateItem(item: String): Boolean { + delay(200) // Simulate validation time + return item.length> 0 +} + +fun main() { + runBlocking { + val items = listOf("Apple", "Banana", "Cherry") + + items.forEach { item -> + val isValid = validateItem(item) + if (isValid) { + val processed = processItem(item) + println(processed) + } else { + println("Invalid item: $item") + } + } + + println("All items processed") + } +} +``` + +### **Exercise 3: Parallel Processing** +Process multiple items in parallel using coroutines. + +**Solution:** +```kotlin +suspend fun processItem(item: String): String { + delay(1000) // Simulate processing time + return "Processed: $item" +} + +fun main() { + runBlocking { + val items = listOf("Item1", "Item2", "Item3", "Item4", "Item5") + + println("Starting parallel processing") + val startTime = System.currentTimeMillis() + + // Process all items in parallel + val deferreds = items.map { item -> + async { + processItem(item) + } + } + + // Wait for all results + val results = deferreds.awaitAll() + + val endTime = System.currentTimeMillis() + + results.forEach { println(it) } + println("Total time: ${endTime - startTime}ms") + println("Parallel processing completed") + } +} +``` + +### **Exercise 4: Coroutine Context Management** +Use different dispatchers and manage coroutine contexts. + +**Solution:** +```kotlin +suspend fun cpuIntensiveTask(): Int { + var result = 0 + repeat(1000000) { + result += it + } + return result +} + +suspend fun ioTask(): String { + delay(1000) // Simulate I/O operation + return "I/O completed" +} + +fun main() { + runBlocking { + println("Starting context management exercise") + + // CPU-intensive task on Default dispatcher + val cpuResult = withContext(Dispatchers.Default) { + println("CPU task on: ${Thread.currentThread().name}") + cpuIntensiveTask() + } + + // I/O task on IO dispatcher + val ioResult = withContext(Dispatchers.IO) { + println("I/O task on: ${Thread.currentThread().name}") + ioTask() + } + + // Main thread task + withContext(Dispatchers.Main) { + println("Main task on: ${Thread.currentThread().name}") + } + + println("CPU result: $cpuResult") + println("I/O result: $ioResult") + println("All tasks completed") + } +} +``` + +## ๐Ÿšจ Common Mistakes to Avoid + +1. **Forgetting `runBlocking`**: Can't call suspending functions from regular functions โœ… +2. **Using GlobalScope in production**: Can lead to memory leaks โœ… +3. **Ignoring coroutine context**: Choose the right dispatcher for your use case โœ… +4. **Not handling exceptions**: Coroutines can fail, handle errors appropriately โœ… + +## ๐ŸŽฏ What's Next? + +Now that you understand coroutines basics, continue with: + +1. **Launch and Async** โ†’ [Launch and Async Coroutines](02-launch-async.md) +2. **Exception Handling** โ†’ [Coroutine Exception Handling](03-exception-handling.md) +3. **Coroutine Context** โ†’ [Coroutine Context and Dispatchers](04-context-dispatchers.md) +4. **Advanced Patterns** โ†’ [Advanced Coroutine Patterns](05-advanced-patterns.md) + +## ๐Ÿ“š Additional Resources + +- [Official Kotlin Coroutines Documentation](https://kotlinlang.org/docs/coroutines-overview.html) +- [Coroutines Guide](https://kotlinlang.org/docs/coroutines-guide.html) +- [Coroutines Basics](https://kotlinlang.org/docs/coroutines-basics.html) + +## ๐Ÿ† Summary + +- โœ… You understand what coroutines are and their benefits +- โœ… You can create and launch basic coroutines +- โœ… You know how to use `runBlocking`, `launch`, and `async` +- โœ… You understand suspending functions and coroutine context +- โœ… You're ready to write asynchronous, non-blocking code! + +**Coroutines make asynchronous programming simple and readable. Start building responsive applications! ๐ŸŽ‰** diff --git a/docs/coroutines/02-launch-async.md b/docs/coroutines/02-launch-async.md new file mode 100644 index 0000000..7e84482 --- /dev/null +++ b/docs/coroutines/02-launch-async.md @@ -0,0 +1,676 @@ +# ๐Ÿš€ Launch and Async Coroutines in Kotlin + +`launch` and `async` are the two main coroutine builders in Kotlin. They allow you to start coroutines in different ways depending on whether you need to return a result or just execute some work in the background. + +## ๐Ÿ“‹ Learning Objectives + +By the end of this lesson, you will be able to: +- โœ… Use `launch` for fire-and-forget coroutines +- โœ… Use `async` for coroutines that return results +- โœ… Understand the difference between `launch` and `async` +- โœ… Handle coroutine jobs and deferred results +- โœ… Implement parallel execution patterns + +## ๐Ÿ” What You'll Learn + +- **Launch coroutines** - Fire-and-forget background execution +- **Async coroutines** - Coroutines that return results +- **Job management** - Controlling coroutine lifecycle +- **Parallel execution** - Running multiple coroutines simultaneously +- **Result handling** - Working with deferred results + +## ๐Ÿ“ Prerequisites + +- Understanding of [Introduction to Coroutines](01-introduction.md) +- Knowledge of [Functions](../functions/01-functions-basics.md) +- Familiarity with [Collections](../collections/01-arrays.md) + +## ๐Ÿ’ป The Code + +Let's examine the launch and async examples: + +**๐Ÿ“ File:** [64_launch_coroutine_builder.kt](../src/64_launch_coroutine_builder.kt) + +## ๐Ÿ” Code Breakdown + +### **1. The `launch` Coroutine Builder** + +#### **Basic Launch Usage** +```kotlin +fun main() { + println("Starting main function") + + // Launch a coroutine that doesn't return a value + val job = GlobalScope.launch { + println("Coroutine started") + delay(1000) + println("Coroutine completed") + } + + println("Main function continues") + job.join() // Wait for coroutine to complete + println("Main function finished") +} +``` + +#### **Launch with Different Dispatchers** +```kotlin +fun main() { + runBlocking { + println("Main thread: ${Thread.currentThread().name}") + + // Launch on IO dispatcher + val ioJob = launch(Dispatchers.IO) { + println("IO coroutine on: ${Thread.currentThread().name}") + delay(1000) + println("IO work completed") + } + + // Launch on Default dispatcher + val defaultJob = launch(Dispatchers.Default) { + println("Default coroutine on: ${Thread.currentThread().name}") + delay(500) + println("Default work completed") + } + + // Launch on Main dispatcher + val mainJob = launch(Dispatchers.Main) { + println("Main coroutine on: ${Thread.currentThread().name}") + delay(300) + println("Main work completed") + } + + // Wait for all to complete + ioJob.join() + defaultJob.join() + mainJob.join() + } +} +``` + +#### **Launch with Job Management** +```kotlin +fun main() { + runBlocking { + // Create a parent job + val parentJob = Job() + + // Launch child coroutines + val child1 = launch(parentJob) { + println("Child 1 started") + delay(1000) + println("Child 1 completed") + } + + val child2 = launch(parentJob) { + println("Child 2 started") + delay(1500) + println("Child 2 completed") + } + + // Cancel parent job after 500ms + delay(500) + parentJob.cancel() + + try { + child1.join() + child2.join() + } catch (e: CancellationException) { + println("Coroutines were cancelled") + } + } +} +``` + +### **2. The `async` Coroutine Builder** + +#### **Basic Async Usage** +```kotlin +fun main() { + runBlocking { + println("Starting async operations") + + // Launch async coroutine that returns a result + val deferred = async { + println("Async operation started") + delay(1000) + "Async result" + } + + println("Main continues while async runs") + val result = deferred.await() // Wait for result + println("Got result: $result") + } +} +``` + +#### **Multiple Async Operations** +```kotlin +fun main() { + runBlocking { + println("Starting multiple async operations") + + val startTime = System.currentTimeMillis() + + // Launch multiple async coroutines + val deferred1 = async { fetchData(1) } + val deferred2 = async { fetchData(2) } + val deferred3 = async { fetchData(3) } + + // Wait for all results + val result1 = deferred1.await() + val result2 = deferred2.await() + val result3 = deferred3.await() + + val endTime = System.currentTimeMillis() + + println("Results: $result1, $result2, $result3") + println("Total time: ${endTime - startTime}ms") + } +} + +suspend fun fetchData(id: Int): String { + delay(1000) // Simulate network call + return "Data for ID: $id" +} +``` + +#### **Async with Error Handling** +```kotlin +fun main() { + runBlocking { + try { + val deferred = async { + // Simulate operation that might fail + if (Math.random() < 0.5) { + throw RuntimeException("Random failure") + } + "Success result" + } + + val result = deferred.await() + println("Operation succeeded: $result") + + } catch (e: Exception) { + println("Operation failed: ${e.message}") + } + } +} +``` + +### **3. Comparing Launch vs Async** + +#### **Key Differences** +```kotlin +fun demonstrateDifferences() { + runBlocking { + println("=== Launch vs Async Comparison ===") + + // Launch - fire and forget, returns Job + val launchJob = launch { + delay(1000) + println("Launch coroutine completed") + } + + // Async - returns result, returns Deferred + val asyncDeferred = async { + delay(1000) + "Async coroutine result" + } + + // Launch doesn't return a value + println("Launch job type: ${launchJob::class.simpleName}") + + // Async returns a Deferred + println("Async deferred type: ${asyncDeferred::class.simpleName}") + + // Wait for both to complete + launchJob.join() + val result = asyncDeferred.await() + println("Async result: $result") + } +} +``` + +#### **Use Case Examples** +```kotlin +class UseCaseExamples { + // Use launch when: + // - You don't need a return value + // - You want fire-and-forget execution + // - You need to perform side effects + + fun performBackgroundTask() { + GlobalScope.launch { + // Perform some background work + delay(1000) + println("Background task completed") + } + } + + // Use async when: + // - You need a return value + // - You want to compose results + // - You need to run operations in parallel + + suspend fun fetchUserData(userId: Int): User { + val userDeferred = async { fetchUser(userId) } + val postsDeferred = async { fetchUserPosts(userId) } + val settingsDeferred = async { fetchUserSettings(userId) } + + val user = userDeferred.await() + val posts = postsDeferred.await() + val settings = settingsDeferred.await() + + return user.copy(posts = posts, settings = settings) + } + + private suspend fun fetchUser(id: Int): User = User(id, "User $id") + private suspend fun fetchUserPosts(id: Int): List = listOf(Post("Post 1")) + private suspend fun fetchUserSettings(id: Int): UserSettings = UserSettings() +} + +// Mock classes +data class User(val id: Int, val name: String, val posts: List = emptyList(), val settings: UserSettings = UserSettings()) +data class Post(val title: String) +class UserSettings +``` + +### **4. Advanced Patterns** + +#### **Structured Concurrency** +```kotlin +fun main() { + runBlocking { + // Create a coroutine scope + coroutineScope { + val deferred1 = async { fetchData(1) } + val deferred2 = async { fetchData(2) } + + // If any coroutine fails, all are cancelled + try { + val result1 = deferred1.await() + val result2 = deferred2.await() + println("Both succeeded: $result1, $result2") + } catch (e: Exception) { + println("One or both failed: ${e.message}") + } + } + } +} + +suspend fun fetchData(id: Int): String { + delay(1000) + if (id == 2 && Math.random() < 0.5) { + throw RuntimeException("Random failure for ID 2") + } + return "Data $id" +} +``` + +#### **Supervisor Scope** +```kotlin +fun main() { + runBlocking { + // Supervisor scope - child failures don't affect siblings + supervisorScope { + val deferred1 = async { fetchData(1) } + val deferred2 = async { fetchData(2) } + val deferred3 = async { fetchData(3) } + + // Collect results, ignoring failures + val results = listOf(deferred1, deferred2, deferred3) + .mapNotNull { deferred -> + try { + deferred.await() + } catch (e: Exception) { + println("Failed: ${e.message}") + null + } + } + + println("Successful results: $results") + } + } +} +``` + +#### **Lazy Async** +```kotlin +fun main() { + runBlocking { + // Lazy async - only starts when awaited + val lazyDeferred = async(start = CoroutineStart.LAZY) { + println("Lazy async started") + delay(1000) + "Lazy result" + } + + println("Lazy async created but not started") + delay(500) + + println("Now starting lazy async") + val result = lazyDeferred.await() + println("Got result: $result") + } +} +``` + +### **5. Practical Examples** + +#### **Parallel Data Processing** +```kotlin +fun main() { + runBlocking { + val items = (1..10).toList() + + println("Processing ${items.size} items sequentially...") + val sequentialStart = System.currentTimeMillis() + val sequentialResults = items.map { processItem(it) } + val sequentialTime = System.currentTimeMillis() - sequentialStart + + println("Sequential time: ${sequentialTime}ms") + + println("Processing ${items.size} items in parallel...") + val parallelStart = System.currentTimeMillis() + val deferredResults = items.map { async { processItem(it) } } + val parallelResults = deferredResults.awaitAll() + val parallelTime = System.currentTimeMillis() - parallelStart + + println("Parallel time: ${parallelTime}ms") + println("Speedup: ${sequentialTime.toDouble() / parallelTime}") + } +} + +suspend fun processItem(item: Int): String { + delay(100) // Simulate processing time + return "Processed item $item" +} +``` + +#### **Concurrent API Calls** +```kotlin +fun main() { + runBlocking { + val userIds = listOf(1, 2, 3, 4, 5) + + println("Fetching user data for ${userIds.size} users...") + + // Fetch all user data concurrently + val userDeferreds = userIds.map { id -> + async { + fetchUserData(id) + } + } + + // Wait for all results + val users = userDeferreds.awaitAll() + + // Process results + users.forEach { user -> + println("User: ${user.name}, Posts: ${user.postCount}") + } + } +} + +suspend fun fetchUserData(userId: Int): UserData { + delay(500) // Simulate API call + return UserData("User $userId", (1..5).random()) +} + +data class UserData(val name: String, val postCount: Int) +``` + +## ๐ŸŽฏ Key Concepts Explained + +### **1. Launch vs Async Summary** +```kotlin +// Launch: +// - Returns Job +// - Fire and forget +// - No return value +// - Good for side effects + +// Async: +// - Returns Deferred +// - Returns a result +// - Can be awaited +// - Good for parallel execution +``` + +### **2. Job Lifecycle** +```kotlin +val job = launch { + // Coroutine body +} + +// Job states: New -> Active -> Completing -> Completed +// Can be cancelled at any time + +job.cancel() // Cancel the job +job.join() // Wait for completion +job.isActive // Check if active +job.isCompleted // Check if completed +``` + +### **3. Deferred Results** +```kotlin +val deferred = async { + "Result" +} + +// Deferred is a Job that can return a value +deferred.await() // Wait for and get the result +deferred.isCompleted // Check if completed +deferred.getCompleted() // Get result if completed +``` + +## ๐Ÿงช Running the Examples + +### **Step 1: Open the File** +1. Navigate to `src/64_launch_coroutine_builder.kt` +2. Open it in IntelliJ IDEA + +### **Step 2: Run the Program** +1. Right-click on the file +2. Select "Run '64_launch_coroutine_builderKt'" +3. Observe the output + +## ๐ŸŽฎ Hands-On Exercises + +### **Exercise 1: Basic Launch and Async** +Create and manage basic launch and async coroutines. + +**Solution:** +```kotlin +fun main() { + runBlocking { + println("Starting exercise") + + // Launch a background task + val backgroundJob = launch { + repeat(5) { + delay(200) + println("Background task iteration $it") + } + } + + // Async task that returns a result + val resultDeferred = async { + delay(1000) + "Async computation result" + } + + // Wait for background task + backgroundJob.join() + println("Background task completed") + + // Get async result + val result = resultDeferred.await() + println("Async result: $result") + + println("Exercise completed") + } +} +``` + +### **Exercise 2: Parallel Processing** +Process multiple items in parallel using async. + +**Solution:** +```kotlin +fun main() { + runBlocking { + val items = listOf("Apple", "Banana", "Cherry", "Date", "Elderberry") + + println("Processing items: $items") + + // Process all items in parallel + val processingDeferreds = items.map { item -> + async { + processItem(item) + } + } + + // Wait for all results + val results = processingDeferreds.awaitAll() + + println("Processing results:") + results.forEach { (item, processed) -> + println("$item -> $processed") + } + } +} + +suspend fun processItem(item: String): Pair { + delay(500) // Simulate processing time + val processed = item.uppercase().reversed() + return item to processed +} +``` + +### **Exercise 3: Error Handling with Async** +Handle errors in async coroutines gracefully. + +**Solution:** +```kotlin +fun main() { + runBlocking { + val operations = listOf(1, 2, 3, 4, 5) + + println("Starting operations: $operations") + + // Launch operations with error handling + val results = operations.map { id -> + async { + try { + performOperation(id) + } catch (e: Exception) { + "Failed: ${e.message}" + } + } + } + + // Collect all results + val completedResults = results.awaitAll() + + println("Operation results:") + completedResults.forEachIndexed { index, result -> + println("Operation ${operations[index]}: $result") + } + } +} + +suspend fun performOperation(id: Int): String { + delay(200) + + // Simulate random failures + if (id % 3 == 0) { + throw RuntimeException("Operation $id failed") + } + + return "Operation $id completed successfully" +} +``` + +### **Exercise 4: Structured Concurrency** +Implement structured concurrency patterns. + +**Solution:** +```kotlin +fun main() { + runBlocking { + println("Starting structured concurrency example") + + // Use coroutineScope for structured concurrency + val result = coroutineScope { + val userDeferred = async { fetchUser(123) } + val postsDeferred = async { fetchPosts(123) } + val settingsDeferred = async { fetchSettings(123) } + + // All coroutines must complete successfully + val user = userDeferred.await() + val posts = postsDeferred.await() + val settings = settingsDeferred.await() + + UserProfile(user, posts, settings) + } + + println("User profile created: ${result.user.name}") + println("Posts count: ${result.posts.size}") + println("Settings: ${result.settings}") + } +} + +suspend fun fetchUser(id: Int): User { + delay(300) + return User(id, "User $id") +} + +suspend fun fetchPosts(userId: Int): List { + delay(400) + return listOf(Post("Post 1"), Post("Post 2")) +} + +suspend fun fetchSettings(userId: Int): UserSettings { + delay(200) + return UserSettings(theme = "dark", notifications = true) +} + +data class UserProfile(val user: User, val posts: List, val settings: UserSettings) +data class User(val id: Int, val name: String) +data class Post(val title: String) +data class UserSettings(val theme: String, val notifications: Boolean) +``` + +## ๐Ÿšจ Common Mistakes to Avoid + +1. **Forgetting to await async results**: Results won't be available โœ… +2. **Using GlobalScope in production**: Can lead to memory leaks โœ… +3. **Not handling exceptions**: Async coroutines can fail โœ… +4. **Ignoring structured concurrency**: Use coroutineScope for proper lifecycle management โœ… + +## ๐ŸŽฏ What's Next? + +Now that you understand launch and async, continue with: + +1. **Exception Handling** โ†’ [Coroutine Exception Handling](03-exception-handling.md) +2. **Coroutine Context** โ†’ [Coroutine Context and Dispatchers](04-context-dispatchers.md) +3. **Advanced Patterns** โ†’ [Advanced Coroutine Patterns](05-advanced-patterns.md) +4. **Collections** โ†’ [Collections Overview](../collections/README.md) + +## ๐Ÿ“š Additional Resources + +- [Official Kotlin Coroutines Documentation](https://kotlinlang.org/docs/coroutines-overview.html) +- [Coroutine Builders](https://kotlinlang.org/docs/coroutines-basics.html#coroutine-builders) +- [Async Coroutines](https://kotlinlang.org/docs/coroutines-basics.html#async-coroutines) + +## ๐Ÿ† Summary + +- โœ… You can use `launch` for fire-and-forget coroutines +- โœ… You can use `async` for coroutines that return results +- โœ… You understand the difference between `launch` and `async` +- โœ… You can handle coroutine jobs and deferred results +- โœ… You're ready to implement parallel execution patterns! + +**Launch and async are the foundation of coroutine programming. Use them to build responsive, concurrent applications! ๐ŸŽ‰** diff --git a/docs/functional-programming/01-lambdas.md b/docs/functional-programming/01-lambdas.md new file mode 100644 index 0000000..76fa783 --- /dev/null +++ b/docs/functional-programming/01-lambdas.md @@ -0,0 +1,514 @@ +# ๐Ÿš€ Lambdas and Higher-Order Functions in Kotlin + +Lambdas and higher-order functions are the foundation of functional programming in Kotlin. They allow you to write more concise, readable, and flexible code by treating functions as first-class citizens. + +## ๐Ÿ“‹ Learning Objectives + +By the end of this lesson, you will be able to: +- โœ… Understand what lambdas are and how to create them +- โœ… Use higher-order functions that take functions as parameters +- โœ… Work with lambda syntax and parameter conventions +- โœ… Apply functional programming patterns in your code +- โœ… Use the `it` keyword and lambda conventions effectively + +## ๐Ÿ” What You'll Learn + +- **Lambda expressions** - Anonymous functions and their syntax +- **Higher-order functions** - Functions that take other functions as parameters +- **Lambda conventions** - Using `it`, single parameters, and destructuring +- **Functional patterns** - Common functional programming techniques +- **Real-world applications** - Practical examples of lambdas in action + +## ๐Ÿ“ Prerequisites + +- Understanding of [Functions](../functions/01-functions-basics.md) +- Knowledge of [Collections](../collections/01-arrays.md) +- Familiarity with [Control Flow](../control-flow/01-if-expressions.md) + +## ๐Ÿ’ป The Code + +Let's examine the lambdas and higher-order functions examples: + +**๐Ÿ“ File:** [35_lambdas_higher_order_functions.kt](../src/35_lambdas_higher_order_functions.kt) + +## ๐Ÿ” Code Breakdown + +### **1. Basic Lambda Expressions** + +#### **Simple Lambda Syntax** +```kotlin +// Basic lambda that takes no parameters +val sayHello = { println("Hello, World!") } + +// Lambda that takes a parameter +val greet = { name: String -> println("Hello, $name!") } + +// Lambda that returns a value +val square = { x: Int -> x * x } + +// Lambda with multiple parameters +val add = { a: Int, b: Int -> a + b } +``` + +#### **Lambda with Type Inference** +```kotlin +// Kotlin can infer types from context +val numbers = listOf(1, 2, 3, 4, 5) + +// Type inference works here +val doubled = numbers.map { it * 2 } +val evenNumbers = numbers.filter { it % 2 == 0 } +val sum = numbers.reduce { acc, num -> acc + num } +``` + +### **2. Higher-Order Functions** + +#### **Functions as Parameters** +```kotlin +// Function that takes another function as a parameter +fun executeOperation(operation: (Int, Int) -> Int, a: Int, b: Int): Int { + return operation(a, b) +} + +// Function that takes a function with no parameters +fun repeatAction(times: Int, action: () -> Unit) { + repeat(times) { + action() + } +} + +// Function that returns a function +fun createMultiplier(factor: Int): (Int) -> Int { + return { number -> number * factor } +} +``` + +#### **Using Higher-Order Functions** +```kotlin +fun main() { + // Pass lambda to executeOperation + val result1 = executeOperation({ a, b -> a + b }, 5, 3) // 8 + val result2 = executeOperation({ a, b -> a * b }, 4, 6) // 24 + + // Pass lambda to repeatAction + repeatAction(3) { println("Hello!") } + + // Use returned function + val double = createMultiplier(2) + val triple = createMultiplier(3) + + println(double(5)) // 10 + println(triple(5)) // 15 +} +``` + +### **3. Lambda with Collections** + +#### **Common Collection Functions** +```kotlin +val numbers = listOf(1, 2, 3, 4, 5, 6, 7, 8, 9, 10) + +// forEach - execute action for each element +numbers.forEach { println("Number: $it") } + +// map - transform each element +val squared = numbers.map { it * it } + +// filter - select elements based on condition +val evenNumbers = numbers.filter { it % 2 == 0 } + +// reduce - combine all elements into single result +val sum = numbers.reduce { acc, num -> acc + num } + +// any - check if any element matches condition +val hasEven = numbers.any { it % 2 == 0 } + +// all - check if all elements match condition +val allPositive = numbers.all { it> 0 } +``` + +#### **Chaining Lambda Operations** +```kotlin +val numbers = listOf(1, 2, 3, 4, 5, 6, 7, 8, 9, 10) + +// Chain multiple operations +val result = numbers + .filter { it % 2 == 0 } // Keep even numbers + .map { it * it } // Square them + .take(3) // Take first 3 + .sum() // Sum the result + +println(result) // 4 + 16 + 36 = 56 +``` + +### **4. Lambda Conventions and `it` Keyword** + +#### **Using `it` for Single Parameters** +```kotlin +val names = listOf("Alice", "Bob", "Charlie", "David") + +// When lambda has single parameter, you can use 'it' +val upperNames = names.map { it.uppercase() } +val longNames = names.filter { it.length> 4 } +val nameLengths = names.map { it.length } +``` + +#### **Lambda with Multiple Parameters** +```kotlin +val numbers = listOf(1, 2, 3, 4, 5) + +// Multiple parameters need explicit names +val pairs = numbers.mapIndexed { index, value -> + "Index $index: Value $value" +} + +// Using destructuring in lambda +val people = listOf("Alice" to 25, "Bob" to 30, "Charlie" to 35) +val descriptions = people.map { (name, age) -> + "$name is $age years old" +} +``` + +### **5. Advanced Lambda Patterns** + +#### **Lambda with Receiver (Extension Function Style)** +```kotlin +// Lambda that acts as an extension function +val buildString = buildString { + append("Hello") + append(" ") + append("World") + append("!") +} + +// Using with() function +val person = Person("Alice", 25) +val description = with(person) { + "Name: $name, Age: $age" +} +``` + +#### **Lambda with Local Variables** +```kotlin +fun processNumbers(numbers: List) { + var sum = 0 + var count = 0 + + numbers.forEach { num -> + sum += num + count++ + } + + println("Sum: $sum, Count: $count, Average: ${sum.toDouble() / count}") +} +``` + +#### **Lambda with Return Statements** +```kotlin +val numbers = listOf(1, 2, 3, 4, 5, 6, 7, 8, 9, 10) + +// Using forEach with early return (using labels) +numbers.forEach { num -> + if (num == 5) { + return@forEach // Return from lambda, not from function + } + println("Processing: $num") +} +``` + +### **6. Practical Examples** + +#### **Custom Higher-Order Functions** +```kotlin +// Function that retries an operation +fun retry( + times: Int, + operation: () -> T, + onError: (Exception) -> Unit = { } +): T? { + repeat(times) { attempt -> + try { + return operation() + } catch (e: Exception) { + onError(e) + if (attempt == times - 1) throw e + } + } + return null +} + +// Function that measures execution time +fun measureTime(operation: () -> T): Pair { + val startTime = System.currentTimeMillis() + val result = operation() + val endTime = System.currentTimeMillis() + return result to (endTime - startTime) +} +``` + +#### **Using Custom Functions** +```kotlin +fun main() { + // Retry example + val result = retry(3, { + // Simulate operation that might fail + if (Math.random() < 0.7) throw RuntimeException("Random failure") + "Success!" + }) { error -> + println("Attempt failed: ${error.message}") + } + + println("Final result: $result") + + // Measure time example + val (sum, time) = measureTime { + (1..1000000).sum() + } + + println("Sum: $sum, Time: ${time}ms") +} +``` + +## ๐ŸŽฏ Key Concepts Explained + +### **1. Lambda vs Function** +```kotlin +// Function declaration +fun add(a: Int, b: Int): Int = a + b + +// Lambda expression +val addLambda = { a: Int, b: Int -> a + b } + +// Both can be used similarly +val result1 = add(5, 3) +val result2 = addLambda(5, 3) +``` + +### **2. Higher-Order Function Benefits** +- **Reusability**: Write once, use many times +- **Flexibility**: Change behavior without changing function +- **Readability**: Intent is clear from function name +- **Testability**: Easy to test different behaviors + +### **3. Lambda Performance** +```kotlin +// Lambdas are objects, so there's a small overhead +val numbers = (1..1000000).toList() + +// For simple operations, consider inline functions +inline fun List.customFilter(predicate: (T) -> Boolean): List { + return filter(predicate) +} +``` + +## ๐Ÿงช Running the Examples + +### **Step 1: Open the File** +1. Navigate to `src/35_lambdas_higher_order_functions.kt` +2. Open it in IntelliJ IDEA + +### **Step 2: Run the Program** +1. Right-click on the file +2. Select "Run '35_lambdas_higher_order_functionsKt'" +3. Observe the output + +## ๐ŸŽฎ Hands-On Exercises + +### **Exercise 1: Custom Collection Functions** +Create your own higher-order functions for collections. + +**Solution:** +```kotlin +fun List.customForEach(action: (T) -> Unit) { + for (item in this) { + action(item) + } +} + +fun List.customMap(transform: (T) -> T): List { + val result = mutableListOf() + for (item in this) { + result.add(transform(item)) + } + return result +} + +fun List.customFilter(predicate: (T) -> Boolean): List { + val result = mutableListOf() + for (item in this) { + if (predicate(item)) { + result.add(item) + } + } + return result +} + +fun main() { + val numbers = listOf(1, 2, 3, 4, 5) + + numbers.customForEach { println("Number: $it") } + + val doubled = numbers.customMap { it * 2 } + println("Doubled: $doubled") + + val evenNumbers = numbers.customFilter { it % 2 == 0 } + println("Even: $evenNumbers") +} +``` + +### **Exercise 2: Function Composition** +Create functions that compose other functions. + +**Solution:** +```kotlin +// Compose two functions +fun compose(f: (B) -> C, g: (A) -> B): (A) -> C { + return { a -> f(g(a)) } +} + +// Compose multiple functions +fun compose(vararg functions: (T) -> T): (T) -> T { + return { value -> + functions.fold(value) { acc, func -> func(acc) } + } +} + +fun main() { + val addOne = { x: Int -> x + 1 } + val multiplyByTwo = { x: Int -> x * 2 } + val square = { x: Int -> x * x } + + // Compose two functions + val addOneThenMultiply = compose(multiplyByTwo, addOne) + println(addOneThenMultiply(5)) // (5 + 1) * 2 = 12 + + // Compose multiple functions + val complexOperation = compose(addOne, multiplyByTwo, square) + println(complexOperation(3)) // ((3 + 1) * 2)^2 = 64 +} +``` + +### **Exercise 3: Event Handling System** +Create a simple event handling system using lambdas. + +**Solution:** +```kotlin +class EventHandler { + private val listeners = mutableMapOf Unit>>() + + fun addListener(event: String, listener: () -> Unit) { + listeners.getOrPut(event) { mutableListOf() }.add(listener) + } + + fun removeListener(event: String, listener: () -> Unit) { + listeners[event]?.remove(listener) + } + + fun triggerEvent(event: String) { + listeners[event]?.forEach { it() } + } +} + +fun main() { + val eventHandler = EventHandler() + + // Add listeners + eventHandler.addListener("userLogin") { + println("User logged in - sending welcome email") + } + eventHandler.addListener("userLogin") { + println("User logged in - updating last login time") + } + eventHandler.addListener("userLogout") { + println("User logged out - cleaning up session") + } + + // Trigger events + eventHandler.triggerEvent("userLogin") + println("---") + eventHandler.triggerEvent("userLogout") +} +``` + +### **Exercise 4: Data Processing Pipeline** +Create a data processing pipeline using functional programming. + +**Solution:** +```kotlin +data class Person(val name: String, val age: Int, val city: String) + +class DataProcessor { + private var pipeline: (List) -> List = { it } + + fun filter(predicate: (T) -> Boolean): DataProcessor { + val currentPipeline = pipeline + pipeline = { data -> currentPipeline(data).filter(predicate) } + return this + } + + fun map(transform: (T) -> T): DataProcessor { + val currentPipeline = pipeline + pipeline = { data -> currentPipeline(data).map(transform) } + return this + } + + fun sort(comparator: (T, T) -> Int): DataProcessor { + val currentPipeline = pipeline + pipeline = { data -> currentPipeline(data).sortedWith(comparator) } + return this + } + + fun process(data: List): List = pipeline(data) +} + +fun main() { + val people = listOf( + Person("Alice", 25, "New York"), + Person("Bob", 30, "Boston"), + Person("Charlie", 22, "Chicago"), + Person("David", 35, "New York"), + Person("Eve", 28, "Boston") + ) + + val processor = DataProcessor() + .filter { it.age> 25 } + .filter { it.city == "New York" } + .map { Person(it.name, it.age + 1, it.city) } + .sort { a, b -> a.name.compareTo(b.name) } + + val result = processor.process(people) + result.forEach { println("${it.name} (${it.age}) from ${it.city}") } +} +``` + +## ๐Ÿšจ Common Mistakes to Avoid + +1. **Forgetting parentheses**: `numbers.map { it * 2 }` โœ… vs `numbers.map({ it * 2 })` โœ… +2. **Using wrong parameter names**: Use `it` for single parameters โœ… +3. **Ignoring return types**: Let Kotlin infer types when possible โœ… +4. **Overusing lambdas**: Sometimes a simple function is clearer โœ… + +## ๐ŸŽฏ What's Next? + +Now that you understand lambdas and higher-order functions, continue with: + +1. **Scope Functions** โ†’ [Scope Functions](02-scope-functions.md) +2. **Collections** โ†’ [Collections Overview](../collections/README.md) +3. **Null Safety** โ†’ [Null Safety](../null-safety/01-null-safety.md) +4. **Coroutines** โ†’ [Coroutines](../coroutines/01-introduction.md) + +## ๐Ÿ“š Additional Resources + +- [Official Kotlin Lambdas Documentation](https://kotlinlang.org/docs/lambdas.html) +- [Higher-Order Functions](https://kotlinlang.org/docs/lambdas.html#higher-order-functions) +- [Lambda Expressions](https://kotlinlang.org/docs/lambdas.html#lambda-expressions-and-anonymous-functions) + +## ๐Ÿ† Summary + +- โœ… You understand lambda expressions and their syntax +- โœ… You can create and use higher-order functions +- โœ… You know how to use lambda conventions and the `it` keyword +- โœ… You can apply functional programming patterns +- โœ… You're ready to write more expressive and flexible Kotlin code! + +**Lambdas and higher-order functions make your code more powerful and expressive. Embrace functional programming! ๐ŸŽ‰** diff --git a/docs/functional-programming/02-scope-functions.md b/docs/functional-programming/02-scope-functions.md new file mode 100644 index 0000000..57738f0 --- /dev/null +++ b/docs/functional-programming/02-scope-functions.md @@ -0,0 +1,512 @@ +# ๐Ÿ”ง Scope Functions in Kotlin + +Scope functions are a set of functions that allow you to execute a block of code within the context of an object. They provide a temporary scope where you can access the object without using its name, making your code more concise and readable. + +## ๐Ÿ“‹ Learning Objectives + +By the end of this lesson, you will be able to: +- โœ… Understand the five main scope functions: `let`, `run`, `with`, `apply`, and `also` +- โœ… Choose the right scope function for different use cases +- โœ… Use scope functions to make your code more readable and concise +- โœ… Understand the differences between scope functions +- โœ… Apply scope functions in real-world scenarios + +## ๐Ÿ” What You'll Learn + +- **Scope function types** - Understanding the five main scope functions +- **Context and return values** - How each function handles the context object +- **Use cases** - When to use each scope function +- **Best practices** - Guidelines for effective scope function usage +- **Real-world examples** - Practical applications in everyday coding + +## ๐Ÿ“ Prerequisites + +- Understanding of [Lambdas and Higher-Order Functions](01-lambdas.md) +- Knowledge of [Functions](../functions/01-functions-basics.md) +- Familiarity with [Object-Oriented Programming](../oop/01-classes-constructors.md) + +## ๐Ÿ’ป The Code + +Let's examine the scope functions examples: + +**๐Ÿ“ File:** [39_with_apply_functions.kt](../src/39_with_apply_functions.kt) + +## ๐Ÿ” Code Breakdown + +### **1. The `let` Function** + +#### **Basic Usage** +```kotlin +// let provides the object as 'it' and returns the lambda result +val name: String? = "Alice" +val length = name?.let { + println("Name: $it") + it.length // Return value +} + +// Safe call with let +val nullableName: String? = null +val result = nullableName?.let { + "Hello, $it!" +} ?: "Hello, Guest!" +``` + +#### **Chaining with let** +```kotlin +val numbers = listOf(1, 2, 3, 4, 5) +val result = numbers + .filter { it % 2 == 0 } + .let { evenNumbers -> + println("Even numbers: $evenNumbers") + evenNumbers.sum() + } +``` + +### **2. The `run` Function** + +#### **Basic Usage** +```kotlin +// run provides the object as 'this' and returns the lambda result +val person = Person("Alice", 25) +val description = person.run { + println("Processing: $name") + "Name: $name, Age: $age" // Return value +} +``` + +#### **Extension Function Style** +```kotlin +val result = "Hello, World!".run { + println("Original: $this") + uppercase() + length +} +``` + +#### **Non-Extension run** +```kotlin +val result = run { + val name = "Alice" + val age = 25 + "$name is $age years old" +} +``` + +### **3. The `with` Function** + +#### **Basic Usage** +```kotlin +// with provides the object as 'this' and returns the lambda result +val person = Person("Bob", 30) +val description = with(person) { + println("Name: $name") + println("Age: $age") + "Person: $name ($age years old)" // Return value +} +``` + +#### **Multiple Operations** +```kotlin +val numbers = mutableListOf(1, 2, 3, 4, 5) +with(numbers) { + add(6) + add(7) + remove(1) + println("Size: $size") + sum() // Return value +} +``` + +### **4. The `apply` Function** + +#### **Basic Usage** +```kotlin +// apply provides the object as 'this' and returns the object itself +val person = Person("Charlie", 35).apply { + println("Setting up: $name") + // Can modify properties if they're mutable +} + +// Common use case: object configuration +val button = Button().apply { + text = "Click Me" + color = "Blue" + size = 100 + onClick = { println("Button clicked!") } +} +``` + +#### **Builder Pattern** +```kotlin +val email = EmailBuilder().apply { + to = "user@example.com" + subject = "Hello" + body = "This is a test email" + priority = "High" +}.build() +``` + +### **5. The `also` Function** + +#### **Basic Usage** +```kotlin +// also provides the object as 'it' and returns the object itself +val numbers = mutableListOf(1, 2, 3) +val result = numbers.also { + println("Original list: $it") + it.add(4) + println("After adding 4: $it") +} +// result is the same list, not the println result +``` + +#### **Logging and Side Effects** +```kotlin +val person = Person("David", 40).also { + println("Created person: ${it.name}") + println("Age: ${it.age}") +} +``` + +### **6. Comparing Scope Functions** + +#### **Function Comparison Table** +```kotlin +data class Person(var name: String, var age: Int) + +fun demonstrateScopeFunctions() { + val person = Person("Alice", 25) + + // let - returns lambda result, object as 'it' + val letResult = person.let { + "Name: ${it.name}" + } + + // run - returns lambda result, object as 'this' + val runResult = person.run { + "Name: $name" + } + + // with - returns lambda result, object as 'this' + val withResult = with(person) { + "Name: $name" + } + + // apply - returns object, object as 'this' + val applyResult = person.apply { + age = 26 + } + + // also - returns object, object as 'it' + val alsoResult = person.also { + println("Person: ${it.name}") + } + + println("let: $letResult") + println("run: $runResult") + println("with: $withResult") + println("apply: $applyResult") + println("also: $alsoResult") +} +``` + +## ๐ŸŽฏ Key Concepts Explained + +### **1. Context Object: `this` vs `it`** +```kotlin +val person = Person("Alice", 25) + +// Functions that use 'this' (run, with, apply) +person.run { + name = "Bob" // Direct access to properties + age = 30 +} + +// Functions that use 'it' (let, also) +person.let { + it.name = "Charlie" // Access via 'it' + it.age = 35 +} +``` + +### **2. Return Values** +```kotlin +val person = Person("Alice", 25) + +// Functions that return lambda result +val letResult = person.let { it.name.length } // Int +val runResult = person.run { name.length } // Int +val withResult = with(person) { name.length } // Int + +// Functions that return the object +val applyResult = person.apply { age = 26 } // Person +val alsoResult = person.also { println(it.name) } // Person +``` + +### **3. When to Use Each Function** + +#### **Use `let` when:** +- Executing a lambda on non-null values +- Introducing local variables with limited scope +- Chaining operations + +#### **Use `run` when:** +- Initializing objects and computing the return value +- Executing a block of statements where an expression is needed + +#### **Use `with` when:** +- Calling multiple functions on the same object +- Grouping function calls on an object + +#### **Use `apply` when:** +- Configuring objects +- Setting up object properties +- Builder pattern implementations + +#### **Use `also` when:** +- Adding actions that don't affect the object +- Logging or debugging +- Side effects that don't change the object + +## ๐Ÿงช Running the Examples + +### **Step 1: Open the File** +1. Navigate to `src/39_with_apply_functions.kt` +2. Open it in IntelliJ IDEA + +### **Step 2: Run the Program** +1. Right-click on the file +2. Select "Run '39_with_apply_functionsKt'" +3. Observe the output + +## ๐ŸŽฎ Hands-On Exercises + +### **Exercise 1: Person Configuration** +Create a Person class and configure it using different scope functions. + +**Solution:** +```kotlin +data class Person( + var name: String = "", + var age: Int = 0, + var email: String = "", + var city: String = "" +) + +fun main() { + // Using apply for configuration + val person1 = Person().apply { + name = "Alice" + age = 25 + email = "alice@email.com" + city = "New York" + } + + // Using let for safe operations + val person2 = Person("Bob", 30, "bob@email.com", "Boston") + val description = person2.let { + if (it.age>= 18) { + "Adult: ${it.name} from ${it.city}" + } else { + "Minor: ${it.name}" + } + } + + // Using with for multiple operations + val person3 = Person("Charlie", 35, "charlie@email.com", "Chicago") + val result = with(person3) { + println("Processing: $name") + println("Age: $age") + println("Location: $city") + "Processed: $name" + } + + println("Person 1: $person1") + println("Description: $description") + println("Result: $result") +} +``` + +### **Exercise 2: Collection Processing** +Use scope functions to process collections in different ways. + +**Solution:** +```kotlin +fun main() { + val numbers = listOf(1, 2, 3, 4, 5, 6, 7, 8, 9, 10) + + // Using let for chaining + val evenSum = numbers + .filter { it % 2 == 0 } + .let { evenNumbers -> + println("Even numbers: $evenNumbers") + evenNumbers.sum() + } + + // Using run for processing + val statistics = numbers.run { + val count = size + val sum = sum() + val average = sum.toDouble() / count + val max = maxOrNull() ?: 0 + val min = minOrNull() ?: 0 + + mapOf( + "count" to count, + "sum" to sum, + "average" to average, + "max" to max, + "min" to min + ) + } + + // Using with for multiple operations + val processed = with(numbers) { + val doubled = map { it * 2 } + val filtered = doubled.filter { it> 10 } + val sorted = filtered.sortedDescending() + sorted.take(3) + } + + println("Even sum: $evenSum") + println("Statistics: $statistics") + println("Processed: $processed") +} +``` + +### **Exercise 3: Builder Pattern** +Implement a builder pattern using scope functions. + +**Solution:** +```kotlin +class HTMLBuilder { + private val content = StringBuilder() + + fun tag(name: String, content: String) { + this.content.append("<$name>$content") + } + + fun tag(name: String, init: HTMLBuilder.() -> Unit) { + this.content.append("<$name>") + init() + this.content.append("") + } + + fun build(): String = content.toString() +} + +fun html(init: HTMLBuilder.() -> Unit): String { + return HTMLBuilder().apply(init).build() +} + +fun main() { + val htmlContent = html { + tag("html") { + tag("head") { + tag("title", "My Page") + } + tag("body") { + tag("h1", "Welcome") + tag("p", "This is a paragraph") + tag("div") { + tag("span", "Nested content") + } + } + } + } + + println(htmlContent) +} +``` + +### **Exercise 4: Configuration Management** +Create a configuration system using scope functions. + +**Solution:** +```kotlin +data class DatabaseConfig( + var host: String = "localhost", + var port: Int = 5432, + var username: String = "", + var password: String = "", + var database: String = "" +) + +data class AppConfig( + var name: String = "", + var version: String = "", + var debug: Boolean = false, + var database: DatabaseConfig = DatabaseConfig() +) + +fun createConfig(init: AppConfig.() -> Unit): AppConfig { + return AppConfig().apply(init) +} + +fun main() { + val config = createConfig { + name = "MyApp" + version = "1.0.0" + debug = true + database.apply { + host = "production.db.com" + port = 5432 + username = "admin" + password = "secret" + database = "myapp_prod" + } + } + + // Using let for safe operations + config.database.let { db -> + if (db.host != "localhost") { + println("Production database: ${db.host}:${db.port}") + } + } + + // Using with for multiple operations + with(config) { + println("App: $name v$version") + println("Debug mode: $debug") + println("Database: ${database.host}:${database.port}") + } + + // Using also for logging + config.also { + println("Configuration created successfully") + println("Total config size: ${it.name.length + it.version.length}") + } +} +``` + +## ๐Ÿšจ Common Mistakes to Avoid + +1. **Overusing scope functions**: Don't use them when simple property access is clearer โœ… +2. **Confusing return values**: Remember which functions return the object vs lambda result โœ… +3. **Nesting scope functions**: Can make code hard to read - use sparingly โœ… +4. **Ignoring context**: Choose the right function based on whether you need `this` or `it` โœ… + +## ๐ŸŽฏ What's Next? + +Now that you understand scope functions, continue with: + +1. **Collections** โ†’ [Collections Overview](../collections/README.md) +2. **Null Safety** โ†’ [Null Safety](../null-safety/01-null-safety.md) +3. **Coroutines** โ†’ [Coroutines](../coroutines/01-introduction.md) +4. **Advanced OOP** โ†’ [Object-Oriented Programming](../oop/README.md) + +## ๐Ÿ“š Additional Resources + +- [Official Kotlin Scope Functions Documentation](https://kotlinlang.org/docs/scope-functions.html) +- [Scope Functions Guide](https://kotlinlang.org/docs/scope-functions.html#scope-functions) +- [Kotlin Standard Library](https://kotlinlang.org/api/latest/jvm/stdlib/) + +## ๐Ÿ† Summary + +- โœ… You understand the five main scope functions and their differences +- โœ… You can choose the right scope function for different use cases +- โœ… You know when to use `this` vs `it` context +- โœ… You can apply scope functions to make code more readable +- โœ… You're ready to write more concise and expressive Kotlin code! + +**Scope functions are powerful tools for making your code more readable and concise. Use them wisely! ๐ŸŽ‰** diff --git a/docs/functions/02-functions-as-expressions.md b/docs/functions/02-functions-as-expressions.md index a70c160..3869313 100644 --- a/docs/functions/02-functions-as-expressions.md +++ b/docs/functions/02-functions-as-expressions.md @@ -518,3 +518,4 @@ Excellent! You've mastered functions as expressions in Kotlin. Now you're ready - โœ… You're ready to create elegant, functional Kotlin code! **Keep practicing and happy coding! ๐ŸŽ‰** + diff --git a/docs/functions/03-named-parameters.md b/docs/functions/03-named-parameters.md index 2719b3b..1303a1b 100644 --- a/docs/functions/03-named-parameters.md +++ b/docs/functions/03-named-parameters.md @@ -643,3 +643,4 @@ Excellent! You've mastered named parameters in Kotlin. Now you're ready to: - โœ… You're ready to create self-documenting function calls! **Keep practicing and happy coding! ๐ŸŽ‰** + diff --git a/docs/functions/04-extension-functions.md b/docs/functions/04-extension-functions.md index 84387cf..abaca2c 100644 --- a/docs/functions/04-extension-functions.md +++ b/docs/functions/04-extension-functions.md @@ -607,3 +607,4 @@ Excellent! You've mastered extension functions in Kotlin. Now you're ready to: - โœ… You're ready to extend any class with new functionality! **Keep practicing and happy coding! ๐ŸŽ‰** + diff --git a/docs/null-safety/01-null-safety.md b/docs/null-safety/01-null-safety.md new file mode 100644 index 0000000..8f244a7 --- /dev/null +++ b/docs/null-safety/01-null-safety.md @@ -0,0 +1,443 @@ +# ๐Ÿ›ก๏ธ Null Safety in Kotlin + +Null safety is one of Kotlin's most powerful features, designed to eliminate the dreaded `NullPointerException` that plagues many Java applications. Kotlin's type system distinguishes between nullable and non-nullable types, making your code safer and more predictable. + +## ๐Ÿ“‹ Learning Objectives + +By the end of this lesson, you will be able to: +- โœ… Understand the difference between nullable and non-nullable types +- โœ… Use safe call operators (`?.`) and null coalescing (`?:`) +- โœ… Implement null checks and smart casts +- โœ… Work with safe casting (`as?`) +- โœ… Use the `!!` operator safely and understand its risks + +## ๐Ÿ” What You'll Learn + +- **Type system** - How Kotlin distinguishes nullable from non-nullable types +- **Safe operators** - Using `?.`, `?:`, and `as?` safely +- **Null checks** - Implementing proper null checking strategies +- **Smart casts** - How Kotlin automatically handles null checks +- **Best practices** - When and how to use different null safety approaches + +## ๐Ÿ“ Prerequisites + +- Understanding of [Variables and Data Types](../basics/02-variables-data-types.md) +- Knowledge of [Functions](../functions/01-functions-basics.md) +- Familiarity with [Control Flow](../control-flow/01-if-expressions.md) + +## ๐Ÿ’ป The Code + +Let's examine the null safety examples: + +**๐Ÿ“ File:** [46_null_safety.kt](../src/46_null_safety.kt) + +## ๐Ÿ” Code Breakdown + +### **1. Nullable vs Non-Nullable Types** + +#### **Non-Nullable Types (Default)** +```kotlin +// These variables cannot hold null values +val name: String = "Alice" // Non-nullable String +val age: Int = 25 // Non-nullable Int +val isStudent: Boolean = true // Non-nullable Boolean + +// This would cause a compilation error: +// val name: String = null // โŒ Compilation error +``` + +#### **Nullable Types (Explicit Declaration)** +```kotlin +// These variables can hold null values +val nullableName: String? = "Alice" // Nullable String +val nullableAge: Int? = 25 // Nullable Int +val nullableStudent: Boolean? = true // Nullable Boolean + +// They can also be assigned null: +val nullName: String? = null // โœ… Valid +val nullAge: Int? = null // โœ… Valid +``` + +### **2. Safe Call Operator (`?.`)** + +#### **Basic Safe Call** +```kotlin +val name: String? = "Alice" +val length = name?.length // Safe call - returns Int? (nullable) + +val nullName: String? = null +val nullLength = nullName?.length // Safe call - returns null +``` + +#### **Chaining Safe Calls** +```kotlin +data class Person(val name: String?, val address: Address?) +data class Address(val street: String?, val city: String?) + +val person: Person? = Person("Alice", Address("Main St", "New York")) +val city = person?.address?.city // Safe call chain + +val nullPerson: Person? = null +val nullCity = nullPerson?.address?.city // Returns null safely +``` + +#### **Safe Call with Functions** +```kotlin +val name: String? = "Alice" +val upperName = name?.uppercase() // Safe call to function + +val nullName: String? = null +val upperNullName = nullName?.uppercase() // Returns null safely +``` + +### **3. Null Coalescing Operator (`?:`)** + +#### **Basic Null Coalescing** +```kotlin +val name: String? = "Alice" +val displayName = name ?: "Unknown" // Use "Unknown" if name is null + +val nullName: String? = null +val displayNullName = nullName ?: "Unknown" // Returns "Unknown" +``` + +#### **Null Coalescing with Expressions** +```kotlin +val name: String? = null +val length = name?.length ?: 0 // Use 0 if name or length is null + +val age: Int? = null +val status = age?.let { if (it>= 18) "Adult" else "Minor" } ?: "Unknown" +``` + +#### **Null Coalescing in Function Calls** +```kotlin +fun greet(name: String?) { + val displayName = name ?: "Guest" + println("Hello, $displayName!") +} + +greet("Alice") // Prints: Hello, Alice! +greet(null) // Prints: Hello, Guest! +``` + +### **4. Null Checks and Smart Casts** + +#### **Explicit Null Check** +```kotlin +fun processName(name: String?) { + if (name != null) { + // Kotlin knows name is not null in this block + println("Name length: ${name.length}") // Safe to call .length + println("Uppercase: ${name.uppercase()}") // Safe to call .uppercase() + } else { + println("Name is null") + } +} +``` + +#### **Smart Cast with When Expression** +```kotlin +fun describeValue(value: Any?) { + when (value) { + null -> println("Value is null") + is String -> println("String: ${value.length} characters") // Smart cast to String + is Int -> println("Integer: ${value + 10}") // Smart cast to Int + is Boolean -> println("Boolean: ${value.not()}") // Smart cast to Boolean + else -> println("Other type: ${value.javaClass.simpleName}") + } +} +``` + +#### **Smart Cast with Elvis Operator** +```kotlin +fun getLength(value: String?): Int { + return value?.length ?: 0 +} + +fun getLengthAlternative(value: String?): Int { + return if (value != null) value.length else 0 // Smart cast in if block +} +``` + +### **5. Safe Casting (`as?`)** + +#### **Safe Cast Operator** +```kotlin +val anyValue: Any = "Hello" +val stringValue = anyValue as? String // Safe cast to String +val intValue = anyValue as? Int // Safe cast to Int (returns null) + +println(stringValue) // "Hello" +println(intValue) // null +``` + +#### **Safe Cast with Null Coalescing** +```kotlin +val anyValue: Any = "42" +val intValue = (anyValue as? String)?.toIntOrNull() ?: 0 + +val nullValue: Any? = null +val safeInt = (nullValue as? String)?.toIntOrNull() ?: 0 +``` + +### **6. The `!!` Operator (Use with Caution)** + +#### **Non-Null Assertion** +```kotlin +val name: String? = "Alice" +val length = name!!.length // Asserts name is not null + +val nullName: String? = null +// val nullLength = nullName!!.length // โŒ Throws NullPointerException at runtime +``` + +#### **When to Use `!!`** +```kotlin +// Only use !! when you're absolutely certain the value is not null +fun processNonNullName(name: String?) { + requireNotNull(name) { "Name cannot be null" } // Better than !! + + // Now Kotlin knows name is not null + println("Processing: ${name.length}") // Safe to use +} +``` + +### **7. Collection Null Safety** + +#### **Nullable Collections** +```kotlin +val nullableList: List? = listOf("Alice", "Bob") +val size = nullableList?.size ?: 0 + +val nullList: List? = null +val safeSize = nullList?.size ?: 0 +``` + +#### **Collections with Nullable Elements** +```kotlin +val names: List = listOf("Alice", null, "Bob", null, "Charlie") + +// Filter out null values +val nonNullNames = names.filterNotNull() // ["Alice", "Bob", "Charlie"] + +// Safe processing +names.forEach { name -> + name?.let { println("Name: $it") } // Only print non-null names +} +``` + +## ๐ŸŽฏ Key Concepts Explained + +### **1. Type System Safety** +```kotlin +// Kotlin's type system prevents null assignment to non-nullable types +var name: String = "Alice" // Non-nullable +// name = null // โŒ Compilation error + +var nullableName: String? = "Alice" // Nullable +nullableName = null // โœ… Valid +``` + +### **2. Smart Casts** +```kotlin +fun example(value: String?) { + if (value != null) { + // Kotlin automatically casts value to non-nullable String + val length = value.length // Safe to use + val upper = value.uppercase() // Safe to use + } + // Outside this block, value is still nullable +} +``` + +### **3. Safe Call vs Null Check** +```kotlin +val name: String? = "Alice" + +// Safe call - concise but returns nullable result +val length1 = name?.length // Int? + +// Null check - more verbose but gives non-nullable result +val length2 = if (name != null) name.length else 0 // Int +``` + +## ๐Ÿงช Running the Examples + +### **Step 1: Open the File** +1. Navigate to `src/46_null_safety.kt` +2. Open it in IntelliJ IDEA + +### **Step 2: Run the Program** +1. Right-click on the file +2. Select "Run '46_null_safetyKt'" +3. Observe the output + +## ๐ŸŽฎ Hands-On Exercises + +### **Exercise 1: Safe String Processing** +Create functions that safely process nullable strings. + +**Solution:** +```kotlin +fun processString(input: String?): String { + return input?.trim()?.uppercase() ?: "DEFAULT" +} + +fun getStringLength(input: String?): Int { + return input?.length ?: 0 +} + +fun main() { + println(processString(" hello world ")) // "HELLO WORLD" + println(processString(null)) // "DEFAULT" + + println(getStringLength("Hello")) // 5 + println(getStringLength(null)) // 0 +} +``` + +### **Exercise 2: Nullable Person Processing** +Create a Person class with nullable fields and process them safely. + +**Solution:** +```kotlin +data class Person( + val name: String?, + val age: Int?, + val email: String? +) + +fun processPerson(person: Person?): String { + if (person == null) return "No person data" + + val name = person.name ?: "Unknown" + val age = person.age?.toString() ?: "Unknown" + val email = person.email ?: "No email" + + return "Name: $name, Age: $age, Email: $email" +} + +fun main() { + val person1 = Person("Alice", 25, "alice@email.com") + val person2 = Person("Bob", null, null) + val person3 = Person(null, 30, "bob@email.com") + + println(processPerson(person1)) // "Name: Alice, Age: 25, Email: alice@email.com" + println(processPerson(person2)) // "Name: Bob, Age: Unknown, Email: No email" + println(processPerson(person3)) // "Name: Unknown, Age: 30, Email: bob@email.com" + println(processPerson(null)) // "No person data" +} +``` + +### **Exercise 3: Safe Collection Processing** +Process collections that may contain null values. + +**Solution:** +```kotlin +fun processNullableList(list: List?): List { + return list?.filterNotNull()?.map { it.uppercase() } ?: emptyList() +} + +fun safeListOperations(list: List?): Map { + if (list == null) return mapOf("error" to "List is null") + + val nonNullNumbers = list.filterNotNull() + if (nonNullNumbers.isEmpty()) return mapOf("error" to "No valid numbers") + + return mapOf( + "count" to nonNullNumbers.size, + "sum" to nonNullNumbers.sum(), + "average" to nonNullNumbers.average(), + "min" to nonNullNumbers.minOrNull() ?: 0, + "max" to nonNullNumbers.maxOrNull() ?: 0 + ) +} + +fun main() { + val list1 = listOf("hello", null, "world", null, "kotlin") + val list2: List? = null + val list3: List? = listOf(1, null, 3, null, 5) + + println(processNullableList(list1)) // [HELLO, WORLD, KOTLIN] + println(processNullableList(list2)) // [] + + println(safeListOperations(list3)) // {count=3, sum=9.0, average=3.0, min=1, max=5} +} +``` + +### **Exercise 4: Complex Null Safety** +Handle complex nested nullable structures. + +**Solution:** +```kotlin +data class Address(val street: String?, val city: String?, val country: String?) +data class Contact(val phone: String?, val email: String?) +data class Employee(val name: String?, val address: Address?, val contact: Contact?) + +fun getEmployeeInfo(employee: Employee?): String { + return employee?.let { emp -> + val name = emp.name ?: "Unknown" + val street = emp.address?.street ?: "No street" + val city = emp.address?.city ?: "No city" + val phone = emp.contact?.phone ?: "No phone" + val email = emp.contact?.email ?: "No email" + + "Employee: $name\nAddress: $street, $city\nContact: $phone, $email" + } ?: "No employee data" +} + +fun main() { + val employee1 = Employee( + "Alice", + Address("Main St", "New York", "USA"), + Contact("123-456-7890", "alice@company.com") + ) + + val employee2 = Employee( + "Bob", + Address(null, "Boston", null), + Contact(null, null) + ) + + println(getEmployeeInfo(employee1)) + println("\n" + getEmployeeInfo(employee2)) + println("\n" + getEmployeeInfo(null)) +} +``` + +## ๐Ÿšจ Common Mistakes to Avoid + +1. **Using `!!` without null checks**: Can cause runtime exceptions โŒ + - **Better**: Use safe calls or explicit null checks โœ… + +2. **Forgetting nullable types**: Always consider if a value can be null โœ… + +3. **Ignoring smart casts**: Let Kotlin handle type safety automatically โœ… + +4. **Not using safe operators**: `?.` and `?:` make code more readable โœ… + +## ๐ŸŽฏ What's Next? + +Now that you understand null safety, continue with: + +1. **Lateinit and Lazy** โ†’ [Lateinit and Lazy Keywords](02-lateinit-lazy.md) +2. **Scope functions** โ†’ [Scope Functions](../functional-programming/02-scope-functions.md) +3. **Collections** โ†’ [Collections Overview](../collections/README.md) +4. **Coroutines** โ†’ [Coroutines](../coroutines/01-introduction.md) + +## ๐Ÿ“š Additional Resources + +- [Official Kotlin Null Safety Documentation](https://kotlinlang.org/docs/null-safety.html) +- [Kotlin Type System](https://kotlinlang.org/docs/types.html) +- [Safe Calls and Elvis Operator](https://kotlinlang.org/docs/null-safety.html#safe-calls) + +## ๐Ÿ† Summary + +- โœ… You understand nullable vs non-nullable types +- โœ… You can use safe call operators (`?.`) safely +- โœ… You know how to implement null checks and smart casts +- โœ… You understand when to use the `!!` operator (rarely!) +- โœ… You're ready to write safer, more robust Kotlin code! + +**Null safety is one of Kotlin's greatest strengths. Use it to eliminate null pointer exceptions! ๐ŸŽ‰** diff --git a/docs/null-safety/02-lateinit-lazy.md b/docs/null-safety/02-lateinit-lazy.md new file mode 100644 index 0000000..936f61b --- /dev/null +++ b/docs/null-safety/02-lateinit-lazy.md @@ -0,0 +1,731 @@ +# โฐ Lateinit and Lazy in Kotlin + +The `lateinit` and `lazy` keywords are Kotlin's solutions for handling properties that need to be initialized later or on-demand. They provide different approaches to delayed initialization, each with their own use cases and benefits. + +## ๐Ÿ“‹ Learning Objectives + +By the end of this lesson, you will be able to: +- โœ… Understand when and how to use `lateinit` for delayed initialization +- โœ… Use `lazy` for on-demand initialization with caching +- โœ… Choose between `lateinit` and `lazy` for different scenarios +- โœ… Handle initialization checks and error cases +- โœ… Apply these patterns in real-world scenarios + +## ๐Ÿ” What You'll Learn + +- **Lateinit keyword** - Properties initialized after declaration +- **Lazy initialization** - Properties initialized on first access +- **Initialization timing** - When each approach initializes values +- **Use case scenarios** - When to use each approach +- **Best practices** - Guidelines for effective usage + +## ๐Ÿ“ Prerequisites + +- Understanding of [Null Safety](01-null-safety.md) +- Knowledge of [Variables and Data Types](../basics/02-variables-data-types.md) +- Familiarity with [Classes](../oop/01-classes-constructors.md) + +## ๐Ÿ’ป The Code + +Let's examine the lateinit and lazy examples: + +**๐Ÿ“ File:** [47_lateinit_keyword.kt](../src/47_lateinit_keyword.kt) + +## ๐Ÿ” Code Breakdown + +### **1. The `lateinit` Keyword** + +#### **Basic Usage** +```kotlin +class UserManager { + // Property declared but not initialized + lateinit var userName: String + + // Property declared but not initialized + lateinit var userEmail: String + + fun initializeUser(name: String, email: String) { + // Initialize the properties later + userName = name + userEmail = email + } + + fun getUserInfo(): String { + // Check if initialized before use + if (::userName.isInitialized && ::userEmail.isInitialized) { + return "User: $userName, Email: $userEmail" + } + return "User not initialized" + } +} +``` + +#### **Lateinit with Different Types** +```kotlin +class Configuration { + // String properties + lateinit var appName: String + lateinit var version: String + + // Custom object properties + lateinit var database: Database + lateinit var logger: Logger + + // Array and collection properties + lateinit var settings: Array + lateinit var users: List +} +``` + +#### **Initialization Checks** +```kotlin +class DataProcessor { + lateinit var dataSource: DataSource + + fun processData() { + // Check if initialized before use + if (::dataSource.isInitialized) { + dataSource.process() + } else { + throw IllegalStateException("DataSource not initialized") + } + } + + fun safeProcessData() { + // Safe access with initialization check + if (::dataSource.isInitialized) { + dataSource.process() + } else { + println("DataSource not ready, skipping processing") + } + } +} +``` + +### **2. The `lazy` Delegate** + +#### **Basic Lazy Initialization** +```kotlin +class ExpensiveObject { + // Property initialized on first access + val expensiveValue by lazy { + println("Computing expensive value...") + // Simulate expensive computation + Thread.sleep(1000) + "Expensive computed result" + } + + // Another lazy property + val configuration by lazy { + println("Loading configuration...") + mapOf( + "timeout" to 5000, + "retries" to 3, + "cacheSize" to 1000 + ) + } +} +``` + +#### **Lazy with Different Initialization Logic** +```kotlin +class UserService { + // Lazy property with custom logic + val userCache by lazy { + println("Initializing user cache...") + mutableMapOf() + } + + // Lazy property with parameters + val defaultUser by lazy { + User( + id = 0, + name = "Default User", + email = "default@example.com" + ) + } + + // Lazy property with computation + val userCount by lazy { + println("Counting users...") + userCache.size + } +} +``` + +#### **Lazy with Thread Safety** +```kotlin +class ThreadSafeService { + // Thread-safe lazy initialization + val sharedResource by lazy(LazyThreadSafetyMode.SYNCHRONIZED) { + println("Initializing shared resource...") + "Thread-safe resource" + } + + // Publication-safe lazy initialization (default) + val safeResource by lazy(LazyThreadSafetyMode.PUBLICATION) { + println("Initializing publication-safe resource...") + "Publication-safe resource" + } + + // Non-thread-safe lazy initialization + val fastResource by lazy(LazyThreadSafetyMode.NONE) { + println("Initializing fast resource...") + "Fast resource" + } +} +``` + +### **3. Comparing Lateinit vs Lazy** + +#### **Initialization Timing** +```kotlin +class ComparisonExample { + // Lateinit - must be initialized manually + lateinit var lateinitProperty: String + + // Lazy - initialized on first access + val lazyProperty by lazy { + println("Lazy property initialized") + "Lazy value" + } + + fun demonstrateTiming() { + println("1. Function started") + + // Lateinit property not accessed yet + println("2. Lateinit property declared but not initialized") + + // Lazy property not accessed yet + println("3. Lazy property declared but not initialized") + + // Access lazy property - triggers initialization + println("4. Accessing lazy property: $lazyProperty") + + // Initialize lateinit property + lateinitProperty = "Lateinit value" + println("5. Lateinit property initialized: $lateinitProperty") + } +} +``` + +#### **Use Case Examples** +```kotlin +class UseCaseExamples { + // Use lateinit when: + // - Property will be initialized by dependency injection + // - Property will be set by external framework + // - You need to control initialization timing + + lateinit var database: Database + lateinit var networkService: NetworkService + lateinit var userPreferences: UserPreferences + + // Use lazy when: + // - Property is expensive to compute + // - Property is not always needed + // - You want automatic caching + + val expensiveCalculation by lazy { + performExpensiveCalculation() + } + + val cachedData by lazy { + loadDataFromCache() + } + + val defaultSettings by lazy { + loadDefaultSettings() + } + + private fun performExpensiveCalculation(): String { + // Simulate expensive operation + Thread.sleep(2000) + return "Expensive result" + } + + private fun loadDataFromCache(): Map { + return mapOf("cached" to true, "timestamp" to System.currentTimeMillis()) + } + + private fun loadDefaultSettings(): Map { + return mapOf("theme" to "dark", "language" to "en") + } +} +``` + +### **4. Practical Examples** + +#### **Dependency Injection Pattern** +```kotlin +class UserController { + // Dependencies injected later + lateinit var userService: UserService + lateinit var authService: AuthService + lateinit var logger: Logger + + fun initializeDependencies( + userService: UserService, + authService: AuthService, + logger: Logger + ) { + this.userService = userService + this.authService = authService + this.logger = logger + } + + fun createUser(userData: UserData): User { + // Check if dependencies are initialized + if (!::userService.isInitialized || !::authService.isInitialized) { + throw IllegalStateException("Dependencies not initialized") + } + + logger.log("Creating user: ${userData.name}") + return userService.createUser(userData) + } +} +``` + +#### **Configuration Management** +```kotlin +class AppConfiguration { + // Configuration loaded on demand + val databaseConfig by lazy { + loadDatabaseConfig() + } + + val apiConfig by lazy { + loadApiConfig() + } + + val featureFlags by lazy { + loadFeatureFlags() + } + + private fun loadDatabaseConfig(): DatabaseConfig { + println("Loading database configuration...") + return DatabaseConfig( + host = "localhost", + port = 5432, + database = "myapp" + ) + } + + private fun loadApiConfig(): ApiConfig { + println("Loading API configuration...") + return ApiConfig( + baseUrl = "https://api.example.com", + timeout = 30000, + retries = 3 + ) + } + + private fun loadFeatureFlags(): Map { + println("Loading feature flags...") + return mapOf( + "newUI" to true, + "betaFeatures" to false, + "analytics" to true + ) + } +} +``` + +#### **Resource Management** +```kotlin +class ResourceManager { + // Resources created on demand + val connectionPool by lazy { + createConnectionPool() + } + + val cache by lazy { + createCache() + } + + val threadPool by lazy { + createThreadPool() + } + + private fun createConnectionPool(): ConnectionPool { + println("Creating connection pool...") + return ConnectionPool(maxConnections = 10) + } + + private fun createCache(): Cache { + println("Creating cache...") + return Cache(maxSize = 1000) + } + + private fun createThreadPool(): ThreadPool { + println("Creating thread pool...") + return ThreadPool(coreSize = 4, maxSize = 8) + } +} + +// Mock classes for demonstration +class ConnectionPool(val maxConnections: Int) +class Cache(val maxSize: Int) +class ThreadPool(val coreSize: Int, val maxSize: Int) +``` + +## ๐ŸŽฏ Key Concepts Explained + +### **1. Lateinit Characteristics** +```kotlin +// Lateinit properties: +// - Must be declared as 'var' (mutable) +// - Cannot be nullable +// - Must be initialized before use +// - Can be changed after initialization +// - Use ::property.isInitialized to check status + +class LateinitExample { + lateinit var property: String + + fun initialize() { + property = "Initial value" + } + + fun changeValue() { + property = "New value" // Can be changed + } + + fun checkStatus() { + if (::property.isInitialized) { + println("Property is initialized: $property") + } else { + println("Property is not initialized") + } + } +} +``` + +### **2. Lazy Characteristics** +```kotlin +// Lazy properties: +// - Must be declared as 'val' (immutable) +// - Initialized on first access +// - Cached for subsequent accesses +// - Thread-safe by default +// - Cannot be changed after initialization + +class LazyExample { + val property by lazy { + println("Initializing...") + "Computed value" + } + + fun accessProperty() { + println("First access: $property") // Triggers initialization + println("Second access: $property") // Uses cached value + } +} +``` + +### **3. When to Use Each** + +#### **Use `lateinit` when:** +- Property will be initialized by external code (DI, framework) +- You need to control initialization timing +- Property value will change after initialization +- Property is not always needed + +#### **Use `lazy` when:** +- Property is expensive to compute +- Property should be cached after first access +- Property is not always needed +- You want automatic initialization + +## ๐Ÿงช Running the Examples + +### **Step 1: Open the File** +1. Navigate to `src/47_lateinit_keyword.kt` +2. Open it in IntelliJ IDEA + +### **Step 2: Run the Program** +1. Right-click on the file +2. Select "Run '47_lateinit_keywordKt'" +3. Observe the output + +## ๐ŸŽฎ Hands-On Exercises + +### **Exercise 1: Lateinit Property Management** +Create a class with lateinit properties and manage their lifecycle. + +**Solution:** +```kotlin +class TaskManager { + lateinit var taskExecutor: TaskExecutor + lateinit var taskQueue: TaskQueue + lateinit var logger: Logger + + fun initialize(executor: TaskExecutor, queue: TaskQueue, log: Logger) { + taskExecutor = executor + taskQueue = queue + logger = log + } + + fun executeTask(task: Task) { + if (!::taskExecutor.isInitialized || !::taskQueue.isInitialized) { + throw IllegalStateException("TaskManager not initialized") + } + + logger.log("Executing task: ${task.name}") + taskQueue.add(task) + taskExecutor.execute(task) + } + + fun isReady(): Boolean { + return ::taskExecutor.isInitialized && + ::taskQueue.isInitialized && + ::logger.isInitialized + } +} + +// Mock classes +class Task(val name: String) +class TaskExecutor { fun execute(task: Task) {} } +class TaskQueue { fun add(task: Task) {} } +class Logger { fun log(message: String) {} } + +fun main() { + val manager = TaskManager() + + println("Manager ready: ${manager.isReady()}") + + try { + manager.executeTask(Task("Test Task")) + } catch (e: Exception) { + println("Error: ${e.message}") + } + + manager.initialize(TaskExecutor(), TaskQueue(), Logger()) + println("Manager ready: ${manager.isReady()}") + + manager.executeTask(Task("Test Task")) +} +``` + +### **Exercise 2: Lazy Configuration Loading** +Implement a configuration system using lazy initialization. + +**Solution:** +```kotlin +class ConfigurationLoader { + val databaseConfig by lazy { + println("Loading database config...") + loadFromFile("database.properties") + } + + val apiConfig by lazy { + println("Loading API config...") + loadFromFile("api.properties") + } + + val userConfig by lazy { + println("Loading user config...") + loadFromFile("user.properties") + } + + private fun loadFromFile(filename: String): Map { + // Simulate file loading + Thread.sleep(500) + return mapOf( + "filename" to filename, + "loaded" to "true", + "timestamp" to System.currentTimeMillis().toString() + ) + } +} + +fun main() { + val config = ConfigurationLoader() + + println("Configuration object created") + + // Access properties - triggers lazy initialization + println("Database config: ${config.databaseConfig}") + println("API config: ${config.apiConfig}") + + // Second access uses cached values + println("Database config again: ${config.databaseConfig}") + println("User config: ${config.userConfig}") +} +``` + +### **Exercise 3: Resource Management with Lazy** +Create a resource manager that initializes resources on demand. + +**Solution:** +```kotlin +class ResourceManager { + val database by lazy { + println("Initializing database connection...") + Database("localhost", 5432) + } + + val cache by lazy { + println("Initializing cache...") + Cache(1000) + } + + val httpClient by lazy { + println("Initializing HTTP client...") + HttpClient("https://api.example.com") + } + + fun processRequest(request: String) { + // Resources are initialized only when needed + if (request.startsWith("db:")) { + database.query(request.substring(3)) + } else if (request.startsWith("cache:")) { + cache.get(request.substring(6)) + } else if (request.startsWith("http:")) { + httpClient.get(request.substring(5)) + } + } +} + +// Mock classes +class Database(val host: String, val port: Int) { + fun query(query: String) = println("Database query: $query") +} +class Cache(val size: Int) { + fun get(key: String) = println("Cache get: $key") +} +class HttpClient(val baseUrl: String) { + fun get(path: String) = println("HTTP GET: $baseUrl$path") +} + +fun main() { + val manager = ResourceManager() + + println("Resource manager created") + + // Process different types of requests + manager.processRequest("db:SELECT * FROM users") + manager.processRequest("cache:user:123") + manager.processRequest("http:/api/users") + + // Process another database request - uses existing connection + manager.processRequest("db:SELECT COUNT(*) FROM users") +} +``` + +### **Exercise 4: Combined Lateinit and Lazy** +Create a service that uses both lateinit and lazy properties. + +**Solution:** +```kotlin +class UserService { + // Dependencies injected later + lateinit var userRepository: UserRepository + lateinit var emailService: EmailService + + // Computed properties initialized on demand + val userCache by lazy { + println("Initializing user cache...") + mutableMapOf() + } + + val defaultUser by lazy { + println("Creating default user...") + User(0, "Default", "default@example.com") + } + + fun initialize(repository: UserRepository, emailService: EmailService) { + this.userRepository = repository + this.emailService = emailService + } + + fun createUser(name: String, email: String): User { + if (!::userRepository.isInitialized) { + throw IllegalStateException("UserRepository not initialized") + } + + val user = userRepository.createUser(name, email) + userCache[user.id] = user + emailService.sendWelcomeEmail(user.email) + + return user + } + + fun getUser(id: Int): User? { + return userCache[id] ?: userRepository.getUser(id) + } + + fun getDefaultUser(): User { + return defaultUser + } +} + +// Mock classes +class User(val id: Int, val name: String, val email: String) +class UserRepository { + fun createUser(name: String, email: String) = User(1, name, email) + fun getUser(id: Int) = User(id, "Unknown", "unknown@example.com") +} +class EmailService { + fun sendWelcomeEmail(email: String) = println("Welcome email sent to $email") +} + +fun main() { + val service = UserService() + + try { + service.createUser("Alice", "alice@example.com") + } catch (e: Exception) { + println("Error: ${e.message}") + } + + // Initialize dependencies + service.initialize(UserRepository(), EmailService()) + + // Now it should work + val user = service.createUser("Alice", "alice@example.com") + println("Created user: ${user.name}") + + // Access lazy properties + val defaultUser = service.getDefaultUser() + println("Default user: ${defaultUser.name}") +} +``` + +## ๐Ÿšจ Common Mistakes to Avoid + +1. **Using lateinit with nullable types**: `lateinit var property: String?` โŒ + - **Correct**: `lateinit var property: String` โœ… + +2. **Forgetting to initialize lateinit properties**: Accessing before initialization โŒ + - **Correct**: Always check `::property.isInitialized` โœ… + +3. **Using lazy with var**: `var property by lazy { ... }` โŒ + - **Correct**: `val property by lazy { ... }` โœ… + +4. **Ignoring thread safety**: Using lazy in multi-threaded environments without considering thread safety โœ… + +## ๐ŸŽฏ What's Next? + +Now that you understand lateinit and lazy, continue with: + +1. **Scope Functions** โ†’ [Scope Functions](../functional-programming/02-scope-functions.md) +2. **Collections** โ†’ [Collections Overview](../collections/README.md) +3. **Coroutines** โ†’ [Coroutines](../coroutines/01-introduction.md) +4. **Advanced OOP** โ†’ [Object-Oriented Programming](../oop/README.md) + +## ๐Ÿ“š Additional Resources + +- [Official Kotlin Lateinit Documentation](https://kotlinlang.org/docs/properties.html#late-initialized-properties-and-variables) +- [Official Kotlin Lazy Documentation](https://kotlinlang.org/docs/delegated-properties.html#lazy-properties) +- [Kotlin Properties Guide](https://kotlinlang.org/docs/properties.html) + +## ๐Ÿ† Summary + +- โœ… You understand when and how to use `lateinit` for delayed initialization +- โœ… You can use `lazy` for on-demand initialization with caching +- โœ… You know how to choose between `lateinit` and `lazy` for different scenarios +- โœ… You can handle initialization checks and error cases +- โœ… You're ready to implement efficient initialization patterns! + +**Lateinit and lazy provide powerful ways to manage property initialization. Use them to create more efficient and flexible code! ๐ŸŽ‰** diff --git a/docs/oop/01-classes-constructors.md b/docs/oop/01-classes-constructors.md index d1b8638..ac558d2 100644 --- a/docs/oop/01-classes-constructors.md +++ b/docs/oop/01-classes-constructors.md @@ -793,3 +793,4 @@ Excellent! You've mastered classes and constructors in Kotlin. Now you're ready - โœ… You're ready to build object-oriented applications! **Keep practicing and happy coding! ๐ŸŽ‰** + diff --git a/docs/oop/02-inheritance.md b/docs/oop/02-inheritance.md index 0b2710e..0eabdc2 100644 --- a/docs/oop/02-inheritance.md +++ b/docs/oop/02-inheritance.md @@ -864,3 +864,4 @@ Excellent! You've mastered inheritance in Kotlin. Now you're ready to: - โœ… You're ready to build complex object-oriented systems! **Keep practicing and happy coding! ๐ŸŽ‰** + diff --git a/docs/oop/03-method-overriding.md b/docs/oop/03-method-overriding.md index 4cb97a9..3702675 100644 --- a/docs/oop/03-method-overriding.md +++ b/docs/oop/03-method-overriding.md @@ -869,3 +869,4 @@ Excellent! You've mastered method overriding in Kotlin. Now you're ready to: - โœ… You're ready to create polymorphic class hierarchies! **Keep practicing and happy coding! ๐ŸŽ‰** + diff --git a/docs/oop/04-abstract-classes.md b/docs/oop/04-abstract-classes.md index fb4b5b5..9aac335 100644 --- a/docs/oop/04-abstract-classes.md +++ b/docs/oop/04-abstract-classes.md @@ -950,3 +950,4 @@ Excellent! You've mastered abstract classes in Kotlin. Now you're ready to: - โœ… You're ready to design robust object-oriented systems! **Keep practicing and happy coding! ๐ŸŽ‰** + From 5a23841cb43b5f017b8fab434337fa4ce1c542d7 Mon Sep 17 00:00:00 2001 From: gpl-gowthamchand Date: 2025ๅนด8ๆœˆ25ๆ—ฅ 09:35:25 +0530 Subject: [PATCH 08/23] feat: Add comprehensive advanced Kotlin documentation - Advanced scope functions (let, also, run) - Coroutine exception handling and context/dispatchers - Enums, sealed classes, and companion objects - Predicates for functional programming - Kotlin-Java interoperability - Updated main README with all new topics --- docs/README.md | 7 + docs/advanced/01-kotlin-interoperability.md | 618 +++++++++++++ docs/coroutines/03-exception-handling.md | 551 ++++++++++++ docs/coroutines/04-context-dispatchers.md | 611 +++++++++++++ .../functional-programming/03-let-also-run.md | 489 +++++++++++ docs/functional-programming/04-predicates.md | 595 +++++++++++++ docs/oop/07-enums-sealed-classes.md | 817 ++++++++++++++++++ 7 files changed, 3688 insertions(+) create mode 100644 docs/advanced/01-kotlin-interoperability.md create mode 100644 docs/coroutines/03-exception-handling.md create mode 100644 docs/coroutines/04-context-dispatchers.md create mode 100644 docs/functional-programming/03-let-also-run.md create mode 100644 docs/functional-programming/04-predicates.md create mode 100644 docs/oop/07-enums-sealed-classes.md diff --git a/docs/README.md b/docs/README.md index 262b251..55a5dee 100644 --- a/docs/README.md +++ b/docs/README.md @@ -45,12 +45,18 @@ Welcome to the comprehensive Kotlin learning guide! This documentation is organi - [Abstract Classes](oop/04-abstract-classes.md) โ†’ [Code](src/30_abstract_class.kt) - [Interfaces](oop/05-interfaces.md) โ†’ [Code](src/31_interface.kt) - [Data Classes](oop/06-data-classes.md) โ†’ [Code](src/32_data_class.kt) +- [Enums, Sealed Classes, and Companion Objects](oop/07-enums-sealed-classes.md) โ†’ [Code](src/34_1_enum_class, 34_2_sealed_class.kt, 34_companion_object.kt) ### **๐Ÿ”ง Advanced Level** #### **Functional Programming** - [Lambdas and Higher-Order Functions](functional-programming/01-lambdas.md) โ†’ [Code](src/35_lambdas_higher_order_functions.kt) - [Scope Functions](functional-programming/02-scope-functions.md) โ†’ [Code](src/39_with_apply_functions.kt) +- [Advanced Scope Functions](functional-programming/03-let-also-run.md) โ†’ [Code](src/51_also_scope_function.kt, 52_let_scope_function.kt, 53_run_scope_function.kt) +- [Predicates](functional-programming/04-predicates.md) โ†’ [Code](src/45_predicate.kt) + +#### **Advanced Topics** +- [Kotlin-Java Interoperability](advanced/01-kotlin-interoperability.md) โ†’ [Code](src/MyJavaFile.java, myKotlinInteroperability.kt) #### **Collections** - [Arrays](collections/01-arrays.md) โ†’ [Code](src/40_arrays.kt) @@ -67,6 +73,7 @@ Welcome to the comprehensive Kotlin learning guide! This documentation is organi - [Introduction to Coroutines](coroutines/01-introduction.md) โ†’ [Code](src/61_first_coroutine.kt) - [Launch and Async](coroutines/02-launch-async.md) โ†’ [Code](src/64_launch_coroutine_builder.kt) - [Exception Handling](coroutines/03-exception-handling.md) โ†’ [Code](src/70_exception_handling.kt) +- [Context and Dispatchers](coroutines/04-context-dispatchers.md) โ†’ [Code](src/78_CoroutineContext_and_Dispatchers.kt) ## ๐ŸŽ“ Recommended Learning Order diff --git a/docs/advanced/01-kotlin-interoperability.md b/docs/advanced/01-kotlin-interoperability.md new file mode 100644 index 0000000..e030ac2 --- /dev/null +++ b/docs/advanced/01-kotlin-interoperability.md @@ -0,0 +1,618 @@ +# ๐Ÿ”„ Kotlin-Java Interoperability + +This lesson covers Kotlin-Java interoperability, which allows you to use Java code from Kotlin and vice versa. This is essential for working with existing Java libraries and gradually migrating Java projects to Kotlin. + +## ๐Ÿ“‹ Learning Objectives + +By the end of this lesson, you will be able to: +- โœ… Call Java code from Kotlin seamlessly +- โœ… Use Kotlin code from Java +- โœ… Handle null safety differences between languages +- โœ… Work with Java collections and Kotlin collections +- โœ… Apply best practices for interoperability + +## ๐Ÿ” What You'll Learn + +- **Java from Kotlin** - Calling Java methods and classes +- **Kotlin from Java** - Using Kotlin classes in Java +- **Null Safety** - Handling nullable types across languages +- **Collections** - Working with Java and Kotlin collections +- **Best Practices** - Guidelines for smooth interoperability + +## ๐Ÿ“ Prerequisites + +- Understanding of [Kotlin Basics](../basics/README.md) +- Knowledge of [Null Safety](../null-safety/01-null-safety.md) +- Familiarity with [Collections](../collections/README.md) +- Basic Java knowledge + +## ๐Ÿ’ป The Code + +Let's examine the interoperability examples: + +**๐Ÿ“ File:** [MyJavaFile.java](../src/MyJavaFile.java) +**๐Ÿ“ File:** [myKotlinInteroperability.kt](../src/myKotlinInteroperability.kt) + +## ๐Ÿ” Code Breakdown + +### **1. Calling Java from Kotlin** + +#### **Basic Java Class Usage** +```kotlin +// Java class can be used directly in Kotlin +fun main() { + val javaClass = MyJavaClass() + + // Call Java methods + javaClass.setName("John Doe") + javaClass.setAge(30) + + // Access Java properties (getters/setters) + println("Name: ${javaClass.name}") + println("Age: ${javaClass.age}") + + // Call Java methods with different signatures + javaClass.printInfo() + javaClass.printInfo("Custom prefix") +} +``` + +#### **Java Collections in Kotlin** +```kotlin +fun main() { + // Java ArrayList + val javaList = java.util.ArrayList() + javaList.add("Item 1") + javaList.add("Item 2") + + // Use Kotlin collection functions + val filtered = javaList.filter { it.contains("1") } + val mapped = javaList.map { "Processed: $it" } + + println("Original: $javaList") + println("Filtered: $filtered") + println("Mapped: $mapped") + + // Convert to Kotlin collections + val kotlinList = javaList.toList() + val kotlinMutableList = javaList.toMutableList() +} +``` + +#### **Java Static Methods** +```kotlin +fun main() { + // Call Java static methods + val random = MyJavaClass.generateRandomNumber() + val formatted = MyJavaClass.formatString("Hello", "World") + + println("Random number: $random") + println("Formatted string: $formatted") + + // Access Java constants + println("Max value: ${MyJavaClass.MAX_VALUE}") + println("Default name: ${MyJavaClass.DEFAULT_NAME}") +} +``` + +### **2. Using Kotlin from Java** + +#### **Kotlin Class in Java** +```java +public class JavaClient { + public static void main(String[] args) { + // Create Kotlin object + MyKotlinClass kotlinObject = new MyKotlinClass("Kotlin", 42); + + // Call Kotlin methods + String info = kotlinObject.getInfo(); + System.out.println("Info: " + info); + + // Access Kotlin properties + String name = kotlinObject.getName(); + int value = kotlinObject.getValue(); + System.out.println("Name: " + name + ", Value: " + value); + + // Call Kotlin functions with default parameters + String result = kotlinObject.processData("input", true); + System.out.println("Result: " + result); + } +} +``` + +#### **Kotlin Functions with Default Parameters** +```kotlin +class MyKotlinClass(val name: String, val value: Int) { + fun getInfo(): String = "Name: $name, Value: $value" + + fun processData(input: String, verbose: Boolean = false): String { + return if (verbose) { + "Processing '$input' with verbose output" + } else { + "Processed: $input" + } + } + + fun getValue(): Int = value +} +``` + +### **3. Null Safety Interoperability** + +#### **Java Nullable Types in Kotlin** +```kotlin +fun main() { + val javaClass = MyJavaClass() + + // Java methods can return null + val nullableString = javaClass.getNullableString() + + // Handle null safely + if (nullableString != null) { + println("String length: ${nullableString.length}") + } else { + println("String is null") + } + + // Use safe call operator + println("String length: ${nullableString?.length}") + + // Use Elvis operator for default value + val safeString = nullableString ?: "Default string" + println("Safe string: $safeString") +} +``` + +#### **Kotlin Non-Null Types in Java** +```kotlin +class KotlinService { + fun getNonNullString(): String = "Always returns a string" + + fun getNullableString(): String? = null + + fun processString(input: String) { + println("Processing: $input") + } +} +``` + +```java +public class JavaClient { + public static void main(String[] args) { + KotlinService service = new KotlinService(); + + // Kotlin non-null String becomes Java String + String nonNull = service.getNonNullString(); + service.processString(nonNull); // Safe to call + + // Kotlin nullable String becomes Java String (with @Nullable annotation) + String nullable = service.getNullableString(); + if (nullable != null) { + service.processString(nullable); + } + } +} +``` + +### **4. Collection Interoperability** + +#### **Java Collections in Kotlin** +```kotlin +fun main() { + // Java HashMap + val javaMap = java.util.HashMap() + javaMap.put("one", 1) + javaMap.put("two", 2) + javaMap.put("three", 3) + + // Use Kotlin collection functions + val filteredMap = javaMap.filter { (key, value) -> value> 1 } + val keys = javaMap.keys.toList() + val values = javaMap.values.toList() + + println("Original map: $javaMap") + println("Filtered map: $filteredMap") + println("Keys: $keys") + println("Values: $values") + + // Convert to Kotlin collections + val kotlinMap = javaMap.toMap() + val kotlinMutableMap = javaMap.toMutableMap() +} +``` + +#### **Kotlin Collections in Java** +```kotlin +class KotlinCollectionProvider { + fun getList(): List = listOf("a", "b", "c") + + fun getMutableList(): MutableList = mutableListOf("x", "y", "z") + + fun getMap(): Map = mapOf("one" to 1, "two" to 2) + + fun getSet(): Set = setOf("alpha", "beta", "gamma") +} +``` + +```java +import java.util.List; +import java.util.Map; +import java.util.Set; + +public class JavaCollectionClient { + public static void main(String[] args) { + KotlinCollectionProvider provider = new KotlinCollectionProvider(); + + // Kotlin List becomes Java List + List list = provider.getList(); + System.out.println("List: " + list); + + // Kotlin MutableList becomes Java List + List mutableList = provider.getMutableList(); + mutableList.add("new"); + System.out.println("Mutable list: " + mutableList); + + // Kotlin Map becomes Java Map + Map map = provider.getMap(); + System.out.println("Map: " + map); + + // Kotlin Set becomes Java Set + Set set = provider.getSet(); + System.out.println("Set: " + set); + } +} +``` + +### **5. Advanced Interoperability Patterns** + +#### **Java Builder Pattern in Kotlin** +```kotlin +fun main() { + // Use Java builder pattern + val person = PersonBuilder() + .setName("Jane Doe") + .setAge(25) + .setEmail("jane@example.com") + .build() + + println("Person: $person") + + // Use Java fluent API + val config = ConfigurationBuilder() + .setHost("localhost") + .setPort(8080) + .setTimeout(5000) + .build() + + println("Config: $config") +} +``` + +#### **Kotlin Extension Functions on Java Classes** +```kotlin +// Extend Java classes with Kotlin functions +fun java.util.ArrayList.addIfNotExists(item: String): Boolean { + return if (this.contains(item)) { + false + } else { + this.add(item) + true + } +} + +fun java.util.HashMap.getString(key: String): String? { + return this[key] as? String +} + +fun main() { + val javaList = java.util.ArrayList() + javaList.addIfNotExists("first") + javaList.addIfNotExists("second") + javaList.addIfNotExists("first") // Won't add duplicate + + println("List: $javaList") + + val javaMap = java.util.HashMap() + javaMap["name"] = "John" + javaMap["age"] = 30 + + val name = javaMap.getString("name") + println("Name: $name") +} +``` + +#### **Java SAM (Single Abstract Method) Interfaces** +```kotlin +fun main() { + // Java Runnable interface + val runnable = java.lang.Runnable { println("Running from Kotlin") } + + // Java Thread with Kotlin lambda + val thread = java.lang.Thread { println("Thread running") } + thread.start() + + // Java Comparator + val names = listOf("Charlie", "Alice", "Bob") + val sortedNames = names.sortedWith(java.util.Comparator { a, b -> a.compareTo(b) }) + + println("Sorted names: $sortedNames") +} +``` + +## ๐ŸŽฏ Key Concepts Explained + +### **1. Type Mapping** + +| Kotlin Type | Java Type | Notes | +|-------------|-----------|-------| +| `String` | `String` | Non-null String | +| `String?` | `String` | Nullable String (with annotations) | +| `Int` | `int` | Primitive int | +| `Int?` | `Integer` | Nullable Integer | +| `List` | `List` | Read-only list | +| `MutableList` | `List` | Mutable list | + +### **2. Null Safety Differences** +- **Kotlin**: Compile-time null safety +- **Java**: Runtime null safety +- **Interop**: Kotlin nullable types become Java types with `@Nullable` annotations +- **Best Practice**: Always check for null when calling Java methods + +### **3. Collection Differences** +- **Kotlin**: Read-only vs mutable collections +- **Java**: All collections are mutable +- **Interop**: Kotlin read-only collections become Java collections (mutable) + +## ๐Ÿงช Running the Examples + +### **Step 1: Open the Files** +1. Navigate to `src/MyJavaFile.java` +2. Navigate to `src/myKotlinInteroperability.kt` +3. Open them in IntelliJ IDEA + +### **Step 2: Run the Programs** +1. Right-click on the Kotlin file +2. Select "Run 'myKotlinInteroperabilityKt'" +3. Observe the output + +## ๐ŸŽฎ Hands-On Exercises + +### **Exercise 1: Java Library Integration** +Create a Kotlin program that uses Java libraries like Apache Commons. + +**Solution:** +```kotlin +import org.apache.commons.lang3.StringUtils +import java.util.Random + +fun main() { + // Use Java Random class + val random = Random() + val randomNumber = random.nextInt(100) + println("Random number: $randomNumber") + + // Use Apache Commons StringUtils + val text = " hello world " + val trimmed = StringUtils.trim(text) + val capitalized = StringUtils.capitalize(trimmed) + val reversed = StringUtils.reverse(capitalized) + + println("Original: '$text'") + println("Trimmed: '$trimmed'") + println("Capitalized: '$capitalized'") + println("Reversed: '$reversed'") + + // Use Java Collections + val javaList = java.util.ArrayList() + javaList.add("item1") + javaList.add("item2") + + // Convert to Kotlin and use Kotlin functions + val kotlinList = javaList.toList() + val processed = kotlinList.map { "Processed: $it" } + println("Processed: $processed") +} +``` + +### **Exercise 2: Kotlin Service in Java** +Create a Kotlin service that can be easily used from Java. + +**Solution:** +```kotlin +// Kotlin service designed for Java interop +class UserService { + private val users = mutableMapOf() + + fun createUser(name: String, email: String): User { + val user = User(name, email) + users[email] = user + return user + } + + fun getUser(email: String): User? = users[email] + + fun getAllUsers(): List = users.values.toList() + + fun updateUser(email: String, newName: String): Boolean { + val user = users[email] ?: return false + users[email] = user.copy(name = newName) + return true + } + + fun deleteUser(email: String): Boolean { + return users.remove(email) != null + } +} + +data class User(val name: String, val email: String) +``` + +```java +// Java client using Kotlin service +public class JavaUserClient { + public static void main(String[] args) { + UserService service = new UserService(); + + // Create users + User user1 = service.createUser("John Doe", "john@example.com"); + User user2 = service.createUser("Jane Smith", "jane@example.com"); + + System.out.println("Created users: " + user1 + ", " + user2); + + // Get all users + List allUsers = service.getAllUsers(); + System.out.println("All users: " + allUsers); + + // Update user + boolean updated = service.updateUser("john@example.com", "John Updated"); + System.out.println("Update successful: " + updated); + + // Get updated user + User updatedUser = service.getUser("john@example.com"); + System.out.println("Updated user: " + updatedUser); + } +} +``` + +### **Exercise 3: Collection Interoperability** +Create a program that demonstrates collection interoperability. + +**Solution:** +```kotlin +fun main() { + // Java collections + val javaArrayList = java.util.ArrayList() + val javaHashMap = java.util.HashMap() + + // Populate Java collections + for (i in 1..5) { + javaArrayList.add(i * i) + javaHashMap["key$i"] = i + } + + println("Java ArrayList: $javaArrayList") + println("Java HashMap: $javaHashMap") + + // Convert to Kotlin collections + val kotlinList = javaArrayList.toList() + val kotlinMap = javaHashMap.toMap() + + // Use Kotlin collection functions + val filteredList = kotlinList.filter { it> 10 } + val transformedMap = kotlinMap.mapValues { (key, value) -> value * 2 } + + println("Filtered list (>10): $filteredList") + println("Transformed map (values * 2): $transformedMap") + + // Convert back to Java collections + val backToJavaList = kotlinList.toMutableList() + val backToJavaMap = kotlinMap.toMutableMap() + + // Use Java collection methods + backToJavaList.add(36) + backToJavaMap.put("newKey", 100) + + println("Modified Java list: $backToJavaList") + println("Modified Java map: $backToJavaMap") +} +``` + +### **Exercise 4: Null Safety Bridge** +Create a system that safely bridges null safety between Kotlin and Java. + +**Solution:** +```kotlin +// Kotlin class with null safety +class SafeDataProcessor { + fun processData(data: String): String { + return "Processed: $data" + } + + fun processNullableData(data: String?): String? { + return data?.let { "Processed: $it" } + } + + fun getDefaultValue(): String = "Default value" + + fun getNullableValue(): String? = null +} + +// Java-friendly wrapper +class JavaFriendlyWrapper { + private val processor = SafeDataProcessor() + + fun processData(data: String): String { + return processor.processData(data) + } + + fun processNullableData(data: String?): String { + return processor.processNullableData(data) ?: "No data provided" + } + + fun getDefaultValue(): String = processor.getDefaultValue() + + fun getNullableValue(): String? = processor.getNullableValue() +} +``` + +```java +public class JavaNullSafetyClient { + public static void main(String[] args) { + JavaFriendlyWrapper wrapper = new JavaFriendlyWrapper(); + + // Safe non-null operations + String result1 = wrapper.processData("Hello"); + System.out.println("Result 1: " + result1); + + // Handle nullable data safely + String result2 = wrapper.processNullableData("World"); + System.out.println("Result 2: " + result2); + + String result3 = wrapper.processNullableData(null); + System.out.println("Result 3: " + result3); + + // Get values + String defaultValue = wrapper.getDefaultValue(); + System.out.println("Default: " + defaultValue); + + String nullableValue = wrapper.getNullableValue(); + if (nullableValue != null) { + System.out.println("Nullable: " + nullableValue); + } else { + System.out.println("Nullable value is null"); + } + } +} +``` + +## ๐Ÿšจ Common Mistakes to Avoid + +1. **Ignoring null safety**: Always check for null when calling Java methods โœ… +2. **Collection mutability**: Remember that Java collections are always mutable โœ… +3. **Type mismatches**: Be aware of primitive vs wrapper type differences โœ… +4. **Forgetting annotations**: Use proper annotations for Java interop โœ… + +## ๐ŸŽฏ What's Next? + +Now that you understand Kotlin-Java interoperability, continue with: + +1. **Advanced Coroutines** โ†’ [Coroutines Introduction](../coroutines/01-introduction.md) +2. **Functional Programming** โ†’ [Lambdas and Higher-Order Functions](../functional-programming/01-lambdas.md) +3. **Collections** โ†’ [Collections Overview](../collections/README.md) +4. **Advanced OOP** โ†’ [Enums and Sealed Classes](../oop/07-enums-sealed-classes.md) + +## ๐Ÿ“š Additional Resources + +- [Official Kotlin-Java Interop Guide](https://kotlinlang.org/docs/java-interop.html) +- [Calling Java from Kotlin](https://kotlinlang.org/docs/java-interop.html#calling-java-code-from-kotlin) +- [Calling Kotlin from Java](https://kotlinlang.org/docs/java-interop.html#calling-kotlin-code-from-java) + +## ๐Ÿ† Summary + +- โœ… You can call Java code from Kotlin seamlessly +- โœ… You know how to use Kotlin code from Java +- โœ… You understand how to handle null safety differences +- โœ… You can work with Java collections and Kotlin collections +- โœ… You're ready to apply best practices for interoperability! + +**Kotlin-Java interoperability is essential for real-world development. Master it for seamless integration with existing Java codebases! ๐ŸŽ‰** diff --git a/docs/coroutines/03-exception-handling.md b/docs/coroutines/03-exception-handling.md new file mode 100644 index 0000000..f88a32a --- /dev/null +++ b/docs/coroutines/03-exception-handling.md @@ -0,0 +1,551 @@ +# ๐Ÿšจ Coroutine Exception Handling + +This lesson covers how to handle exceptions in Kotlin coroutines. Exception handling in coroutines is crucial for building robust asynchronous applications that can gracefully handle errors and failures. + +## ๐Ÿ“‹ Learning Objectives + +By the end of this lesson, you will be able to: +- โœ… Handle exceptions in coroutines using try-catch blocks +- โœ… Use CoroutineExceptionHandler for global exception handling +- โœ… Understand exception propagation in coroutine hierarchies +- โœ… Implement proper error handling strategies +- โœ… Debug coroutine exceptions effectively + +## ๐Ÿ” What You'll Learn + +- **Exception handling basics** - Try-catch in coroutines +- **CoroutineExceptionHandler** - Global exception handling +- **Exception propagation** - How exceptions flow through coroutine scopes +- **Error recovery strategies** - Retry mechanisms and fallbacks +- **Debugging techniques** - Tools and best practices + +## ๐Ÿ“ Prerequisites + +- Understanding of [Coroutines Introduction](../coroutines/01-introduction.md) +- Knowledge of [Launch and Async](../coroutines/02-launch-async.md) +- Familiarity with [Basic Exception Handling](../basics/README.md) + +## ๐Ÿ’ป The Code + +Let's examine the exception handling examples: + +**๐Ÿ“ File:** [70_exception_handling.kt](../src/70_exception_handling.kt) + +## ๐Ÿ” Code Breakdown + +### **1. Basic Exception Handling in Coroutines** + +#### **Try-Catch with Coroutines** +```kotlin +import kotlinx.coroutines.* + +fun main() = runBlocking { + try { + val result = async { + delay(1000) + throw RuntimeException("Something went wrong!") + }.await() + + println("Result: $result") + } catch (e: Exception) { + println("Caught exception: ${e.message}") + } +} +``` + +#### **Exception Handling with Launch** +```kotlin +fun main() = runBlocking { + val job = launch { + try { + delay(1000) + throw RuntimeException("Error in coroutine") + } catch (e: Exception) { + println("Caught in coroutine: ${e.message}") + } + } + + job.join() + println("Coroutine completed") +} +``` + +### **2. CoroutineExceptionHandler** + +#### **Global Exception Handler** +```kotlin +val exceptionHandler = CoroutineExceptionHandler { _, exception -> + println("Coroutine exception: ${exception.message}") + // Log the exception, send to monitoring service, etc. +} + +fun main() = runBlocking { + val job = GlobalScope.launch(exceptionHandler) { + delay(1000) + throw RuntimeException("Unhandled exception") + } + + job.join() + println("Main function completed") +} +``` + +#### **Exception Handler with Different Dispatchers** +```kotlin +val ioExceptionHandler = CoroutineExceptionHandler { _, exception -> + println("IO Coroutine exception: ${exception.message}") + // Handle IO-specific exceptions +} + +val defaultExceptionHandler = CoroutineExceptionHandler { _, exception -> + println("Default Coroutine exception: ${exception.message}") + // Handle general exceptions +} + +fun main() = runBlocking { + // IO operations with specific exception handling + val ioJob = launch(Dispatchers.IO + ioExceptionHandler) { + delay(1000) + throw IOException("IO operation failed") + } + + // General operations with default exception handling + val defaultJob = launch(Dispatchers.Default + defaultExceptionHandler) { + delay(500) + throw RuntimeException("General error") + } + + ioJob.join() + defaultJob.join() +} +``` + +### **3. Exception Propagation** + +#### **Exception Propagation in Coroutine Hierarchy** +```kotlin +fun main() = runBlocking { + val parentJob = launch { + println("Parent coroutine started") + + val childJob = launch { + println("Child coroutine started") + delay(1000) + throw RuntimeException("Child exception") + } + + try { + childJob.join() + } catch (e: Exception) { + println("Parent caught: ${e.message}") + } + + println("Parent coroutine completed") + } + + parentJob.join() + println("All coroutines completed") +} +``` + +#### **Supervisor Scope Exception Handling** +```kotlin +fun main() = runBlocking { + supervisorScope { + val job1 = launch { + delay(1000) + throw RuntimeException("Job 1 failed") + } + + val job2 = launch { + delay(2000) + println("Job 2 completed successfully") + } + + try { + job1.join() + } catch (e: Exception) { + println("Caught job1 exception: ${e.message}") + } + + job2.join() + println("Supervisor scope completed") + } +} +``` + +### **4. Advanced Exception Handling Patterns** + +#### **Retry Mechanism with Exponential Backoff** +```kotlin +suspend fun retryWithBackoff( + times: Int, + initialDelay: Long = 100, + factor: Double = 2.0, + block: suspend () -> T +): T { + var currentDelay = initialDelay + repeat(times - 1) { attempt -> + try { + return block() + } catch (e: Exception) { + println("Attempt ${attempt + 1} failed: ${e.message}") + if (attempt < times - 2) { + delay(currentDelay) + currentDelay = (currentDelay * factor).toLong() + } + } + } + return block() // Last attempt +} + +fun main() = runBlocking { + val result = retryWithBackoff(3) { + delay(100) + if (Math.random() < 0.7) { + throw RuntimeException("Random failure") + } + "Success!" + } + + println("Final result: $result") +} +``` + +#### **Exception Recovery with Fallback** +```kotlin +suspend fun fetchDataWithFallback(): String { + return try { + // Try primary data source + fetchFromPrimarySource() + } catch (e: Exception) { + println("Primary source failed: ${e.message}") + + try { + // Try secondary data source + fetchFromSecondarySource() + } catch (e2: Exception) { + println("Secondary source failed: ${e2.message}") + // Return default data + "Default data" + } + } +} + +suspend fun fetchFromPrimarySource(): String { + delay(1000) + throw RuntimeException("Primary source unavailable") +} + +suspend fun fetchFromSecondarySource(): String { + delay(500) + throw RuntimeException("Secondary source unavailable") +} + +fun main() = runBlocking { + val data = fetchDataWithFallback() + println("Final data: $data") +} +``` + +### **5. Exception Handling Best Practices** + +#### **Structured Exception Handling** +```kotlin +class DataService { + suspend fun processData(data: String): String { + return withContext(Dispatchers.IO) { + try { + // Simulate data processing + delay(1000) + if (data.isEmpty()) { + throw IllegalArgumentException("Data cannot be empty") + } + "Processed: $data" + } catch (e: Exception) { + // Log the exception + println("Data processing failed: ${e.message}") + // Re-throw or return default value + throw e + } + } + } +} + +fun main() = runBlocking { + val service = DataService() + + try { + val result = service.processData("") + println("Result: $result") + } catch (e: Exception) { + println("Service error: ${e.message}") + } +} +``` + +#### **Exception Handling in Parallel Operations** +```kotlin +suspend fun processItemsParallel(items: List): List { + return items.map { item -> + async { + try { + processItem(item) + } catch (e: Exception) { + println("Failed to process $item: ${e.message}") + "Error processing $item" + } + } + }.awaitAll() +} + +suspend fun processItem(item: String): String { + delay(100) + if (item == "error") { + throw RuntimeException("Cannot process error item") + } + return "Processed: $item" +} + +fun main() = runBlocking { + val items = listOf("item1", "error", "item3", "item4") + val results = processItemsParallel(items) + + println("Results: $results") +} +``` + +## ๐ŸŽฏ Key Concepts Explained + +### **1. Exception Propagation Rules** +- Exceptions in coroutines propagate to their parent +- Unhandled exceptions can crash the entire coroutine scope +- Use try-catch or CoroutineExceptionHandler to prevent crashes + +### **2. CoroutineExceptionHandler vs Try-Catch** +```kotlin +// CoroutineExceptionHandler - handles uncaught exceptions +val handler = CoroutineExceptionHandler { _, exception -> + println("Uncaught: ${exception.message}") +} + +// Try-catch - handles exceptions within the coroutine +launch { + try { + // risky operation + } catch (e: Exception) { + // handle exception + } +} +``` + +### **3. Exception Handling in Different Scopes** +- **GlobalScope**: Use CoroutineExceptionHandler +- **Structured concurrency**: Exceptions propagate to parent +- **SupervisorScope**: Exceptions don't affect siblings + +## ๐Ÿงช Running the Examples + +### **Step 1: Open the File** +1. Navigate to `src/70_exception_handling.kt` +2. Open it in IntelliJ IDEA + +### **Step 2: Run the Program** +1. Right-click on the file +2. Select "Run '70_exception_handlingKt'" +3. Observe the output + +## ๐ŸŽฎ Hands-On Exercises + +### **Exercise 1: Basic Exception Handling** +Create a coroutine that simulates a network call and handles exceptions. + +**Solution:** +```kotlin +import kotlinx.coroutines.* + +suspend fun simulateNetworkCall(url: String): String { + delay(1000) + if (url.contains("error")) { + throw RuntimeException("Network error for $url") + } + return "Response from $url" +} + +fun main() = runBlocking { + val urls = listOf("https://api.example.com", "https://error.example.com", "https://success.example.com") + + urls.forEach { url -> + try { + val response = simulateNetworkCall(url) + println("Success: $response") + } catch (e: Exception) { + println("Failed to fetch $url: ${e.message}") + } + } +} +``` + +### **Exercise 2: CoroutineExceptionHandler** +Implement a global exception handler for your application. + +**Solution:** +```kotlin +import kotlinx.coroutines.* + +class ApplicationExceptionHandler : CoroutineExceptionHandler { + override val key = CoroutineExceptionHandler.Key + + override fun handleException(context: CoroutineContext, exception: Throwable) { + when (exception) { + is RuntimeException -> { + println("Runtime error: ${exception.message}") + // Log to monitoring service + } + is IOException -> { + println("IO error: ${exception.message}") + // Retry logic + } + else -> { + println("Unknown error: ${exception.message}") + // General error handling + } + } + } +} + +fun main() = runBlocking { + val handler = ApplicationExceptionHandler() + + val job = GlobalScope.launch(handler) { + delay(1000) + throw RuntimeException("Application error") + } + + job.join() + println("Application completed") +} +``` + +### **Exercise 3: Exception Recovery** +Create a resilient data processing system that can recover from failures. + +**Solution:** +```kotlin +import kotlinx.coroutines.* + +class ResilientDataProcessor { + suspend fun processWithRetry(data: String, maxRetries: Int = 3): String { + var lastException: Exception? = null + + repeat(maxRetries) { attempt -> + try { + return processData(data) + } catch (e: Exception) { + lastException = e + println("Attempt ${attempt + 1} failed: ${e.message}") + if (attempt < maxRetries - 1) { + delay(1000 * (attempt + 1)) // Exponential backoff + } + } + } + + throw lastException ?: RuntimeException("Unknown error") + } + + private suspend fun processData(data: String): String { + delay(500) + if (Math.random() < 0.6) { + throw RuntimeException("Processing failed") + } + return "Processed: $data" + } +} + +fun main() = runBlocking { + val processor = ResilientDataProcessor() + + try { + val result = processor.processWithRetry("important data") + println("Success: $result") + } catch (e: Exception) { + println("All retries failed: ${e.message}") + } +} +``` + +### **Exercise 4: Exception Handling in Parallel Operations** +Handle exceptions in multiple concurrent operations without failing the entire batch. + +**Solution:** +```kotlin +import kotlinx.coroutines.* + +data class ProcessingResult(val item: String, val success: Boolean, val result: String?, val error: String?) + +suspend fun processBatchResilient(items: List): List { + return items.map { item -> + async { + try { + val result = processItem(item) + ProcessingResult(item, true, result, null) + } catch (e: Exception) { + ProcessingResult(item, false, null, e.message) + } + } + }.awaitAll() +} + +suspend fun processItem(item: String): String { + delay(200) + if (item == "fail") { + throw RuntimeException("Cannot process $item") + } + return "Processed: $item" +} + +fun main() = runBlocking { + val items = listOf("item1", "fail", "item3", "item4", "fail2") + val results = processBatchResilient(items) + + results.forEach { result -> + if (result.success) { + println("โœ… ${result.item}: ${result.result}") + } else { + println("โŒ ${result.item}: ${result.error}") + } + } +} +``` + +## ๐Ÿšจ Common Mistakes to Avoid + +1. **Ignoring exceptions**: Always handle exceptions in coroutines โœ… +2. **Using GlobalScope without exception handler**: Can crash your application โœ… +3. **Not propagating exceptions**: Handle exceptions at the appropriate level โœ… +4. **Forgetting structured concurrency**: Use proper coroutine scopes โœ… + +## ๐ŸŽฏ What's Next? + +Now that you understand exception handling, continue with: + +1. **Coroutine Context and Dispatchers** โ†’ [Coroutine Context and Dispatchers](../coroutines/04-context-dispatchers.md) +2. **Advanced Coroutines** โ†’ [Sequential vs Concurrent](../coroutines/05-sequential-concurrent.md) +3. **Scope Functions** โ†’ [Advanced Scope Functions](../functional-programming/03-let-also-run.md) +4. **Collections** โ†’ [Collections Overview](../collections/README.md) + +## ๐Ÿ“š Additional Resources + +- [Official Coroutines Exception Handling](https://kotlinlang.org/docs/exception-handling.html) +- [Coroutines Guide](https://kotlinlang.org/docs/coroutines-guide.html) +- [Exception Handling Best Practices](https://kotlinlang.org/docs/exception-handling.html#exception-handling) + +## ๐Ÿ† Summary + +- โœ… You can handle exceptions in coroutines using try-catch blocks +- โœ… You understand how to use CoroutineExceptionHandler for global handling +- โœ… You know how exceptions propagate in coroutine hierarchies +- โœ… You can implement proper error handling strategies +- โœ… You're ready to build robust asynchronous applications! + +**Exception handling is crucial for production-ready coroutine applications. Master these patterns for bulletproof async code! ๐ŸŽ‰** diff --git a/docs/coroutines/04-context-dispatchers.md b/docs/coroutines/04-context-dispatchers.md new file mode 100644 index 0000000..92d2dcf --- /dev/null +++ b/docs/coroutines/04-context-dispatchers.md @@ -0,0 +1,611 @@ +# ๐ŸŽญ Coroutine Context and Dispatchers + +This lesson covers Kotlin coroutine context and dispatchers, which control how and where coroutines execute. Understanding these concepts is essential for optimizing performance and managing resources in concurrent applications. + +## ๐Ÿ“‹ Learning Objectives + +By the end of this lesson, you will be able to: +- โœ… Understand coroutine context and its components +- โœ… Use different dispatchers for various types of work +- โœ… Combine context elements effectively +- โœ… Manage coroutine lifecycle with context +- โœ… Optimize performance with appropriate dispatchers + +## ๐Ÿ” What You'll Learn + +- **Coroutine Context** - Understanding context structure and elements +- **Dispatchers** - Default, IO, Main, and Unconfined dispatchers +- **Context Combination** - Adding and removing context elements +- **Performance Optimization** - Choosing the right dispatcher for the job +- **Real-world Applications** - Practical use cases and patterns + +## ๐Ÿ“ Prerequisites + +- Understanding of [Coroutines Introduction](../coroutines/01-introduction.md) +- Knowledge of [Launch and Async](../coroutines/02-launch-async.md) +- Familiarity with [Exception Handling](../coroutines/03-exception-handling.md) + +## ๐Ÿ’ป The Code + +Let's examine the context and dispatcher examples: + +**๐Ÿ“ File:** [78_CoroutineContext_and_Dispatchers.kt](../src/78_CoroutineContext_and_Dispatchers.kt) + +## ๐Ÿ” Code Breakdown + +### **1. Understanding Coroutine Context** + +#### **Basic Context Structure** +```kotlin +import kotlinx.coroutines.* + +fun main() = runBlocking { + // Every coroutine has a context + val job = launch { + println("Coroutine context: ${coroutineContext}") + println("Job: ${coroutineContext[Job]}") + println("Dispatcher: ${coroutineContext[CoroutineDispatcher]}") + } + + job.join() +} +``` + +#### **Context Elements** +```kotlin +fun main() = runBlocking { + // Context contains various elements + val job = launch { + println("Context elements:") + coroutineContext.forEach { (key, value) -> + println(" $key -> $value") + } + } + + job.join() +} +``` + +### **2. Coroutine Dispatchers** + +#### **Default Dispatcher** +```kotlin +fun main() = runBlocking { + // Default dispatcher - CPU-intensive work + val job1 = launch(Dispatchers.Default) { + println("Running on Default dispatcher: ${Thread.currentThread().name}") + + // CPU-intensive computation + var result = 0 + repeat(1000000) { + result += it + } + println("Computation result: $result") + } + + job1.join() +} +``` + +#### **IO Dispatcher** +```kotlin +fun main() = runBlocking { + // IO dispatcher - network, file operations + val job1 = launch(Dispatchers.IO) { + println("Running on IO dispatcher: ${Thread.currentThread().name}") + + // Simulate IO operation + delay(1000) + println("IO operation completed") + } + + val job2 = launch(Dispatchers.IO) { + println("Another IO operation on: ${Thread.currentThread().name}") + delay(500) + println("Second IO operation completed") + } + + job1.join() + job2.join() +} +``` + +#### **Main Dispatcher** +```kotlin +fun main() = runBlocking { + // Main dispatcher - UI updates (Android, JS) + val job = launch(Dispatchers.Main) { + println("Running on Main dispatcher: ${Thread.currentThread().name}") + + // UI update operations would go here + println("UI updated successfully") + } + + job.join() +} +``` + +#### **Unconfined Dispatcher** +```kotlin +fun main() = runBlocking { + // Unconfined dispatcher - inherits context from caller + val job = launch(Dispatchers.Unconfined) { + println("Unconfined start: ${Thread.currentThread().name}") + + delay(100) + println("Unconfined after delay: ${Thread.currentThread().name}") + } + + job.join() +} +``` + +### **3. Context Combination and Modification** + +#### **Combining Context Elements** +```kotlin +fun main() = runBlocking { + // Combine dispatcher with exception handler + val exceptionHandler = CoroutineExceptionHandler { _, exception -> + println("Caught: ${exception.message}") + } + + val job = launch(Dispatchers.IO + exceptionHandler) { + println("Running on IO with exception handler: ${Thread.currentThread().name}") + throw RuntimeException("Test exception") + } + + job.join() +} +``` + +#### **Adding and Removing Context Elements** +```kotlin +fun main() = runBlocking { + val parentJob = Job() + val exceptionHandler = CoroutineExceptionHandler { _, exception -> + println("Exception: ${exception.message}") + } + + // Start with parent job and exception handler + val job1 = launch(parentJob + Dispatchers.IO + exceptionHandler) { + println("Job 1: ${Thread.currentThread().name}") + delay(1000) + } + + // Remove parent job for independent execution + val job2 = launch(Dispatchers.IO + exceptionHandler) { + println("Job 2: ${Thread.currentThread().name}") + delay(500) + } + + job1.join() + job2.join() + + // Cancel parent job + parentJob.cancel() +} +``` + +#### **Context Inheritance** +```kotlin +fun main() = runBlocking { + // Parent coroutine context + val parentContext = coroutineContext + Dispatchers.IO + + val job = launch(parentContext) { + println("Child inherits IO dispatcher: ${Thread.currentThread().name}") + + // Child can override context + withContext(Dispatchers.Default) { + println("Child switches to Default: ${Thread.currentThread().name}") + } + + println("Child back to inherited IO: ${Thread.currentThread().name}") + } + + job.join() +} +``` + +### **4. Advanced Context Patterns** + +#### **Custom Dispatcher** +```kotlin +import java.util.concurrent.Executors + +fun main() = runBlocking { + // Create custom dispatcher with fixed thread pool + val customDispatcher = Executors.newFixedThreadPool(2).asCoroutineDispatcher() + + val jobs = List(5) { index -> + launch(customDispatcher) { + println("Job $index on custom dispatcher: ${Thread.currentThread().name}") + delay(100) + } + } + + jobs.forEach { it.join() } + + // Clean up custom dispatcher + customDispatcher.close() +} +``` + +#### **Context Switching with withContext** +```kotlin +fun main() = runBlocking { + println("Main coroutine: ${Thread.currentThread().name}") + + val result = withContext(Dispatchers.IO) { + println("Switched to IO: ${Thread.currentThread().name}") + delay(1000) + "Data from IO operation" + } + + println("Back to main: ${Thread.currentThread().name}") + println("Result: $result") +} +``` + +#### **Structured Concurrency with Context** +```kotlin +fun main() = runBlocking { + // Parent context with IO dispatcher + val parentContext = Dispatchers.IO + SupervisorJob() + + coroutineScope(parentContext) { + val job1 = launch { + println("Job 1 on IO: ${Thread.currentThread().name}") + delay(1000) + println("Job 1 completed") + } + + val job2 = launch { + println("Job 2 on IO: ${Thread.currentThread().name}") + delay(500) + println("Job 2 completed") + } + + // Both jobs inherit parent context + job1.join() + job2.join() + } + + println("All jobs completed") +} +``` + +### **5. Performance and Best Practices** + +#### **Dispatcher Selection Guidelines** +```kotlin +fun main() = runBlocking { + // CPU-intensive work + val cpuJob = launch(Dispatchers.Default) { + val start = System.currentTimeMillis() + var result = 0 + repeat(1000000) { + result += it * it + } + val duration = System.currentTimeMillis() - start + println("CPU work completed in ${duration}ms: $result") + } + + // IO work + val ioJob = launch(Dispatchers.IO) { + val start = System.currentTimeMillis() + delay(1000) // Simulate IO + val duration = System.currentTimeMillis() - start + println("IO work completed in ${duration}ms") + } + + cpuJob.join() + ioJob.join() +} +``` + +#### **Context Optimization** +```kotlin +class DataProcessor { + // Use IO dispatcher for data operations + suspend fun processData(data: String): String { + return withContext(Dispatchers.IO) { + // Simulate data processing + delay(500) + "Processed: $data" + } + } + + // Use Default dispatcher for computations + suspend fun computeResult(numbers: List): Int { + return withContext(Dispatchers.Default) { + // CPU-intensive computation + numbers.sum() + } + } +} + +fun main() = runBlocking { + val processor = DataProcessor() + + val data = processor.processData("sample data") + val result = processor.computeResult(listOf(1, 2, 3, 4, 5)) + + println("Data: $data") + println("Result: $result") +} +``` + +## ๐ŸŽฏ Key Concepts Explained + +### **1. Dispatcher Types and Use Cases** + +| Dispatcher | Use Case | Thread Pool | Characteristics | +|------------|----------|-------------|-----------------| +| **Default** | CPU-intensive work | Shared thread pool | Limited threads, efficient for computations | +| **IO** | Network, file operations | Unlimited threads | Many threads, good for blocking operations | +| **Main** | UI updates | Single thread | Main thread, UI operations only | +| **Unconfined** | Testing, special cases | Inherits from caller | No thread confinement, unpredictable | + +### **2. Context Element Hierarchy** +```kotlin +// Context elements are combined with + operator +val context = Dispatchers.IO + SupervisorJob() + CoroutineName("MyCoroutine") + +// Order matters for some elements +val job = launch(context) { + // Coroutine inherits all context elements +} +``` + +### **3. Context Inheritance Rules** +- Child coroutines inherit parent context +- Children can override specific elements +- `withContext` temporarily changes context +- Context changes are isolated to the block + +## ๐Ÿงช Running the Examples + +### **Step 1: Open the File** +1. Navigate to `src/78_CoroutineContext_and_Dispatchers.kt` +2. Open it in IntelliJ IDEA + +### **Step 2: Run the Program** +1. Right-click on the file +2. Select "Run '78_CoroutineContext_and_DispatchersKt'" +3. Observe the output + +## ๐ŸŽฎ Hands-On Exercises + +### **Exercise 1: Dispatcher Selection** +Create a program that demonstrates when to use each dispatcher type. + +**Solution:** +```kotlin +import kotlinx.coroutines.* + +class TaskExecutor { + suspend fun executeCpuTask(): String { + return withContext(Dispatchers.Default) { + println("CPU task on: ${Thread.currentThread().name}") + var result = 0 + repeat(1000000) { + result += it * it + } + "CPU result: $result" + } + } + + suspend fun executeIoTask(): String { + return withContext(Dispatchers.IO) { + println("IO task on: ${Thread.currentThread().name}") + delay(1000) // Simulate IO + "IO completed" + } + } + + suspend fun executeUiTask(): String { + return withContext(Dispatchers.Main) { + println("UI task on: ${Thread.currentThread().name}") + "UI updated" + } + } +} + +fun main() = runBlocking { + val executor = TaskExecutor() + + val cpuResult = executor.executeCpuTask() + val ioResult = executor.executeIoTask() + val uiResult = executor.executeUiTask() + + println("Results: $cpuResult, $ioResult, $uiResult") +} +``` + +### **Exercise 2: Context Combination** +Create a coroutine system with custom context combinations. + +**Solution:** +```kotlin +import kotlinx.coroutines.* +import java.util.concurrent.Executors + +fun main() = runBlocking { + // Custom context elements + val customDispatcher = Executors.newFixedThreadPool(3).asCoroutineDispatcher() + val exceptionHandler = CoroutineExceptionHandler { _, exception -> + println("Custom handler: ${exception.message}") + } + val parentJob = SupervisorJob() + + // Combine all elements + val context = customDispatcher + exceptionHandler + parentJob + + val jobs = List(5) { index -> + launch(context) { + println("Job $index on custom context: ${Thread.currentThread().name}") + + if (index == 2) { + throw RuntimeException("Job $index failed") + } + + delay(1000) + println("Job $index completed") + } + } + + try { + jobs.forEach { it.join() } + } catch (e: Exception) { + println("Caught exception: ${e.message}") + } finally { + customDispatcher.close() + } +} +``` + +### **Exercise 3: Performance Optimization** +Create a data processing pipeline that optimizes dispatcher usage. + +**Solution:** +```kotlin +import kotlinx.coroutines.* + +class OptimizedDataProcessor { + suspend fun processLargeDataset(data: List): List { + return withContext(Dispatchers.IO) { + println("Reading data on IO dispatcher: ${Thread.currentThread().name}") + + // Simulate reading large dataset + delay(1000) + + // Process data in parallel with CPU dispatcher + data.map { item -> + async(Dispatchers.Default) { + processItem(item) + } + }.awaitAll() + } + } + + private suspend fun processItem(item: String): String { + return withContext(Dispatchers.Default) { + // CPU-intensive processing + var result = 0 + repeat(100000) { + result += item.hashCode() * it + } + "Processed: $item (hash: $result)" + } + } +} + +fun main() = runBlocking { + val processor = OptimizedDataProcessor() + + val data = List(10) { "Item$it" } + val start = System.currentTimeMillis() + + val results = processor.processLargeDataset(data) + + val duration = System.currentTimeMillis() - start + println("Processing completed in ${duration}ms") + println("Results: ${results.take(3)}...") +} +``` + +### **Exercise 4: Context Management** +Implement a coroutine system with proper context lifecycle management. + +**Solution:** +```kotlin +import kotlinx.coroutines.* +import java.util.concurrent.Executors + +class ManagedCoroutineSystem { + private val customDispatcher = Executors.newFixedThreadPool(4).asCoroutineDispatcher() + private val exceptionHandler = CoroutineExceptionHandler { _, exception -> + println("System exception: ${exception.message}") + } + + suspend fun executeTask(taskName: String, duration: Long): String { + return withContext(customDispatcher + exceptionHandler) { + println("Executing $taskName on: ${Thread.currentThread().name}") + delay(duration) + "$taskName completed" + } + } + + suspend fun executeParallelTasks(tasks: List>): List { + return withContext(customDispatcher + exceptionHandler) { + tasks.map { (name, duration) -> + async { + executeTask(name, duration) + } + }.awaitAll() + } + } + + fun shutdown() { + customDispatcher.close() + println("System shutdown complete") + } +} + +fun main() = runBlocking { + val system = ManagedCoroutineSystem() + + try { + // Single task + val result1 = system.executeTask("Quick Task", 500) + println("Result: $result1") + + // Parallel tasks + val tasks = listOf( + "Task A" to 1000L, + "Task B" to 800L, + "Task C" to 1200L + ) + + val results = system.executeParallelTasks(tasks) + println("Parallel results: $results") + + } finally { + system.shutdown() + } +} +``` + +## ๐Ÿšจ Common Mistakes to Avoid + +1. **Using wrong dispatcher**: Don't use IO for CPU work or Default for IO work โœ… +2. **Forgetting context cleanup**: Always close custom dispatchers โœ… +3. **Ignoring context inheritance**: Understand how context flows to children โœ… +4. **Overusing Unconfined**: Only use for testing or special cases โœ… + +## ๐ŸŽฏ What's Next? + +Now that you understand context and dispatchers, continue with: + +1. **Sequential vs Concurrent** โ†’ [Sequential vs Concurrent](../coroutines/05-sequential-concurrent.md) +2. **Advanced Coroutines** โ†’ [Coroutine Scopes](../coroutines/06-coroutine-scopes.md) +3. **Exception Handling** โ†’ [Exception Handling](../coroutines/03-exception-handling.md) +4. **Functional Programming** โ†’ [Lambdas and Higher-Order Functions](../functional-programming/01-lambdas.md) + +## ๐Ÿ“š Additional Resources + +- [Official Coroutines Context Documentation](https://kotlinlang.org/docs/coroutine-context-and-dispatchers.html) +- [Dispatchers Guide](https://kotlinlang.org/docs/coroutine-context-and-dispatchers.html#dispatchers-and-threads) +- [Context Elements](https://kotlinlang.org/docs/coroutine-context-and-dispatchers.html#job-in-the-context) + +## ๐Ÿ† Summary + +- โœ… You understand coroutine context structure and elements +- โœ… You can use different dispatchers for various types of work +- โœ… You know how to combine context elements effectively +- โœ… You can manage coroutine lifecycle with context +- โœ… You're ready to optimize performance with appropriate dispatchers! + +**Context and dispatchers are the foundation of efficient coroutine usage. Master them for optimal concurrent performance! ๐ŸŽ‰** diff --git a/docs/functional-programming/03-let-also-run.md b/docs/functional-programming/03-let-also-run.md new file mode 100644 index 0000000..b88d220 --- /dev/null +++ b/docs/functional-programming/03-let-also-run.md @@ -0,0 +1,489 @@ +# ๐Ÿ”ง Advanced Scope Functions: let, also, and run + +This lesson covers the remaining scope functions in Kotlin: `let`, `also`, and `run`. These functions provide different ways to work with objects in temporary scopes, each with their own use cases and benefits. + +## ๐Ÿ“‹ Learning Objectives + +By the end of this lesson, you will be able to: +- โœ… Use the `let` function for safe operations and transformations +- โœ… Apply the `also` function for side effects and logging +- โœ… Utilize the `run` function for object configuration and computation +- โœ… Choose the right scope function for different scenarios +- โœ… Chain scope functions effectively + +## ๐Ÿ” What You'll Learn + +- **Let function** - Safe operations and transformations +- **Also function** - Side effects and logging +- **Run function** - Object configuration and computation +- **Function chaining** - Combining multiple scope functions +- **Real-world applications** - Practical use cases + +## ๐Ÿ“ Prerequisites + +- Understanding of [Scope Functions](02-scope-functions.md) +- Knowledge of [Lambdas and Higher-Order Functions](01-lambdas.md) +- Familiarity with [Null Safety](../null-safety/01-null-safety.md) + +## ๐Ÿ’ป The Code + +Let's examine the scope function examples: + +**๐Ÿ“ File:** [52_let_scope_function.kt](../src/52_let_scope_function.kt) +**๐Ÿ“ File:** [53_run_scope_function.kt](../src/53_run_scope_function.kt) +**๐Ÿ“ File:** [51_also_scope_function.kt](../src/51_also_scope_function.kt) + +## ๐Ÿ” Code Breakdown + +### **1. The `let` Function** + +#### **Basic Usage** +```kotlin +// let provides the object as 'it' and returns the lambda result +val name: String? = "Alice" +val length = name?.let { + println("Processing name: $it") + it.length // Return value +} + +// Safe call with let +val nullableName: String? = null +val result = nullableName?.let { + "Hello, $it!" +} ?: "Hello, Guest!" +``` + +#### **Let with Collections** +```kotlin +val numbers = listOf(1, 2, 3, 4, 5) +val result = numbers + .filter { it % 2 == 0 } + .let { evenNumbers -> + println("Found ${evenNumbers.size} even numbers") + evenNumbers.sum() + } +``` + +#### **Let for Safe Operations** +```kotlin +fun processUser(user: User?) { + user?.let { safeUser -> + println("Processing user: ${safeUser.name}") + safeUser.email?.let { email -> + println("Sending email to: $email") + sendEmail(email) + } + } +} +``` + +### **2. The `also` Function** + +#### **Basic Usage** +```kotlin +// also provides the object as 'it' and returns the object itself +val numbers = mutableListOf(1, 2, 3) +val result = numbers.also { + println("Original list: $it") + it.add(4) + println("After adding 4: $it") +} +// result is the same list, not the println result +``` + +#### **Also for Logging and Side Effects** +```kotlin +val person = Person("David", 40).also { + println("Created person: ${it.name}") + println("Age: ${it.age}") + // Log to database + logPersonCreation(it) +} +``` + +#### **Also for Validation** +```kotlin +fun createUser(name: String, email: String): User { + return User(name, email).also { user -> + // Validate user data + require(user.name.isNotBlank()) { "Name cannot be blank" } + require(user.email.contains("@")) { "Invalid email format" } + + // Log creation + println("User created: $user") + } +} +``` + +### **3. The `run` Function** + +#### **Basic Usage** +```kotlin +// run provides the object as 'this' and returns the lambda result +val person = Person("Alice", 25) +val description = person.run { + println("Processing: $name") + "Name: $name, Age: $age" // Return value +} +``` + +#### **Run for Object Configuration** +```kotlin +val button = Button().run { + text = "Click Me" + color = "Blue" + size = 100 + onClick = { println("Button clicked!") } + this // Return the configured button +} +``` + +#### **Run for Computation** +```kotlin +val result = "Hello, World!".run { + println("Original: $this") + val upper = uppercase() + val length = length + "Uppercase: $upper, Length: $length" +} +``` + +### **4. Comparing All Scope Functions** + +#### **Function Comparison Table** +```kotlin +data class Person(var name: String, var age: Int) + +fun demonstrateAllScopeFunctions() { + val person = Person("Alice", 25) + + // let - returns lambda result, object as 'it' + val letResult = person.let { + "Name: ${it.name}" + } + + // also - returns object, object as 'it' + val alsoResult = person.also { + println("Person: ${it.name}") + } + + // run - returns lambda result, object as 'this' + val runResult = person.run { + "Name: $name" + } + + // with - returns lambda result, object as 'this' + val withResult = with(person) { + "Name: $name" + } + + // apply - returns object, object as 'this' + val applyResult = person.apply { + age = 26 + } + + println("let: $letResult") + println("also: $alsoResult") + println("run: $runResult") + println("with: $withResult") + println("apply: $applyResult") +} +``` + +### **5. Advanced Patterns** + +#### **Chaining Scope Functions** +```kotlin +val result = "hello world" + .let { it.trim() } + .also { println("Trimmed: $it") } + .run { + split(" ") + .map { word -> word.capitalize() } + .joinToString(" ") + } + .also { println("Final result: $it") } +``` + +#### **Conditional Scope Functions** +```kotlin +fun processData(data: String?) { + data?.let { safeData -> + safeData + .also { println("Processing: $it") } + .run { + if (length> 10) { + "Long data: ${substring(0, 10)}..." + } else { + "Short data: $this" + } + } + .also { println("Result: $it") } + } ?: println("No data to process") +} +``` + +#### **Scope Functions with Collections** +```kotlin +val numbers = listOf(1, 2, 3, 4, 5, 6, 7, 8, 9, 10) + +val result = numbers + .let { list -> + list.filter { it % 2 == 0 } + } + .also { evenNumbers -> + println("Even numbers: $evenNumbers") + } + .run { evenNumbers -> + evenNumbers.map { it * it } + } + .also { squares -> + println("Squares: $squares") + } + .let { squares -> + squares.sum() + } +``` + +## ๐ŸŽฏ Key Concepts Explained + +### **1. Return Values** +```kotlin +val person = Person("Alice", 25) + +// Functions that return lambda result +val letResult = person.let { it.name.length } // Int +val runResult = person.run { name.length } // Int +val withResult = with(person) { name.length } // Int + +// Functions that return the object +val alsoResult = person.also { println(it.name) } // Person +val applyResult = person.apply { age = 26 } // Person +``` + +### **2. Context Object: `this` vs `it`** +```kotlin +val person = Person("Alice", 25) + +// Functions that use 'this' (run, with, apply) +person.run { + name = "Bob" // Direct access to properties + age = 30 +} + +// Functions that use 'it' (let, also) +person.let { + it.name = "Charlie" // Access via 'it' + it.age = 35 +} +``` + +### **3. When to Use Each Function** + +#### **Use `let` when:** +- Executing a lambda on non-null values +- Introducing local variables with limited scope +- Chaining operations +- Safe operations on nullable types + +#### **Use `also` when:** +- Adding actions that don't affect the object +- Logging or debugging +- Side effects that don't change the object +- Validation and logging + +#### **Use `run` when:** +- Initializing objects and computing the return value +- Executing a block of statements where an expression is needed +- Object configuration with computation + +## ๐Ÿงช Running the Examples + +### **Step 1: Open the Files** +1. Navigate to `src/52_let_scope_function.kt` +2. Navigate to `src/53_run_scope_function.kt` +3. Navigate to `src/51_also_scope_function.kt` +4. Open them in IntelliJ IDEA + +### **Step 2: Run the Programs** +1. Right-click on each file +2. Select "Run '[filename]Kt'" +3. Observe the output + +## ๐ŸŽฎ Hands-On Exercises + +### **Exercise 1: Safe String Processing with let** +Create a function that safely processes nullable strings using let. + +**Solution:** +```kotlin +fun processString(input: String?): String { + return input?.let { safeInput -> + safeInput + .trim() + .lowercase() + .replace(" ", "_") + } ?: "default_value" +} + +fun main() { + val inputs = listOf(" Hello World ", "Kotlin Programming", null, "") + + inputs.forEach { input -> + val result = processString(input) + println("Input: '$input' -> Output: '$result'") + } +} +``` + +### **Exercise 2: Object Validation with also** +Create a validation system using the also function. + +**Solution:** +```kotlin +data class User( + var name: String = "", + var email: String = "", + var age: Int = 0 +) + +fun createValidUser(name: String, email: String, age: Int): User? { + return User(name, email, age).also { user -> + // Validation rules + require(user.name.isNotBlank()) { "Name cannot be blank" } + require(user.email.contains("@")) { "Invalid email format" } + require(user.age>= 0) { "Age cannot be negative" } + require(user.age <= 150) { "Age seems unrealistic" } + + // Log validation success + println("User validation passed: $user") + } +} + +fun main() { + try { + val user1 = createValidUser("Alice", "alice@email.com", 25) + println("User 1 created: $user1") + + val user2 = createValidUser("", "invalid-email", -5) + println("User 2 created: $user2") + } catch (e: Exception) { + println("Validation failed: ${e.message}") + } +} +``` + +### **Exercise 3: Data Processing Pipeline with run** +Create a data processing pipeline using the run function. + +**Solution:** +```kotlin +class DataProcessor { + private var data: List = emptyList() + + fun setData(newData: List): DataProcessor { + data = newData + return this + } + + fun process(): String { + return run { + val count = data.size + val sum = data.sum() + val average = if (count> 0) sum.toDouble() / count else 0.0 + val min = data.minOrNull() ?: 0 + val max = data.maxOrNull() ?: 0 + + "Count: $count, Sum: $sum, Average: $average, Min: $min, Max: $max" + } + } +} + +fun main() { + val processor = DataProcessor() + val result = processor + .setData(listOf(1, 2, 3, 4, 5, 6, 7, 8, 9, 10)) + .process() + + println("Processing result: $result") +} +``` + +### **Exercise 4: Chaining Multiple Scope Functions** +Create a complex data transformation using multiple scope functions. + +**Solution:** +```kotlin +data class Product(val name: String, val price: Double, val category: String) + +fun processProducts(products: List): String { + return products + .let { productList -> + productList.filter { it.price> 10.0 } + } + .also { expensiveProducts -> + println("Found ${expensiveProducts.size} expensive products") + } + .run { expensiveProducts -> + expensiveProducts + .groupBy { it.category } + .mapValues { (_, products) -> + products.sumOf { it.price } + } + } + .let { categoryTotals -> + categoryTotals.entries + .joinToString("\n") { (category, total) -> + "$category: $${String.format("%.2f", total)}" + } + } + .also { result -> + println("Final result:\n$result") + } +} + +fun main() { + val products = listOf( + Product("Laptop", 999.99, "Electronics"), + Product("Mouse", 29.99, "Electronics"), + Product("Book", 19.99, "Books"), + Product("Chair", 199.99, "Furniture"), + Product("Pen", 2.99, "Office") + ) + + val result = processProducts(products) + println("Processing complete!") +} +``` + +## ๐Ÿšจ Common Mistakes to Avoid + +1. **Confusing return values**: Remember which functions return the object vs lambda result โœ… +2. **Overusing scope functions**: Don't use them when simple property access is clearer โœ… +3. **Nesting scope functions**: Can make code hard to read - use sparingly โœ… +4. **Ignoring context**: Choose the right function based on whether you need `this` or `it` โœ… + +## ๐ŸŽฏ What's Next? + +Now that you understand all scope functions, continue with: + +1. **Advanced Coroutines** โ†’ [Coroutine Exception Handling](../coroutines/03-exception-handling.md) +2. **Advanced OOP** โ†’ [Enums and Sealed Classes](../oop/07-enums-sealed-classes.md) +3. **Collections** โ†’ [Collections Overview](../collections/README.md) +4. **Null Safety** โ†’ [Null Safety](../null-safety/01-null-safety.md) + +## ๐Ÿ“š Additional Resources + +- [Official Kotlin Scope Functions Documentation](https://kotlinlang.org/docs/scope-functions.html) +- [Scope Functions Guide](https://kotlinlang.org/docs/scope-functions.html#scope-functions) +- [Kotlin Standard Library](https://kotlinlang.org/api/latest/jvm/stdlib/) + +## ๐Ÿ† Summary + +- โœ… You understand all five scope functions and their differences +- โœ… You can use `let` for safe operations and transformations +- โœ… You can use `also` for side effects and logging +- โœ… You can use `run` for object configuration and computation +- โœ… You're ready to chain scope functions effectively! + +**Scope functions are powerful tools for making your code more readable and concise. Master them all for maximum Kotlin proficiency! ๐ŸŽ‰** diff --git a/docs/functional-programming/04-predicates.md b/docs/functional-programming/04-predicates.md new file mode 100644 index 0000000..b5665e1 --- /dev/null +++ b/docs/functional-programming/04-predicates.md @@ -0,0 +1,595 @@ +# ๐Ÿ” Predicates in Kotlin + +This lesson covers predicates in Kotlin - functions that return boolean values and are commonly used with collections for filtering, searching, and validation operations. + +## ๐Ÿ“‹ Learning Objectives + +By the end of this lesson, you will be able to: +- โœ… Understand what predicates are and how they work +- โœ… Use predicates with collection functions like filter, any, all, none +- โœ… Create custom predicates for complex conditions +- โœ… Chain predicates for sophisticated filtering +- โœ… Apply predicates in real-world scenarios + +## ๐Ÿ” What You'll Learn + +- **Predicate Basics** - What predicates are and their syntax +- **Collection Predicates** - Using predicates with filter, any, all, none +- **Custom Predicates** - Creating your own predicate functions +- **Predicate Chaining** - Combining multiple predicates +- **Real-world Applications** - Practical use cases and patterns + +## ๐Ÿ“ Prerequisites + +- Understanding of [Lambdas and Higher-Order Functions](01-lambdas.md) +- Knowledge of [Collections Overview](../collections/README.md) +- Familiarity with [Control Flow](../control-flow/README.md) + +## ๐Ÿ’ป The Code + +Let's examine the predicate examples: + +**๐Ÿ“ File:** [45_predicate.kt](../src/45_predicate.kt) + +## ๐Ÿ” Code Breakdown + +### **1. Basic Predicates** + +#### **Simple Predicates** +```kotlin +fun main() { + val numbers = listOf(1, 2, 3, 4, 5, 6, 7, 8, 9, 10) + + // Predicate: is even + val isEven: (Int) -> Boolean = { it % 2 == 0 } + + // Predicate: is greater than 5 + val isGreaterThan5: (Int) -> Boolean = { it> 5 } + + // Predicate: is prime + val isPrime: (Int) -> Boolean = { num -> + if (num < 2) false + else (2..num / 2).none { num % it == 0 } + } + + println("Even numbers: ${numbers.filter(isEven)}") + println("Numbers> 5: ${numbers.filter(isGreaterThan5)}") + println("Prime numbers: ${numbers.filter(isPrime)}") +} +``` + +#### **Predicate with Multiple Parameters** +```kotlin +fun main() { + val people = listOf( + Person("Alice", 25, "Engineer"), + Person("Bob", 30, "Manager"), + Person("Charlie", 22, "Developer"), + Person("Diana", 35, "Designer") + ) + + // Predicate: person is young (under 30) + val isYoung: (Person) -> Boolean = { it.age < 30 } + + // Predicate: person is in tech role + val isInTech: (Person) -> Boolean = { person -> + person.role in listOf("Engineer", "Developer", "Designer") + } + + // Predicate: person meets both criteria + val isYoungAndInTech: (Person) -> Boolean = { person -> + isYoung(person) && isInTech(person) + } + + println("Young people: ${people.filter(isYoung)}") + println("Tech people: ${people.filter(isInTech)}") + println("Young tech people: ${people.filter(isYoungAndInTech)}") +} + +data class Person(val name: String, val age: Int, val role: String) +``` + +### **2. Collection Predicates** + +#### **Filter with Predicates** +```kotlin +fun main() { + val words = listOf("apple", "banana", "cherry", "date", "elderberry", "fig") + + // Predicate: word starts with specific letter + val startsWith = { letter: Char -> { word: String -> word.startsWith(letter) } } + + // Predicate: word has specific length + val hasLength = { length: Int -> { word: String -> word.length == length } } + + // Predicate: word contains specific substring + val contains = { substring: String -> { word: String -> word.contains(substring) } } + + println("Words starting with 'a': ${words.filter(startsWith('a'))}") + println("Words with 5 letters: ${words.filter(hasLength(5))}") + println("Words containing 'er': ${words.filter(contains("er"))}") +} +``` + +#### **Any, All, None with Predicates** +```kotlin +fun main() { + val numbers = listOf(2, 4, 6, 8, 10, 12, 14, 16, 18, 20) + + // Predicate: number is even + val isEven: (Int) -> Boolean = { it % 2 == 0 } + + // Predicate: number is divisible by 4 + val isDivisibleBy4: (Int) -> Boolean = { it % 4 == 0 } + + // Predicate: number is greater than 15 + val isGreaterThan15: (Int) -> Boolean = { it> 15 } + + println("All numbers are even: ${numbers.all(isEven)}") + println("Any number is divisible by 4: ${numbers.any(isDivisibleBy4)}") + println("No number is greater than 15: ${numbers.none(isGreaterThan15)}") + println("All numbers are divisible by 4: ${numbers.all(isDivisibleBy4)}") +} +``` + +#### **Find and FindLast with Predicates** +```kotlin +fun main() { + val products = listOf( + Product("Laptop", 999.99, "Electronics"), + Product("Mouse", 29.99, "Electronics"), + Product("Book", 19.99, "Books"), + Product("Chair", 199.99, "Furniture"), + Product("Pen", 2.99, "Office") + ) + + // Predicate: product is expensive (> 100ใƒ‰ใƒซ) + val isExpensive: (Product) -> Boolean = { it.price> 100.0 } + + // Predicate: product is in electronics category + val isElectronics: (Product) -> Boolean = { it.category == "Electronics" } + + // Predicate: product is affordable (< 50ใƒ‰ใƒซ) + val isAffordable: (Product) -> Boolean = { it.price < 50.0 } + + val firstExpensive = products.find(isExpensive) + val lastElectronics = products.findLast(isElectronics) + val firstAffordable = products.find(isAffordable) + + println("First expensive product: $firstExpensive") + println("Last electronics product: $lastElectronics") + println("First affordable product: $firstAffordable") +} + +data class Product(val name: String, val price: Double, val category: String) +``` + +### **3. Custom Predicates** + +#### **Complex Predicates** +```kotlin +fun main() { + val students = listOf( + Student("Alice", 85, listOf("Math", "Physics", "Chemistry")), + Student("Bob", 92, listOf("Math", "English", "History")), + Student("Charlie", 78, listOf("Physics", "Chemistry", "Biology")), + Student("Diana", 95, listOf("Math", "Physics", "English")), + Student("Eve", 88, listOf("Chemistry", "Biology", "Math")) + ) + + // Predicate: student is high achiever (grade> 90) + val isHighAchiever: (Student) -> Boolean = { it.grade> 90 } + + // Predicate: student studies math + val studiesMath: (Student) -> Boolean = { it.subjects.contains("Math") } + + // Predicate: student has balanced subjects (mix of sciences and humanities) + val hasBalancedSubjects: (Student) -> Boolean = { student -> + val sciences = listOf("Math", "Physics", "Chemistry", "Biology") + val humanities = listOf("English", "History", "Literature") + + val scienceCount = student.subjects.count { it in sciences } + val humanitiesCount = student.subjects.count { it in humanities } + + scienceCount> 0 && humanitiesCount> 0 + } + + // Predicate: student is math high achiever + val isMathHighAchiever: (Student) -> Boolean = { student -> + isHighAchiever(student) && studiesMath(student) + } + + println("High achievers: ${students.filter(isHighAchiever)}") + println("Math students: ${students.filter(studiesMath)}") + println("Balanced students: ${students.filter(hasBalancedSubjects)}") + println("Math high achievers: ${students.filter(isMathHighAchiever)}") +} + +data class Student(val name: String, val grade: Int, val subjects: List) +``` + +#### **Predicate Factories** +```kotlin +fun main() { + val numbers = listOf(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15) + + // Predicate factory: creates range predicate + fun inRange(min: Int, max: Int): (Int) -> Boolean = { it in min..max } + + // Predicate factory: creates multiple predicate + fun isMultipleOf(factor: Int): (Int) -> Boolean = { it % factor == 0 } + + // Predicate factory: creates comparison predicate + fun compareWith(operator: String, value: Int): (Int) -> Boolean = { num -> + when (operator) { + ">" -> num> value + ">=" -> num>= value + "<" -> num < value + "<=" -> num <= value + "==" -> num == value + "!=" -> num != value + else -> false + } + } + + // Predicate factory: creates composite predicate + fun and(vararg predicates: (T) -> Boolean): (T) -> Boolean = { item -> + predicates.all { it(item) } + } + + fun or(vararg predicates: (T) -> Boolean): (T) -> Boolean = { item -> + predicates.any { it(item) } + } + + fun not(predicate: (T) -> Boolean): (T) -> Boolean = { item -> + !predicate(item) + } + + val inRange5to10 = inRange(5, 10) + val isMultipleOf3 = isMultipleOf(3) + val isGreaterThan7 = compareWith(">", 7) + + // Composite predicates + val inRangeAndMultipleOf3 = and(inRange5to10, isMultipleOf3) + val greaterThan7OrMultipleOf5 = or(isGreaterThan7, isMultipleOf(5)) + val notInRange5to10 = not(inRange5to10) + + println("Numbers in range 5-10: ${numbers.filter(inRange5to10)}") + println("Multiples of 3: ${numbers.filter(isMultipleOf3)}") + println("Numbers> 7: ${numbers.filter(isGreaterThan7)}") + println("In range 5-10 AND multiple of 3: ${numbers.filter(inRangeAndMultipleOf3)}") + println("> 7 OR multiple of 5: ${numbers.filter(greaterThan7OrMultipleOf5)}") + println("NOT in range 5-10: ${numbers.filter(notInRange5to10)}") +} +``` + +### **4. Advanced Predicate Patterns** + +#### **Predicate Composition** +```kotlin +fun main() { + val employees = listOf( + Employee("Alice", "Engineer", 3, 75000), + Employee("Bob", "Manager", 7, 95000), + Employee("Charlie", "Developer", 2, 65000), + Employee("Diana", "Designer", 5, 80000), + Employee("Eve", "Engineer", 4, 78000), + Employee("Frank", "Manager", 10, 110000) + ) + + // Base predicates + val isEngineer: (Employee) -> Boolean = { it.position == "Engineer" } + val isManager: (Employee) -> Boolean = { it.position == "Manager" } + val hasExperience: (Employee) -> Boolean = { it.yearsExperience>= 5 } + val isWellPaid: (Employee) -> Boolean = { it.salary> 80000 } + + // Composite predicates + val isSeniorEngineer = and(isEngineer, hasExperience) + val isExperiencedManager = and(isManager, hasExperience) + val isHighEarningEmployee = and(hasExperience, isWellPaid) + val isJuniorOrLowPaid = or(not(hasExperience), not(isWellPaid)) + + println("Senior Engineers: ${employees.filter(isSeniorEngineer)}") + println("Experienced Managers: ${employees.filter(isExperiencedManager)}") + println("High Earning Experienced: ${employees.filter(isHighEarningEmployee)}") + println("Junior or Low Paid: ${employees.filter(isJuniorOrLowPaid)}") +} + +data class Employee(val name: String, val position: String, val yearsExperience: Int, val salary: Int) +``` + +#### **Predicate with State** +```kotlin +fun main() { + val transactions = listOf( + Transaction("T1", 100.0, "credit"), + Transaction("T2", 50.0, "debit"), + Transaction("T3", 200.0, "credit"), + Transaction("T4", 75.0, "debit"), + Transaction("T5", 150.0, "credit") + ) + + // Predicate with running total + fun createRunningTotalPredicate(threshold: Double): (Transaction) -> Boolean { + var runningTotal = 0.0 + + return { transaction -> + when (transaction.type) { + "credit" -> runningTotal += transaction.amount + "debit" -> runningTotal -= transaction.amount + } + runningTotal>= threshold + } + } + + // Predicate: transaction brings running total above threshold + val bringsTotalAbove200 = createRunningTotalPredicate(200.0) + + val significantTransactions = transactions.filter(bringsTotalAbove200) + println("Transactions that bring total above 200: $significantTransactions") +} + +data class Transaction(val id: String, val amount: Double, val type: String) +``` + +## ๐ŸŽฏ Key Concepts Explained + +### **1. Predicate Characteristics** +- **Return Type**: Always returns `Boolean` +- **Single Responsibility**: Should test one specific condition +- **Reusability**: Can be stored in variables and reused +- **Composability**: Can be combined with logical operators + +### **2. Predicate Composition Patterns** +```kotlin +// AND composition +val isAdultAndEmployed = and(isAdult, isEmployed) + +// OR composition +val isStudentOrRetired = or(isStudent, isRetired) + +// NOT composition +val isNotMinor = not(isMinor) + +// Complex composition +val isEligibleForLoan = and( + isAdult, + isEmployed, + or(hasGoodCredit, hasCoSigner), + not(hasBankruptcy) +) +``` + +### **3. Performance Considerations** +- **Short-circuit evaluation**: `all` and `none` stop at first false/true +- **Lazy evaluation**: Use `asSequence()` for large collections +- **Predicate reuse**: Store predicates in variables to avoid recreation + +## ๐Ÿงช Running the Examples + +### **Step 1: Open the File** +1. Navigate to `src/45_predicate.kt` +2. Open it in IntelliJ IDEA + +### **Step 2: Run the Program** +1. Right-click on the file +2. Select "Run '45_predicateKt'" +3. Observe the output + +## ๐ŸŽฎ Hands-On Exercises + +### **Exercise 1: Email Validation Predicates** +Create predicates for validating email addresses. + +**Solution:** +```kotlin +fun main() { + val emails = listOf( + "user@example.com", + "invalid-email", + "test@domain", + "user.name@company.co.uk", + "@example.com", + "user@.com" + ) + + // Email validation predicates + val hasAtSymbol: (String) -> Boolean = { it.contains("@") } + val hasDomain: (String) -> Boolean = { email -> + val parts = email.split("@") + parts.size == 2 && parts[1].contains(".") + } + val hasValidLength: (String) -> Boolean = { it.length in 5..254 } + val doesntStartWithAt: (String) -> Boolean = { !it.startsWith("@") } + val doesntEndWithAt: (String) -> Boolean = { !it.endsWith("@") } + + // Composite email validator + val isValidEmail = and( + hasAtSymbol, + hasDomain, + hasValidLength, + doesntStartWithAt, + doesntEndWithAt + ) + + emails.forEach { email -> + val isValid = isValidEmail(email) + println("$email: ${if (isValid) "โœ… Valid" else "โŒ Invalid"}") + } +} +``` + +### **Exercise 2: Product Filtering System** +Create a product filtering system using predicates. + +**Solution:** +```kotlin +fun main() { + val products = listOf( + Product("Laptop", 999.99, "Electronics", 4.5), + Product("Mouse", 29.99, "Electronics", 4.2), + Product("Book", 19.99, "Books", 4.8), + Product("Chair", 199.99, "Furniture", 4.0), + Product("Pen", 2.99, "Office", 3.5), + Product("Tablet", 499.99, "Electronics", 4.6) + ) + + // Product predicates + val inCategory = { category: String -> { product: Product -> product.category == category } } + val inPriceRange = { min: Double, max: Double -> + { product: Product -> product.price in min..max } + } + val hasRating = { minRating: Double -> { product: Product -> product.rating>= minRating } } + val isExpensive = { product: Product -> product.price> 100.0 } + val isAffordable = { product: Product -> product.price < 50.0 } + + // Filter combinations + val electronicsUnder500 = and(inCategory("Electronics"), inPriceRange(0.0, 500.0)) + val highRatedExpensive = and(hasRating(4.5), isExpensive) + val affordableOrHighRated = or(isAffordable, hasRating(4.5)) + + println("Electronics under 500ใƒ‰ใƒซ: ${products.filter(electronicsUnder500)}") + println("High-rated expensive items: ${products.filter(highRatedExpensive)}") + println("Affordable or high-rated: ${products.filter(affordableOrHighRated)}") +} + +data class Product(val name: String, val price: Double, val category: String, val rating: Double) +``` + +### **Exercise 3: Student Grade Analysis** +Create predicates for analyzing student grades and performance. + +**Solution:** +```kotlin +fun main() { + val students = listOf( + Student("Alice", listOf(85, 92, 78, 95, 88)), + Student("Bob", listOf(72, 68, 75, 80, 85)), + Student("Charlie", listOf(95, 98, 92, 89, 96)), + Student("Diana", listOf(78, 82, 75, 79, 81)), + Student("Eve", listOf(88, 85, 90, 87, 89)) + ) + + // Grade analysis predicates + val hasHighAverage = { threshold: Double -> + { student: Student -> student.grades.average()>= threshold } + } + val hasConsistentGrades = { maxVariation: Double -> + { student: Student -> + val grades = student.grades + grades.maxOrNull()!! - grades.minOrNull()!! <= maxVariation + } + } + val hasImprovingTrend = { student: Student -> + val grades = student.grades + grades.zipWithNext().all { (current, next) -> next>= current } + } + val hasNoFailures = { threshold: Int -> + { student: Student -> student.grades.all { it>= threshold } } + } + + // Analysis combinations + val highAchievers = hasHighAverage(90.0) + val consistentStudents = hasConsistentGrades(10.0) + val improvingStudents = hasImprovingTrend + val passingStudents = hasNoFailures(60) + + println("High achievers (90+ avg): ${students.filter(highAchievers)}") + println("Consistent students (โ‰ค10 variation): ${students.filter(consistentStudents)}") + println("Improving students: ${students.filter(improvingStudents)}") + println("Passing students (60+ all): ${students.filter(passingStudents)}") +} + +data class Student(val name: String, val grades: List) +``` + +### **Exercise 4: Predicate Builder DSL** +Create a domain-specific language for building complex predicates. + +**Solution:** +```kotlin +class PredicateBuilder { + private var predicate: (T) -> Boolean = { true } + + fun and(p: (T) -> Boolean): PredicateBuilder { + val current = predicate + predicate = { item -> current(item) && p(item) } + return this + } + + fun or(p: (T) -> Boolean): PredicateBuilder { + val current = predicate + predicate = { item -> current(item) || p(item) } + return this + } + + fun not(p: (T) -> Boolean): PredicateBuilder { + val current = predicate + predicate = { item -> current(item) && !p(item) } + return this + } + + fun build(): (T) -> Boolean = predicate +} + +fun predicate(init: PredicateBuilder.() -> Unit): (T) -> Boolean { + return PredicateBuilder().apply(init).build() +} + +fun main() { + val numbers = listOf(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15) + + // Using the predicate builder DSL + val complexPredicate = predicate { + and { it> 5 } + and { it % 2 == 0 } + or { it % 3 == 0 } + not { it == 10 } + } + + val result = numbers.filter(complexPredicate) + println("Numbers matching complex predicate: $result") + + // Another example + val rangePredicate = predicate { + and { it in 1..20 } + and { it % 2 == 0 } + or { it % 7 == 0 } + } + + val rangeResult = numbers.filter(rangePredicate) + println("Numbers in range with conditions: $rangeResult") +} +``` + +## ๐Ÿšจ Common Mistakes to Avoid + +1. **Complex predicates**: Keep predicates simple and focused โœ… +2. **Ignoring performance**: Use sequences for large collections โœ… +3. **Forgetting composition**: Leverage predicate composition patterns โœ… +4. **Hardcoding values**: Use predicate factories for flexibility โœ… + +## ๐ŸŽฏ What's Next? + +Now that you understand predicates, continue with: + +1. **Scope Functions** โ†’ [Advanced Scope Functions](03-let-also-run.md) +2. **Collections** โ†’ [Collections Overview](../collections/README.md) +3. **Coroutines** โ†’ [Coroutines Introduction](../coroutines/01-introduction.md) +4. **Advanced OOP** โ†’ [Enums and Sealed Classes](../oop/07-enums-sealed-classes.md) + +## ๐Ÿ“š Additional Resources + +- [Official Kotlin Collections Documentation](https://kotlinlang.org/docs/collections-overview.html) +- [Collection Operations](https://kotlinlang.org/docs/collection-operations.html) +- [Lambda Functions](https://kotlinlang.org/docs/lambdas.html) + +## ๐Ÿ† Summary + +- โœ… You understand what predicates are and how they work +- โœ… You can use predicates with collection functions like filter, any, all, none +- โœ… You can create custom predicates for complex conditions +- โœ… You know how to chain predicates for sophisticated filtering +- โœ… You're ready to apply predicates in real-world scenarios! + +**Predicates are powerful tools for data filtering and validation. Master them for clean and expressive collection operations! ๐ŸŽ‰** diff --git a/docs/oop/07-enums-sealed-classes.md b/docs/oop/07-enums-sealed-classes.md new file mode 100644 index 0000000..384ea86 --- /dev/null +++ b/docs/oop/07-enums-sealed-classes.md @@ -0,0 +1,817 @@ +# ๐ŸŽญ Advanced OOP: Enums, Sealed Classes, and Companion Objects + +This lesson covers advanced object-oriented programming concepts in Kotlin: enums, sealed classes, and companion objects. These features provide powerful ways to model domain concepts and organize code effectively. + +## ๐Ÿ“‹ Learning Objectives + +By the end of this lesson, you will be able to: +- โœ… Use enums to represent fixed sets of values with properties and methods +- โœ… Implement sealed classes for restricted class hierarchies +- โœ… Utilize companion objects for static members and factory methods +- โœ… Choose the right approach for different modeling scenarios +- โœ… Apply these patterns in real-world applications + +## ๐Ÿ” What You'll Learn + +- **Enums** - Fixed value sets with properties and behavior +- **Sealed Classes** - Restricted inheritance hierarchies +- **Companion Objects** - Static members and factory patterns +- **Pattern Matching** - Using when expressions with these types +- **Real-world Applications** - Practical use cases and best practices + +## ๐Ÿ“ Prerequisites + +- Understanding of [Classes and Constructors](../oop/01-classes-constructors.md) +- Knowledge of [Inheritance](../oop/02-inheritance.md) +- Familiarity with [Interfaces](../oop/03-interfaces.md) +- Basic knowledge of [Control Flow](../control-flow/02-when-expressions.md) + +## ๐Ÿ’ป The Code + +Let's examine the advanced OOP examples: + +**๐Ÿ“ File:** [34_1_enum_class](../src/34_1_enum_class) +**๐Ÿ“ File:** [34_2_sealed_class.kt](../src/34_2_sealed_class.kt) +**๐Ÿ“ File:** [34_companion_object.kt](../src/34_companion_object.kt) + +## ๐Ÿ” Code Breakdown + +### **1. Enums (Enumerations)** + +#### **Basic Enum Declaration** +```kotlin +enum class Direction { + NORTH, SOUTH, EAST, WEST +} + +fun main() { + val direction = Direction.NORTH + println("Direction: $direction") + + // Iterate through all enum values + Direction.values().forEach { dir -> + println("Available direction: $dir") + } +} +``` + +#### **Enum with Properties and Methods** +```kotlin +enum class Color(val rgb: Int, val hex: String) { + RED(0xFF0000, "#FF0000") { + override fun description() = "Warm and energetic" + }, + GREEN(0x00FF00, "#00FF00") { + override fun description() = "Natural and peaceful" + }, + BLUE(0x0000FF, "#0000FF") { + override fun description() = "Calm and trustworthy" + }; + + abstract fun description() + + fun isWarm(): Boolean = this == RED + fun isCool(): Boolean = this == BLUE +} + +fun main() { + val color = Color.RED + println("Color: ${color.name}") + println("RGB: ${color.rgb}") + println("Hex: ${color.hex}") + println("Description: ${color.description()}") + println("Is warm: ${color.isWarm()}") +} +``` + +#### **Enum with Parameters** +```kotlin +enum class Planet( + val mass: Double, + val radius: Double, + val distanceFromSun: Double +) { + MERCURY(3.303e23, 2.4397e6, 5.79e10), + VENUS(4.869e24, 6.0518e6, 1.08e11), + EARTH(5.976e24, 6.37814e6, 1.496e11), + MARS(6.421e23, 3.3972e6, 2.279e11); + + fun surfaceGravity(): Double { + val g = 6.67300e-11 + return g * mass / (radius * radius) + } + + fun surfaceWeight(otherMass: Double): Double { + return otherMass * surfaceGravity() + } +} + +fun main() { + val earth = Planet.EARTH + println("Earth surface gravity: ${earth.surfaceGravity()} m/s2") + + val myWeight = earth.surfaceWeight(70.0) + println("My weight on Earth: $myWeight N") +} +``` + +### **2. Sealed Classes** + +#### **Basic Sealed Class** +```kotlin +sealed class Result { + data class Success(val data: T) : Result() + data class Error(val message: String) : Result() + object Loading : Result() +} + +fun processResult(result: Result) { + when (result) { + is Result.Success -> { + println("Success: ${result.data}") + } + is Result.Error -> { + println("Error: ${result.message}") + } + is Result.Loading -> { + println("Loading...") + } + } +} + +fun main() { + val results = listOf( + Result.Success("Data loaded"), + Result.Error("Network failed"), + Result.Loading + ) + + results.forEach { processResult(it) } +} +``` + +#### **Sealed Class with Complex Hierarchy** +```kotlin +sealed class Expression { + data class Number(val value: Double) : Expression() + data class Sum(val left: Expression, val right: Expression) : Expression() + data class Product(val left: Expression, val right: Expression) : Expression() + data class Variable(val name: String) : Expression() + + fun evaluate(variables: Map = emptyMap()): Double { + return when (this) { + is Number -> value + is Sum -> left.evaluate(variables) + right.evaluate(variables) + is Product -> left.evaluate(variables) * right.evaluate(variables) + is Variable -> variables[name] ?: throw IllegalArgumentException("Variable $name not found") + } + } +} + +fun main() { + // Expression: 2 * (x + 3) + val expression = Expression.Product( + Expression.Number(2.0), + Expression.Sum( + Expression.Variable("x"), + Expression.Number(3.0) + ) + ) + + val variables = mapOf("x" to 5.0) + val result = expression.evaluate(variables) + println("2 * (5 + 3) = $result") +} +``` + +#### **Sealed Class for State Management** +```kotlin +sealed class NetworkState { + object Idle : NetworkState() + object Loading : NetworkState() + data class Success(val data: T) : NetworkState() + data class Error(val message: String, val code: Int) : NetworkState() +} + +class NetworkManager { + private var currentState: NetworkState = NetworkState.Idle + + fun updateState(newState: NetworkState) { + currentState = newState + when (newState) { + is NetworkState.Idle -> println("Network idle") + is NetworkState.Loading -> println("Network loading...") + is NetworkState.Success -> println("Network success: ${newState.data}") + is NetworkState.Error -> println("Network error: ${newState.message} (${newState.code})") + } + } + + fun getCurrentState(): NetworkState = currentState +} + +fun main() { + val manager = NetworkManager() + + manager.updateState(NetworkState.Loading) + manager.updateState(NetworkState.Success("Data received")) + manager.updateState(NetworkState.Error("Connection failed", 404)) +} +``` + +### **3. Companion Objects** + +#### **Basic Companion Object** +```kotlin +class DatabaseConnection private constructor(val url: String) { + companion object { + private var instance: DatabaseConnection? = null + + fun getInstance(url: String = "default.db"): DatabaseConnection { + if (instance == null) { + instance = DatabaseConnection(url) + } + return instance!! + } + + const val DEFAULT_TIMEOUT = 30000 + const val MAX_CONNECTIONS = 10 + } + + fun connect() { + println("Connecting to database: $url") + } +} + +fun main() { + val db1 = DatabaseConnection.getInstance("production.db") + val db2 = DatabaseConnection.getInstance("test.db") + + println("Same instance: ${db1 === db2}") + db1.connect() + + println("Default timeout: ${DatabaseConnection.DEFAULT_TIMEOUT}ms") +} +``` + +#### **Companion Object with Factory Methods** +```kotlin +class User private constructor( + val id: String, + val name: String, + val email: String +) { + companion object { + fun createGuest(): User { + return User("guest_${System.currentTimeMillis()}", "Guest", "guest@example.com") + } + + fun createAdmin(name: String, email: String): User { + return User("admin_${System.currentTimeMillis()}", name, email) + } + + fun createRegular(name: String, email: String): User { + return User("user_${System.currentTimeMillis()}", name, email) + } + + fun validateEmail(email: String): Boolean { + return email.contains("@") && email.contains(".") + } + } + + override fun toString(): String { + return "User(id='$id', name='$name', email='$email')" + } +} + +fun main() { + val guest = User.createGuest() + val admin = User.createAdmin("Admin User", "admin@company.com") + val regular = User.createRegular("John Doe", "john@email.com") + + println("Guest: $guest") + println("Admin: $admin") + println("Regular: $regular") + + println("Valid email: ${User.validateEmail("test@example.com")}") + println("Invalid email: ${User.validateEmail("invalid-email")}") +} +``` + +#### **Companion Object with Extension Functions** +```kotlin +class StringUtils { + companion object { + fun isPalindrome(text: String): Boolean { + val clean = text.lowercase().replace(Regex("[^a-z0-9]"), "") + return clean == clean.reversed() + } + + fun countWords(text: String): Int { + return text.trim().split(Regex("\\s+")).size + } + } +} + +// Extension functions on companion object +fun StringUtils.Companion.capitalizeWords(text: String): String { + return text.split(" ").joinToString(" ") { word -> + word.lowercase().replaceFirstChar { it.uppercase() } + } +} + +fun main() { + val text = "A man a plan a canal Panama" + + println("Text: $text") + println("Is palindrome: ${StringUtils.isPalindrome(text)}") + println("Word count: ${StringUtils.countWords(text)}") + println("Capitalized: ${StringUtils.capitalizeWords(text)}") +} +``` + +### **4. Advanced Patterns** + +#### **Enum with Sealed Class Combination** +```kotlin +enum class PaymentMethod { + CREDIT_CARD, DEBIT_CARD, BANK_TRANSFER, CASH +} + +sealed class PaymentResult { + data class Success(val transactionId: String, val amount: Double) : PaymentResult() + data class Failure(val reason: String, val code: Int) : PaymentResult() + object Pending : PaymentResult() +} + +class PaymentProcessor { + fun processPayment(method: PaymentMethod, amount: Double): PaymentResult { + return when (method) { + PaymentMethod.CREDIT_CARD -> { + if (amount> 1000) { + PaymentResult.Failure("Amount exceeds limit", 400) + } else { + PaymentResult.Success("TXN_${System.currentTimeMillis()}", amount) + } + } + PaymentMethod.DEBIT_CARD -> { + PaymentResult.Success("TXN_${System.currentTimeMillis()}", amount) + } + PaymentMethod.BANK_TRANSFER -> { + PaymentResult.Pending + } + PaymentMethod.CASH -> { + PaymentResult.Success("CASH_${System.currentTimeMillis()}", amount) + } + } + } +} + +fun main() { + val processor = PaymentProcessor() + + val methods = PaymentMethod.values() + val amounts = listOf(500.0, 1500.0, 200.0) + + methods.forEach { method -> + amounts.forEach { amount -> + val result = processor.processPayment(method, amount) + println("$method - $${amount}: $result") + } + } +} +``` + +#### **Companion Object with Sealed Class** +```kotlin +sealed class ValidationResult { + object Valid : ValidationResult() + data class Invalid(val errors: List) : ValidationResult() +} + +class UserValidator { + companion object { + fun validateName(name: String): ValidationResult { + val errors = mutableListOf() + + if (name.isBlank()) { + errors.add("Name cannot be blank") + } + if (name.length < 2) { + errors.add("Name must be at least 2 characters") + } + if (name.length> 50) { + errors.add("Name cannot exceed 50 characters") + } + + return if (errors.isEmpty()) ValidationResult.Valid else ValidationResult.Invalid(errors) + } + + fun validateEmail(email: String): ValidationResult { + val errors = mutableListOf() + + if (email.isBlank()) { + errors.add("Email cannot be blank") + } + if (!email.contains("@")) { + errors.add("Email must contain @ symbol") + } + if (!email.contains(".")) { + errors.add("Email must contain domain") + } + + return if (errors.isEmpty()) ValidationResult.Valid else ValidationResult.Invalid(errors) + } + + fun validateAge(age: Int): ValidationResult { + return when { + age < 0 -> ValidationResult.Invalid(listOf("Age cannot be negative")) + age < 13 -> ValidationResult.Invalid(listOf("Must be at least 13 years old")) + age> 120 -> ValidationResult.Invalid(listOf("Age seems unrealistic")) + else -> ValidationResult.Valid + } + } + } +} + +fun main() { + val testCases = listOf( + "name" to "John", + "name" to "", + "name" to "A", + "email" to "john@example.com", + "email" to "invalid-email", + "age" to 25, + "age" to -5, + "age" to 150 + ) + + testCases.forEach { (field, value) -> + val result = when (field) { + "name" -> UserValidator.validateName(value as String) + "email" -> UserValidator.validateEmail(value as String) + "age" -> UserValidator.validateAge(value as Int) + else -> ValidationResult.Invalid(listOf("Unknown field")) + } + + println("$field: $value -> $result") + } +} +``` + +## ๐ŸŽฏ Key Concepts Explained + +### **1. When to Use Each Approach** + +| Feature | Use Case | Example | +|---------|----------|---------| +| **Enum** | Fixed set of values with properties/methods | Days of week, colors, status codes | +| **Sealed Class** | Restricted inheritance hierarchy | Result types, state machines, expressions | +| **Companion Object** | Static members, factory methods, utilities | Singletons, validation, constants | + +### **2. Pattern Matching with When Expressions** +```kotlin +// Exhaustive when expressions with sealed classes +fun handleResult(result: Result) = when (result) { + is Result.Success -> "Success: ${result.data}" + is Result.Error -> "Error: ${result.message}" + is Result.Loading -> "Loading..." +} + +// When with enums +fun getDirectionInfo(direction: Direction) = when (direction) { + Direction.NORTH -> "Going up" + Direction.SOUTH -> "Going down" + Direction.EAST -> "Going right" + Direction.WEST -> "Going left" +} +``` + +### **3. Inheritance and Polymorphism** +- **Enums**: Can implement interfaces, have abstract methods +- **Sealed Classes**: Support inheritance, but only within the same file +- **Companion Objects**: Can inherit from other classes, implement interfaces + +## ๐Ÿงช Running the Examples + +### **Step 1: Open the Files** +1. Navigate to `src/34_1_enum_class` +2. Navigate to `src/34_2_sealed_class.kt` +3. Navigate to `src/34_companion_object.kt` +4. Open them in IntelliJ IDEA + +### **Step 2: Run the Programs** +1. Right-click on each file +2. Select "Run '[filename]Kt'" +3. Observe the output + +## ๐ŸŽฎ Hands-On Exercises + +### **Exercise 1: Enum with Business Logic** +Create an enum for different user roles with associated permissions. + +**Solution:** +```kotlin +enum class UserRole(val permissions: List, val maxFileSize: Long) { + GUEST(emptyList(), 1024 * 1024) { + override fun canAccessAdminPanel() = false + }, + USER(listOf("read", "write"), 10 * 1024 * 1024) { + override fun canAccessAdminPanel() = false + }, + MODERATOR(listOf("read", "write", "moderate"), 50 * 1024 * 1024) { + override fun canAccessAdminPanel() = false + }, + ADMIN(listOf("read", "write", "moderate", "delete", "manage_users"), Long.MAX_VALUE) { + override fun canAccessAdminPanel() = true + }; + + abstract fun canAccessAdminPanel(): Boolean + + fun hasPermission(permission: String): Boolean { + return permissions.contains(permission) + } +} + +fun main() { + val roles = UserRole.values() + + roles.forEach { role -> + println("Role: ${role.name}") + println(" Permissions: ${role.permissions}") + println(" Max file size: ${role.maxFileSize} bytes") + println(" Can access admin: ${role.canAccessAdminPanel()}") + println(" Has write permission: ${role.hasPermission("write")}") + println() + } +} +``` + +### **Exercise 2: Sealed Class for API Responses** +Create a sealed class hierarchy for handling different types of API responses. + +**Solution:** +```kotlin +sealed class ApiResponse { + data class Success(val data: T, val statusCode: Int) : ApiResponse() + data class Error(val message: String, val statusCode: Int, val timestamp: Long) : ApiResponse() + data class ValidationError(val field: String, val message: String) : ApiResponse() + object Unauthorized : ApiResponse() + object NotFound : ApiResponse() + object ServerError : ApiResponse() +} + +class ApiHandler { + fun handleResponse(response: ApiResponse) { + when (response) { + is ApiResponse.Success -> { + println("โœ… Success (${response.statusCode}): ${response.data}") + } + is ApiResponse.Error -> { + println("โŒ Error (${response.statusCode}): ${response.message}") + } + is ApiResponse.ValidationError -> { + println("โš ๏ธ Validation Error in ${response.field}: ${response.message}") + } + is ApiResponse.Unauthorized -> { + println("๐Ÿšซ Unauthorized access") + } + is ApiResponse.NotFound -> { + println("๐Ÿ” Resource not found") + } + is ApiResponse.ServerError -> { + println("๐Ÿ’ฅ Internal server error") + } + } + } +} + +fun main() { + val handler = ApiHandler() + + val responses = listOf( + ApiResponse.Success("User data", 200), + ApiResponse.Error("Database connection failed", 500, System.currentTimeMillis()), + ApiResponse.ValidationError("email", "Invalid email format"), + ApiResponse.Unauthorized, + ApiResponse.NotFound, + ApiResponse.ServerError + ) + + responses.forEach { response -> + handler.handleResponse(response) + } +} +``` + +### **Exercise 3: Companion Object with Builder Pattern** +Implement a companion object with builder pattern for creating complex objects. + +**Solution:** +```kotlin +data class Person( + val name: String, + val age: Int, + val email: String, + val phone: String?, + val address: String? +) { + companion object { + class Builder { + private var name: String = "" + private var age: Int = 0 + private var email: String = "" + private var phone: String? = null + private var address: String? = null + + fun name(name: String) = apply { this.name = name } + fun age(age: Int) = apply { this.age = age } + fun email(email: String) = apply { this.email = email } + fun phone(phone: String?) = apply { this.phone = phone } + fun address(address: String?) = apply { this.address = address } + + fun build(): Person { + require(name.isNotBlank()) { "Name is required" } + require(age> 0) { "Age must be positive" } + require(email.isNotBlank()) { "Email is required" } + + return Person(name, age, email, phone, address) + } + } + + fun builder() = Builder() + + fun createDefault() = Person("Unknown", 18, "unknown@example.com", null, null) + + fun validateEmail(email: String): Boolean { + return email.contains("@") && email.contains(".") + } + } +} + +fun main() { + // Using builder pattern + val person1 = Person.builder() + .name("John Doe") + .age(30) + .email("john@example.com") + .phone("123-456-7890") + .address("123 Main St") + .build() + + println("Person 1: $person1") + + // Using factory method + val person2 = Person.createDefault() + println("Person 2: $person2") + + // Validation + println("Valid email: ${Person.validateEmail("test@example.com")}") + println("Invalid email: ${Person.validateEmail("invalid")}") +} +``` + +### **Exercise 4: Combined Pattern** +Create a system that combines enums, sealed classes, and companion objects. + +**Solution:** +```kotlin +enum class OrderStatus(val description: String, val canBeModified: Boolean) { + PENDING("Order is waiting for confirmation", true), + CONFIRMED("Order has been confirmed", true), + PROCESSING("Order is being processed", false), + SHIPPED("Order has been shipped", false), + DELIVERED("Order has been delivered", false), + CANCELLED("Order has been cancelled", false); + + fun nextPossibleStatuses(): List { + return when (this) { + PENDING -> listOf(CONFIRMED, CANCELLED) + CONFIRMED -> listOf(PROCESSING, CANCELLED) + PROCESSING -> listOf(SHIPPED) + SHIPPED -> listOf(DELIVERED) + DELIVERED -> emptyList() + CANCELLED -> emptyList() + } + } +} + +sealed class OrderEvent { + data class Created(val orderId: String, val timestamp: Long) : OrderEvent() + data class StatusChanged(val orderId: String, val oldStatus: OrderStatus, val newStatus: OrderStatus) : OrderEvent() + data class Cancelled(val orderId: String, val reason: String) : OrderEvent() +} + +class OrderManager { + companion object { + private val orders = mutableMapOf() + private val events = mutableListOf() + + fun createOrder(orderId: String): Boolean { + if (orders.containsKey(orderId)) { + return false + } + + orders[orderId] = OrderStatus.PENDING + events.add(OrderEvent.Created(orderId, System.currentTimeMillis())) + return true + } + + fun changeStatus(orderId: String, newStatus: OrderStatus): Boolean { + val currentStatus = orders[orderId] ?: return false + + if (!currentStatus.nextPossibleStatuses().contains(newStatus)) { + return false + } + + orders[orderId] = newStatus + events.add(OrderEvent.StatusChanged(orderId, currentStatus, newStatus)) + return true + } + + fun cancelOrder(orderId: String, reason: String): Boolean { + val currentStatus = orders[orderId] ?: return false + + if (!currentStatus.canBeModified) { + return false + } + + orders[orderId] = OrderStatus.CANCELLED + events.add(OrderEvent.Cancelled(orderId, reason)) + return true + } + + fun getOrderStatus(orderId: String): OrderStatus? = orders[orderId] + + fun getOrderEvents(orderId: String): List { + return events.filter { event -> + when (event) { + is OrderEvent.Created -> event.orderId == orderId + is OrderEvent.StatusChanged -> event.orderId == orderId + is OrderEvent.Cancelled -> event.orderId == orderId + } + } + } + } +} + +fun main() { + val orderId = "ORD-001" + + // Create order + if (OrderManager.createOrder(orderId)) { + println("Order created: $orderId") + } + + // Change status + OrderManager.changeStatus(orderId, OrderStatus.CONFIRMED) + OrderManager.changeStatus(orderId, OrderStatus.PROCESSING) + + // Try to cancel (should fail) + if (!OrderManager.cancelOrder(orderId, "Customer request")) { + println("Cannot cancel order in ${OrderManager.getOrderStatus(orderId)} status") + } + + // Complete order + OrderManager.changeStatus(orderId, OrderStatus.SHIPPED) + OrderManager.changeStatus(orderId, OrderStatus.DELIVERED) + + // Display final status and events + println("Final status: ${OrderManager.getOrderStatus(orderId)}") + println("Order events:") + OrderManager.getOrderEvents(orderId).forEach { event -> + println(" $event") + } +} +``` + +## ๐Ÿšจ Common Mistakes to Avoid + +1. **Forgetting exhaustive when expressions**: Always handle all sealed class cases โœ… +2. **Using enums for complex hierarchies**: Use sealed classes for inheritance โœ… +3. **Overusing companion objects**: Don't put everything in companion objects โœ… +4. **Ignoring enum properties**: Leverage enum properties and methods โœ… + +## ๐ŸŽฏ What's Next? + +Now that you understand advanced OOP concepts, continue with: + +1. **Data Classes** โ†’ [Data Classes](../oop/08-data-classes.md) +2. **Object Declarations** โ†’ [Object Declarations](../oop/09-object-declarations.md) +3. **Coroutines** โ†’ [Coroutines Introduction](../coroutines/01-introduction.md) +4. **Functional Programming** โ†’ [Lambdas and Higher-Order Functions](../functional-programming/01-lambdas.md) + +## ๐Ÿ“š Additional Resources + +- [Official Kotlin Enums Documentation](https://kotlinlang.org/docs/enum-classes.html) +- [Sealed Classes Guide](https://kotlinlang.org/docs/sealed-classes.html) +- [Companion Objects](https://kotlinlang.org/docs/object-declarations.html#companion-objects) + +## ๐Ÿ† Summary + +- โœ… You can use enums to represent fixed sets of values with properties and methods +- โœ… You understand sealed classes for restricted class hierarchies +- โœ… You can utilize companion objects for static members and factory methods +- โœ… You know how to choose the right approach for different modeling scenarios +- โœ… You're ready to apply these patterns in real-world applications! + +**Enums, sealed classes, and companion objects are powerful tools for modeling domain concepts. Master them for elegant and maintainable code! ๐ŸŽ‰** From 5016838661145f7bb761d455de33e8690bec52f2 Mon Sep 17 00:00:00 2001 From: gpl-gowthamchand Date: 2025ๅนด8ๆœˆ25ๆ—ฅ 10:56:47 +0530 Subject: [PATCH 09/23] docs: Update README to original simple format with comprehensive documentation links --- docs/README.md | 233 ++++++++++++++++++++++++------------------------- 1 file changed, 112 insertions(+), 121 deletions(-) diff --git a/docs/README.md b/docs/README.md index 55a5dee..8a9d092 100644 --- a/docs/README.md +++ b/docs/README.md @@ -1,124 +1,115 @@ -# ๐Ÿ“š Kotlin Tutorial Documentation - -Welcome to the comprehensive Kotlin learning guide! This documentation is organized by topic and provides detailed explanations with practical examples. - -## ๐ŸŽฏ How to Use This Documentation - -1. **Start with the topic you want to learn** -2. **Read the detailed explanation** in the `.md` file -3. **Study the code examples** in the linked `.kt` files -4. **Practice with the exercises** provided -5. **Follow the learning path** to progress systematically - -## ๐Ÿ“– Learning Path - -### **๐Ÿš€ Beginner Level (Start Here)** - -#### **Basics** -- [Hello World - Your First Program](basics/01-hello-world.md) โ†’ [Code](src/01_hello_world.kt) -- [Variables and Data Types](basics/02-variables-data-types.md) โ†’ [Code](src/04_variables_data_types.kt) -- [Comments and Documentation](basics/03-comments.md) โ†’ [Code](src/03_comments.kt) -- [Data Types Deep Dive](basics/04-data-types.md) โ†’ [Code](src/07_data_types.kt) -- [String Interpolation](basics/05-string-interpolation.md) โ†’ [Code](src/08_string_interpolation.kt) -- [Ranges and Sequences](basics/06-ranges.md) โ†’ [Code](src/09_ranges.kt) - -#### **Control Flow** -- [If Expressions](control-flow/01-if-expressions.md) โ†’ [Code](src/10_if_expression.kt) -- [When Expressions](control-flow/02-when-expressions.md) โ†’ [Code](src/11_when_expression.kt) -- [For Loops](control-flow/03-for-loops.md) โ†’ [Code](src/12_for_loop.kt) -- [While Loops](control-flow/04-while-loops.md) โ†’ [Code](src/13_while_loop.kt) -- [Break and Continue](control-flow/05-break-continue.md) โ†’ [Code](src/15_break_keyword.kt) - -### **โš™๏ธ Intermediate Level** - -#### **Functions** -- [Functions Basics](functions/01-functions-basics.md) โ†’ [Code](src/17_functions_basics.kt) -- [Functions as Expressions](functions/02-functions-expressions.md) โ†’ [Code](src/18_functions_as_expressions.kt) -- [Extension Functions](functions/03-extension-functions.md) โ†’ [Code](src/22_extension_function_one.kt) -- [Infix Functions](functions/04-infix-functions.md) โ†’ [Code](src/24_infix_function.kt) -- [Named Parameters](functions/05-named-parameters.md) โ†’ [Code](src/21_named_parameters.kt) - -#### **Object-Oriented Programming** -- [Classes and Constructors](oop/01-classes-constructors.md) โ†’ [Code](src/26_class_and_constructor.kt) -- [Inheritance](oop/02-inheritance.md) โ†’ [Code](src/27_inheritance.kt) -- [Method Overriding](oop/03-method-overriding.md) โ†’ [Code](src/28_overriding_methods_properties.kt) -- [Abstract Classes](oop/04-abstract-classes.md) โ†’ [Code](src/30_abstract_class.kt) -- [Interfaces](oop/05-interfaces.md) โ†’ [Code](src/31_interface.kt) -- [Data Classes](oop/06-data-classes.md) โ†’ [Code](src/32_data_class.kt) -- [Enums, Sealed Classes, and Companion Objects](oop/07-enums-sealed-classes.md) โ†’ [Code](src/34_1_enum_class, 34_2_sealed_class.kt, 34_companion_object.kt) - -### **๐Ÿ”ง Advanced Level** - -#### **Functional Programming** -- [Lambdas and Higher-Order Functions](functional-programming/01-lambdas.md) โ†’ [Code](src/35_lambdas_higher_order_functions.kt) -- [Scope Functions](functional-programming/02-scope-functions.md) โ†’ [Code](src/39_with_apply_functions.kt) -- [Advanced Scope Functions](functional-programming/03-let-also-run.md) โ†’ [Code](src/51_also_scope_function.kt, 52_let_scope_function.kt, 53_run_scope_function.kt) -- [Predicates](functional-programming/04-predicates.md) โ†’ [Code](src/45_predicate.kt) - -#### **Advanced Topics** -- [Kotlin-Java Interoperability](advanced/01-kotlin-interoperability.md) โ†’ [Code](src/MyJavaFile.java, myKotlinInteroperability.kt) - -#### **Collections** -- [Arrays](collections/01-arrays.md) โ†’ [Code](src/40_arrays.kt) -- [Lists](collections/02-lists.md) โ†’ [Code](src/41_list.kt) -- [Maps and HashMaps](collections/03-maps.md) โ†’ [Code](src/42_map_hashmap.kt) -- [Sets and HashSets](collections/04-sets.md) โ†’ [Code](src/43_set_hashset.kt) -- [Filter, Map, and Sorting](collections/05-filter-map-sorting.md) โ†’ [Code](src/44_filter_map_sorting.kt) - -#### **Null Safety** -- [Null Safety](null-safety/01-null-safety.md) โ†’ [Code](src/46_null_safety.kt) -- [Lateinit and Lazy](null-safety/02-lateinit-lazy.md) โ†’ [Code](src/47_lateinit_keyword.kt) - -#### **Coroutines** -- [Introduction to Coroutines](coroutines/01-introduction.md) โ†’ [Code](src/61_first_coroutine.kt) -- [Launch and Async](coroutines/02-launch-async.md) โ†’ [Code](src/64_launch_coroutine_builder.kt) -- [Exception Handling](coroutines/03-exception-handling.md) โ†’ [Code](src/70_exception_handling.kt) -- [Context and Dispatchers](coroutines/04-context-dispatchers.md) โ†’ [Code](src/78_CoroutineContext_and_Dispatchers.kt) - -## ๐ŸŽ“ Recommended Learning Order - -### **Week 1: Basics** -1. Start with [Hello World](basics/01-hello-world.md) -2. Learn [Variables and Data Types](basics/02-variables-data-types.md) -3. Understand [Control Flow](control-flow/01-if-expressions.md) -4. Practice [Loops](control-flow/03-for-loops.md) - -### **Week 2: Functions & OOP** -1. Master [Functions](functions/01-functions-basics.md) -2. Learn [Classes](oop/01-classes-constructors.md) -3. Understand [Inheritance](oop/02-inheritance.md) -4. Explore [Data Classes](oop/06-data-classes.md) - -### **Week 3: Advanced Concepts** -1. Dive into [Functional Programming](functional-programming/01-lambdas.md) -2. Master [Collections](collections/01-arrays.md) -3. Learn [Null Safety](null-safety/01-null-safety.md) -4. Practice [Coroutines](coroutines/01-introduction.md) - -## ๐Ÿ› ๏ธ Prerequisites - -- Basic programming concepts (variables, functions, loops) -- No prior Kotlin experience required -- IntelliJ IDEA or Android Studio recommended - -## ๐Ÿ“ How to Use - -1. **Choose a topic** from the learning path above -2. **Read the detailed explanation** in the `.md` file -3. **Study the code examples** in the linked `.kt` file -4. **Run the examples** to see them in action -5. **Complete the exercises** to reinforce learning -6. **Move to the next topic** following the learning path - -## ๐Ÿค Contributing - -Found an error or want to improve something? Contributions are welcome! - -1. Fork the repository -2. Create a feature branch -3. Make your changes -4. Submit a pull request +# Kotlin Programming Tutorial for Beginners +Learn Kotlin Programming, its basics and Fundamentals from scratch. + +## Topics to be covered +0. **Overview** + - [Course introduction, prerequisites and software required](basics/01-hello-world.md) +1. **Installation** + - Install required softwares for Windows, MAC and Linux (Ubuntu) +2. **Getting Started with Kotlin Programming** + - [Run your first app in Kotlin](basics/01-hello-world.md) +3. **Exploring Data Types and Variables** + - [Data Types and Variables](basics/02-variables-data-types.md) + - [String, Literals and String Interpolation](basics/05-string-interpolation.md) + - [Comments](basics/03-comments.md) +4. **Constants, Variables and Data Types** + - [Data Types Deep Dive](basics/04-data-types.md) + - [Ranges and Sequences](basics/06-ranges.md) +5. **Control Flow Statements** + - [IF ELSE](control-flow/01-if-expressions.md) + - [IF Expressions](control-flow/01-if-expressions.md) + - [WHEN Expressions](control-flow/02-when-expressions.md) +6. **Loop Control Statements** + - What are Iterators? + - [FOR Loop and how it works](control-flow/03-for-loops.md) + - [WHILE Loop](control-flow/04-while-loops.md) + - [DO WHILE Loop](control-flow/04-while-loops.md) + - [BREAK statements](control-flow/05-break-continue.md) + - [CONTINUE keyword](control-flow/05-break-continue.md) + - Labelled FOR Loop +7. **Functions and Interoperability** + - [Declaring functions](functions/01-functions-basics.md) + - [Interoperability with Java code](advanced/01-kotlin-interoperability.md) + - [Function as Expressions](functions/02-functions-expressions.md) + - [Extension Functions](functions/03-extension-functions.md) + - [Infix Functions](functions/04-infix-functions.md) + - Default Parameters + - [Named Parameters](functions/05-named-parameters.md) + - Tailrec Functions +8. **Object Oriented Programming in Kotlin** + - [Defining Class and creating Objects](oop/01-classes-constructors.md) + - INIT block + - [Primary and Secondary Constructors](oop/01-classes-constructors.md) + - Properties (Field variables) + - [Inheritance](oop/02-inheritance.md) + - [Method and Property Overriding](oop/03-method-overriding.md) + - Polymorphism + - [Abstract Class, Property and Method](oop/04-abstract-classes.md) + - [Interface](oop/05-interfaces.md) + - [Data Class](oop/06-data-classes.md) + - Object Declaration + - [Enum class](oop/07-enums-sealed-classes.md) + - [Sealed class](oop/07-enums-sealed-classes.md) + - [Companion Object](oop/07-enums-sealed-classes.md) +9. **Functional Programming in Kotlin** + - [Lambdas](functional-programming/01-lambdas.md) + - [Higher-Order Functions](functional-programming/01-lambdas.md) + - Closures + - 'it' keyword + - ['with' function](functional-programming/02-scope-functions.md) + - ['apply' function](functional-programming/02-scope-functions.md) + - [Advanced Scope Functions: let, also, run](functional-programming/03-let-also-run.md) +10. **Collections in Kotlin** + - [Arrays](collections/01-arrays.md) + - [List](collections/02-lists.md) + - [Map and HashMap](collections/03-maps.md) + - [Set and HashSet](collections/04-sets.md) +11. **Sorting and Filtering** + - ["filter" function](collections/05-filter-map-sorting.md) + - ["map" function](collections/05-filter-map-sorting.md) + - [Predicates: all, any, find, count](functional-programming/04-predicates.md) +12. **Kotlin NULL Safety** + - [Safe call](null-safety/01-null-safety.md) + - with Let + - [Elvis](null-safety/01-null-safety.md) + - [Lateinit keyword](null-safety/02-lateinit-lazy.md) + - [Lazy delegation and 'lateinit' vs. 'lazy'](null-safety/02-lateinit-lazy.md) +13. **Scope Functions** + - [with](functional-programming/02-scope-functions.md) + - [apply](functional-programming/02-scope-functions.md) + - [let](functional-programming/03-let-also-run.md) + - [also](functional-programming/03-let-also-run.md) + - [run](functional-programming/03-let-also-run.md) +14. **Coroutines** + - [What are Coroutines? How are they related to Threads?](coroutines/01-introduction.md) + - [launch, async, runBlocking](coroutines/02-launch-async.md) + - [withContext, withTimeoutOrNull](coroutines/04-context-dispatchers.md) + - [Suspending function](coroutines/01-introduction.md) + - [Cancellation and Timeouts](coroutines/03-exception-handling.md) + - [Cooperative suspending functions and isActive flag](coroutines/01-introduction.md) + - [Exception Handling in Coroutines](coroutines/03-exception-handling.md) + - [Sequential execution of suspending function in Coroutines](coroutines/02-launch-async.md) + - [Concurrency within Coroutine](coroutines/02-launch-async.md) + - [lazy 'async'](coroutines/02-launch-async.md) + - [CoroutineScope and CoroutineContext](coroutines/04-context-dispatchers.md) + - [Dispatchers: Confined dispatcher, Default Dispatcher, and Unconfined Dispatcher](coroutines/04-context-dispatchers.md) +15. **Conclusion** + +## Getting Started + +1. Clone this repository +2. Open the project in IntelliJ IDEA +3. Start with the basics and work your way up +4. Each topic has detailed documentation and examples + +## Source Code + +All source code examples are located in the `src/` directory and are linked from the documentation. + +## Authors + +* **Sriyank Siddhartha** --- -**Happy Learning! ๐ŸŽ‰** +Happy Learning! ๐Ÿš€ From 2caddb16b2551bf88d282a2efa163bde2a7766f7 Mon Sep 17 00:00:00 2001 From: gpl-gowthamchand Date: 2025ๅนด8ๆœˆ25ๆ—ฅ 11:01:47 +0530 Subject: [PATCH 10/23] docs: Clean README to only include original format with navigation links --- docs/README.md | 17 +---------------- 1 file changed, 1 insertion(+), 16 deletions(-) diff --git a/docs/README.md b/docs/README.md index 8a9d092..40fa8bd 100644 --- a/docs/README.md +++ b/docs/README.md @@ -95,21 +95,6 @@ Learn Kotlin Programming, its basics and Fundamentals from scratch. - [Dispatchers: Confined dispatcher, Default Dispatcher, and Unconfined Dispatcher](coroutines/04-context-dispatchers.md) 15. **Conclusion** -## Getting Started - -1. Clone this repository -2. Open the project in IntelliJ IDEA -3. Start with the basics and work your way up -4. Each topic has detailed documentation and examples - -## Source Code - -All source code examples are located in the `src/` directory and are linked from the documentation. - ## Authors -* **Sriyank Siddhartha** - ---- - -Happy Learning! ๐Ÿš€ +* **Sriyank Siddhartha** From 35da9082cd1e5cf056c49506b5cd12079757811e Mon Sep 17 00:00:00 2001 From: gpl-gowthamchand Date: 2025ๅนด8ๆœˆ25ๆ—ฅ 11:04:12 +0530 Subject: [PATCH 11/23] docs: Restore README to exact original format with only navigation links added --- docs/README.md | 38 +++++++++++++++++++------------------- 1 file changed, 19 insertions(+), 19 deletions(-) diff --git a/docs/README.md b/docs/README.md index 40fa8bd..ab5f884 100644 --- a/docs/README.md +++ b/docs/README.md @@ -2,24 +2,24 @@ Learn Kotlin Programming, its basics and Fundamentals from scratch. ## Topics to be covered -0. **Overview** +0. Overview - [Course introduction, prerequisites and software required](basics/01-hello-world.md) -1. **Installation** - - Install required softwares for Windows, MAC and Linux (Ubuntu) -2. **Getting Started with Kotlin Programming** +1. Installation + - Install required softwares for Windows, MAC and Linux ( Ubuntu ) +2. Getting Started with Kotlin Programming - [Run your first app in Kotlin](basics/01-hello-world.md) -3. **Exploring Data Types and Variables** +3. Exploring Data Types and Variables - [Data Types and Variables](basics/02-variables-data-types.md) - [String, Literals and String Interpolation](basics/05-string-interpolation.md) - [Comments](basics/03-comments.md) -4. **Constants, Variables and Data Types** +4. Constants, Variables and Data Types - [Data Types Deep Dive](basics/04-data-types.md) - [Ranges and Sequences](basics/06-ranges.md) -5. **Control Flow Statements** +5. Control Flow Statements - [IF ELSE](control-flow/01-if-expressions.md) - [IF Expressions](control-flow/01-if-expressions.md) - [WHEN Expressions](control-flow/02-when-expressions.md) -6. **Loop Control Statements** +6. Loop Control Statements - What are Iterators? - [FOR Loop and how it works](control-flow/03-for-loops.md) - [WHILE Loop](control-flow/04-while-loops.md) @@ -27,7 +27,7 @@ Learn Kotlin Programming, its basics and Fundamentals from scratch. - [BREAK statements](control-flow/05-break-continue.md) - [CONTINUE keyword](control-flow/05-break-continue.md) - Labelled FOR Loop -7. **Functions and Interoperability** +7. Functions and Interoperability - [Declaring functions](functions/01-functions-basics.md) - [Interoperability with Java code](advanced/01-kotlin-interoperability.md) - [Function as Expressions](functions/02-functions-expressions.md) @@ -36,11 +36,11 @@ Learn Kotlin Programming, its basics and Fundamentals from scratch. - Default Parameters - [Named Parameters](functions/05-named-parameters.md) - Tailrec Functions -8. **Object Oriented Programming in Kotlin** +8. Object Oriented Programming in Kotlin - [Defining Class and creating Objects](oop/01-classes-constructors.md) - INIT block - [Primary and Secondary Constructors](oop/01-classes-constructors.md) - - Properties (Field variables) + - Properties ( Field variables ) - [Inheritance](oop/02-inheritance.md) - [Method and Property Overriding](oop/03-method-overriding.md) - Polymorphism @@ -51,36 +51,36 @@ Learn Kotlin Programming, its basics and Fundamentals from scratch. - [Enum class](oop/07-enums-sealed-classes.md) - [Sealed class](oop/07-enums-sealed-classes.md) - [Companion Object](oop/07-enums-sealed-classes.md) -9. **Functional Programming in Kotlin** +9. Functional Programming in Koltin - [Lambdas](functional-programming/01-lambdas.md) - [Higher-Order Functions](functional-programming/01-lambdas.md) - Closures - 'it' keyword - ['with' function](functional-programming/02-scope-functions.md) - ['apply' function](functional-programming/02-scope-functions.md) - - [Advanced Scope Functions: let, also, run](functional-programming/03-let-also-run.md) -10. **Collections in Kotlin** + - [let, also, run](functional-programming/03-let-also-run.md) +10. Collections in Kotlin - [Arrays](collections/01-arrays.md) - [List](collections/02-lists.md) - [Map and HashMap](collections/03-maps.md) - [Set and HashSet](collections/04-sets.md) -11. **Sorting and Filtering** +11. Sorting and Filtering - ["filter" function](collections/05-filter-map-sorting.md) - ["map" function](collections/05-filter-map-sorting.md) - [Predicates: all, any, find, count](functional-programming/04-predicates.md) -12. **Kotlin NULL Safety** +12. Kotlin NULL Safety - [Safe call](null-safety/01-null-safety.md) - with Let - [Elvis](null-safety/01-null-safety.md) - [Lateinit keyword](null-safety/02-lateinit-lazy.md) - [Lazy delegation and 'lateinit' vs. 'lazy'](null-safety/02-lateinit-lazy.md) -13. **Scope Functions** +13. Scope Functions - [with](functional-programming/02-scope-functions.md) - [apply](functional-programming/02-scope-functions.md) - [let](functional-programming/03-let-also-run.md) - [also](functional-programming/03-let-also-run.md) - [run](functional-programming/03-let-also-run.md) -14. **Coroutines** +14. Coroutines - [What are Coroutines? How are they related to Threads?](coroutines/01-introduction.md) - [launch, async, runBlocking](coroutines/02-launch-async.md) - [withContext, withTimeoutOrNull](coroutines/04-context-dispatchers.md) @@ -93,7 +93,7 @@ Learn Kotlin Programming, its basics and Fundamentals from scratch. - [lazy 'async'](coroutines/02-launch-async.md) - [CoroutineScope and CoroutineContext](coroutines/04-context-dispatchers.md) - [Dispatchers: Confined dispatcher, Default Dispatcher, and Unconfined Dispatcher](coroutines/04-context-dispatchers.md) -15. **Conclusion** +15. Conclusion ## Authors From 23211beebbc1e517734a9101b876472d5a8b8e4f Mon Sep 17 00:00:00 2001 From: gpl-gowthamchand Date: 2025ๅนด8ๆœˆ25ๆ—ฅ 11:20:28 +0530 Subject: [PATCH 12/23] docs: Fix root README to show original simple format with navigation links --- README.md | 230 ++++++++++++++++++++++++------------------------------ 1 file changed, 100 insertions(+), 130 deletions(-) diff --git a/README.md b/README.md index 6ff5ddc..1f2c47e 100644 --- a/README.md +++ b/README.md @@ -1,130 +1,100 @@ -# ๐Ÿš€ Kotlin Programming Tutorial for Beginners - -**Learn Kotlin Programming from scratch with practical examples and hands-on exercises.** - -## ๐Ÿ“– Quick Navigation - -- **๐Ÿ“š [Complete Documentation](docs/README.md)** - Start here for detailed learning -- **๐Ÿ’ป [Source Code](src/)** - All Kotlin examples -- **๐ŸŽฏ [Learning Path](docs/README.md#learning-path)** - Recommended study order - -## ๐ŸŽฏ What You'll Learn - -This tutorial covers everything from basic syntax to advanced concepts: - -### **๐Ÿš€ Beginner Level** -- Hello World and basic syntax -- Variables, data types, and constants -- Control flow (if, when, loops) -- Basic functions and parameters - -### **โš™๏ธ Intermediate Level** -- Advanced functions and expressions -- Object-oriented programming -- Collections and data structures -- Null safety and error handling - -### **๐Ÿ”ง Advanced Level** -- Functional programming with lambdas -- Coroutines and asynchronous programming -- Scope functions and advanced features -- Real-world project examples - -## ๐Ÿš€ Quick Start - -1. **Clone this repository:** - ```bash - git clone https://github.com/gpl-gowthamchand/KotlinTutorial.git - cd KotlinTutorial - ``` - -2. **Open in IntelliJ IDEA:** - - File โ†’ Open โ†’ Select the project folder - - Wait for project indexing to complete - -3. **Start Learning:** - - Begin with [Hello World](docs/basics/01-hello-world.md) - - Follow the [Learning Path](docs/README.md#learning-path) - - Practice with exercises in each section - -## ๐Ÿ“ How to Use This Tutorial - -1. **Read the Documentation:** Start with the `.md` file for each topic -2. **Study the Code:** Look at the corresponding `.kt` file -3. **Run Examples:** Execute the code to see it in action -4. **Practice:** Complete the exercises provided -5. **Move Forward:** Follow the "Next Steps" links - -## ๐ŸŽ“ Recommended Learning Order - -### **Week 1: Basics** -1. [Hello World](docs/basics/01-hello-world.md) - Your first program -2. [Variables and Data Types](docs/basics/02-variables-data-types.md) - Storing data -3. [Control Flow](docs/control-flow/01-if-expressions.md) - Making decisions -4. [Loops](docs/control-flow/03-for-loops.md) - Repeating actions - -### **Week 2: Functions & OOP** -1. [Functions Basics](docs/functions/01-functions-basics.md) - Reusable code -2. [Classes and Constructors](docs/oop/01-classes-constructors.md) - Object creation -3. [Inheritance](docs/oop/02-inheritance.md) - Code reuse -4. [Data Classes](docs/oop/06-data-classes.md) - Simple data containers - -### **Week 3: Advanced Concepts** -1. [Functional Programming](docs/functional-programming/01-lambdas.md) - Modern programming -2. [Collections](docs/collections/01-arrays.md) - Data structures -3. [Null Safety](docs/null-safety/01-null-safety.md) - Preventing errors -4. [Coroutines](docs/coroutines/01-introduction.md) - Asynchronous programming - -## ๐Ÿ› ๏ธ Prerequisites - -- Basic programming concepts (variables, functions, loops) -- No prior Kotlin experience required -- IntelliJ IDEA or Android Studio recommended - -## ๐Ÿ“ Project Structure - -``` -KotlinTutorial/ -โ”œโ”€โ”€ docs/ # ๐Ÿ“š Documentation -โ”‚ โ”œโ”€โ”€ README.md # Main documentation index -โ”‚ โ”œโ”€โ”€ basics/ # Beginner topics -โ”‚ โ”œโ”€โ”€ control-flow/ # Decision making and loops -โ”‚ โ”œโ”€โ”€ functions/ # Function concepts -โ”‚ โ”œโ”€โ”€ oop/ # Object-oriented programming -โ”‚ โ”œโ”€โ”€ functional-programming/ # Functional concepts -โ”‚ โ”œโ”€โ”€ collections/ # Data structures -โ”‚ โ”œโ”€โ”€ null-safety/ # Error prevention -โ”‚ โ””โ”€โ”€ coroutines/ # Asynchronous programming -โ”œโ”€โ”€ src/ # ๐Ÿ’ป Source code -โ”‚ โ”œโ”€โ”€ 01_hello_world.kt # Hello World example -โ”‚ โ”œโ”€โ”€ 04_variables_data_types.kt # Variables and types -โ”‚ โ”œโ”€โ”€ 17_functions_basics.kt # Functions basics -โ”‚ โ””โ”€โ”€ ... # More examples -โ””โ”€โ”€ README.md # This file -``` - -## ๐Ÿค Contributing - -Found an error or want to improve something? Contributions are welcome! - -1. Fork the repository -2. Create a feature branch -3. Make your changes -4. Submit a pull request - -## ๐Ÿ“š Additional Resources - -- [Official Kotlin Documentation](https://kotlinlang.org/docs/home.html) -- [Kotlin Playground](https://play.kotlinlang.org/) - Try code online -- [Kotlin by Example](https://play.kotlinlang.org/byExample/overview) - -## ๐Ÿ‘จโ€๐Ÿซ About - -**Original Author:** Sriyank Siddhartha -**Forked by:** [gpl-gowthamchand](https://github.com/gpl-gowthamchand) - ---- - -**๐ŸŽฏ Ready to start learning? Begin with [Hello World](docs/basics/01-hello-world.md)!** - -**Happy Learning! ๐ŸŽ‰** +# Kotlin Programming Tutorial for Beginners +Learn Kotlin Programming, its basics and Fundamentals from scratch. + +## Topics to be covered +0. Overview + - [Course introduction, prerequisites and software required](docs/basics/01-hello-world.md) +1. Installation + - Install required softwares for Windows, MAC and Linux ( Ubuntu ) +2. Getting Started with Kotlin Programming + - [Run your first app in Kotlin](docs/basics/01-hello-world.md) +3. Exploring Data Types and Variables + - [Data Types and Variables](docs/basics/02-variables-data-types.md) + - [String, Literals and String Interpolation](docs/basics/05-string-interpolation.md) + - [Comments](docs/basics/03-comments.md) +4. Constants, Variables and Data Types + - [Data Types Deep Dive](docs/basics/04-data-types.md) + - [Ranges and Sequences](docs/basics/06-ranges.md) +5. Control Flow Statements + - [IF ELSE](docs/control-flow/01-if-expressions.md) + - [IF Expressions](docs/control-flow/01-if-expressions.md) + - [WHEN Expressions](docs/control-flow/02-when-expressions.md) +6. Loop Control Statements + - What are Iterators? + - [FOR Loop and how it works](docs/control-flow/03-for-loops.md) + - [WHILE Loop](docs/control-flow/04-while-loops.md) + - [DO WHILE Loop](docs/control-flow/04-while-loops.md) + - [BREAK statements](docs/control-flow/05-break-continue.md) + - [CONTINUE keyword](docs/control-flow/05-break-continue.md) + - Labelled FOR Loop +7. Functions and Interoperability + - [Declaring functions](docs/functions/01-functions-basics.md) + - [Interoperability with Java code](docs/advanced/01-kotlin-interoperability.md) + - [Function as Expressions](docs/functions/02-functions-expressions.md) + - [Extension Functions](docs/functions/03-extension-functions.md) + - [Infix Functions](docs/functions/04-infix-functions.md) + - Default Parameters + - [Named Parameters](docs/functions/05-named-parameters.md) + - Tailrec Functions +8. Object Oriented Programming in Kotlin + - [Defining Class and creating Objects](docs/oop/01-classes-constructors.md) + - INIT block + - [Primary and Secondary Constructors](docs/oop/01-classes-constructors.md) + - Properties ( Field variables ) + - [Inheritance](docs/oop/02-inheritance.md) + - [Method and Property Overriding](docs/oop/03-method-overriding.md) + - Polymorphism + - [Abstract Class, Property and Method](docs/oop/04-abstract-classes.md) + - [Interface](docs/oop/05-interfaces.md) + - [Data Class](docs/oop/06-data-classes.md) + - Object Declaration + - [Enum class](docs/oop/07-enums-sealed-classes.md) + - [Sealed class](docs/oop/07-enums-sealed-classes.md) + - [Companion Object](docs/oop/07-enums-sealed-classes.md) +9. Functional Programming in Koltin + - [Lambdas](docs/functional-programming/01-lambdas.md) + - [Higher-Order Functions](docs/functional-programming/01-lambdas.md) + - Closures + - 'it' keyword + - ['with' function](docs/functional-programming/02-scope-functions.md) + - ['apply' function](docs/functional-programming/02-scope-functions.md) + - [let, also, run](docs/functional-programming/03-let-also-run.md) +10. Collections in Kotlin + - [Arrays](docs/collections/01-arrays.md) + - [List](docs/collections/02-lists.md) + - [Map and HashMap](docs/collections/03-maps.md) + - [Set and HashSet](docs/collections/04-sets.md) +11. Sorting and Filtering + - ["filter" function](docs/collections/05-filter-map-sorting.md) + - ["map" function](docs/collections/05-filter-map-sorting.md) + - [Predicates: all, any, find, count](docs/functional-programming/04-predicates.md) +12. Kotlin NULL Safety + - [Safe call](docs/null-safety/01-null-safety.md) + - with Let + - [Elvis](docs/null-safety/01-null-safety.md) + - [Lateinit keyword](docs/null-safety/02-lateinit-lazy.md) + - [Lazy delegation and 'lateinit' vs. 'lazy'](docs/null-safety/02-lateinit-lazy.md) +13. Scope Functions + - [with](docs/functional-programming/02-scope-functions.md) + - [apply](docs/functional-programming/02-scope-functions.md) + - [let](docs/functional-programming/03-let-also-run.md) + - [also](docs/functional-programming/03-let-also-run.md) + - [run](docs/functional-programming/03-let-also-run.md) +14. Coroutines + - [What are Coroutines? How are they related to Threads?](docs/coroutines/01-introduction.md) + - [launch, async, runBlocking](docs/coroutines/02-launch-async.md) + - [withContext, withTimeoutOrNull](docs/coroutines/04-context-dispatchers.md) + - [Suspending function](docs/coroutines/01-introduction.md) + - [Cancellation and Timeouts](docs/coroutines/03-exception-handling.md) + - [Cooperative suspending functions and isActive flag](docs/coroutines/01-introduction.md) + - [Exception Handling in Coroutines](docs/coroutines/03-exception-handling.md) + - [Sequential execution of suspending function in Coroutines](docs/coroutines/02-launch-async.md) + - [Concurrency within Coroutine](docs/coroutines/02-launch-async.md) + - [lazy 'async'](docs/coroutines/02-launch-async.md) + - [CoroutineScope and CoroutineContext](docs/coroutines/04-context-dispatchers.md) + - [Dispatchers: Confined dispatcher, Default Dispatcher, and Unconfined Dispatcher](docs/coroutines/04-context-dispatchers.md) +15. Conclusion + +## Authors + +* **Sriyank Siddhartha** From 5dc3f80072b2a6b97e0217228002c395a0057850 Mon Sep 17 00:00:00 2001 From: gpl-gowthamchand Date: 2025ๅนด8ๆœˆ25ๆ—ฅ 11:30:05 +0530 Subject: [PATCH 13/23] fix: Update broken links in documentation files to use correct GitHub URLs --- docs/basics/01-hello-world.md | 4 ++-- docs/basics/02-variables-data-types.md | 2 +- docs/basics/03-comments.md | 2 +- docs/collections/01-arrays.md | 2 +- docs/control-flow/01-if-expressions.md | 2 +- docs/control-flow/02-when-expressions.md | 2 +- docs/coroutines/01-introduction.md | 2 +- docs/functions/01-functions-basics.md | 2 +- docs/oop/01-classes-constructors.md | 2 +- 9 files changed, 10 insertions(+), 10 deletions(-) diff --git a/docs/basics/01-hello-world.md b/docs/basics/01-hello-world.md index ad2fd99..1d986d2 100644 --- a/docs/basics/01-hello-world.md +++ b/docs/basics/01-hello-world.md @@ -34,7 +34,7 @@ fun main(args: Array) { } ``` -**๐Ÿ“ File:** [01_hello_world.kt](src/01_hello_world.kt) +**๐Ÿ“ File:** [01_hello_world.kt](https://github.com/gpl-gowthamchand/KotlinTutorial/blob/feature/documentation-improvements/src/01_hello_world.kt) ## ๐Ÿ” Code Breakdown @@ -94,7 +94,7 @@ fun functionName(parameters): returnType { ### **Step 1: Open IntelliJ IDEA** 1. Launch IntelliJ IDEA 2. Open the project folder -3. Navigate to `src/01_hello_world.kt` +3. Navigate to [01_hello_world.kt](https://github.com/gpl-gowthamchand/KotlinTutorial/blob/feature/documentation-improvements/src/01_hello_world.kt) ### **Step 2: Run the Program** 1. Right-click on the file diff --git a/docs/basics/02-variables-data-types.md b/docs/basics/02-variables-data-types.md index e609428..93f0ced 100644 --- a/docs/basics/02-variables-data-types.md +++ b/docs/basics/02-variables-data-types.md @@ -59,7 +59,7 @@ fun main(args: Array) { } ``` -**๐Ÿ“ File:** [04_variables_data_types.kt](src/04_variables_data_types.kt) +**๐Ÿ“ File:** [04_variables_data_types.kt](https://github.com/gpl-gowthamchand/KotlinTutorial/blob/feature/documentation-improvements/src/04_variables_data_types.kt) ## ๐Ÿ” Code Breakdown diff --git a/docs/basics/03-comments.md b/docs/basics/03-comments.md index 90b51e8..e7bdad1 100644 --- a/docs/basics/03-comments.md +++ b/docs/basics/03-comments.md @@ -42,7 +42,7 @@ fun main(args: Array) { // This is inline comment ... } ``` -**๐Ÿ“ File:** [03_comments.kt](src/03_comments.kt) +**๐Ÿ“ File:** [03_comments.kt](https://github.com/gpl-gowthamchand/KotlinTutorial/blob/feature/documentation-improvements/src/03_comments.kt) ## ๐Ÿ” Code Breakdown diff --git a/docs/collections/01-arrays.md b/docs/collections/01-arrays.md index 95614c6..51b149a 100644 --- a/docs/collections/01-arrays.md +++ b/docs/collections/01-arrays.md @@ -29,7 +29,7 @@ By the end of this lesson, you will be able to: Let's examine the arrays example: -**๐Ÿ“ File:** [40_arrays.kt](../src/40_arrays.kt) +**๐Ÿ“ File:** [40_arrays.kt](https://github.com/gpl-gowthamchand/KotlinTutorial/blob/feature/documentation-improvements/src/40_arrays.kt) ## ๐Ÿ” Code Breakdown diff --git a/docs/control-flow/01-if-expressions.md b/docs/control-flow/01-if-expressions.md index dc26981..3c3f7d3 100644 --- a/docs/control-flow/01-if-expressions.md +++ b/docs/control-flow/01-if-expressions.md @@ -45,7 +45,7 @@ fun main(args: Array) { } ``` -**๐Ÿ“ File:** [10_if_expression.kt](src/10_if_expression.kt) +**๐Ÿ“ File:** [10_if_expression.kt](https://github.com/gpl-gowthamchand/KotlinTutorial/blob/feature/documentation-improvements/src/10_if_expression.kt) ## ๐Ÿ” Code Breakdown diff --git a/docs/control-flow/02-when-expressions.md b/docs/control-flow/02-when-expressions.md index c5eebe1..fb0853b 100644 --- a/docs/control-flow/02-when-expressions.md +++ b/docs/control-flow/02-when-expressions.md @@ -45,7 +45,7 @@ fun main(args: Array) { } ``` -**๐Ÿ“ File:** [11_when_expression.kt](src/11_when_expression.kt) +**๐Ÿ“ File:** [11_when_expression.kt](https://github.com/gpl-gowthamchand/KotlinTutorial/blob/feature/documentation-improvements/src/11_when_expression.kt) ## ๐Ÿ” Code Breakdown diff --git a/docs/coroutines/01-introduction.md b/docs/coroutines/01-introduction.md index 92e8886..e2f9e5c 100644 --- a/docs/coroutines/01-introduction.md +++ b/docs/coroutines/01-introduction.md @@ -30,7 +30,7 @@ By the end of this lesson, you will be able to: Let's examine the coroutines examples: -**๐Ÿ“ File:** [61_first_coroutine.kt](../src/61_first_coroutine.kt) +**๐Ÿ“ File:** [61_first_coroutine.kt](https://github.com/gpl-gowthamchand/KotlinTutorial/blob/feature/documentation-improvements/src/61_first_coroutine.kt) ## ๐Ÿ” Code Breakdown diff --git a/docs/functions/01-functions-basics.md b/docs/functions/01-functions-basics.md index 08e9634..695e121 100644 --- a/docs/functions/01-functions-basics.md +++ b/docs/functions/01-functions-basics.md @@ -61,7 +61,7 @@ fun add(a: Int, b: Int): Int { } ``` -**๐Ÿ“ File:** [17_functions_basics.kt](src/17_functions_basics.kt) +**๐Ÿ“ File:** [17_functions_basics.kt](https://github.com/gpl-gowthamchand/KotlinTutorial/blob/feature/documentation-improvements/src/17_functions_basics.kt) ## ๐Ÿ” Code Breakdown diff --git a/docs/oop/01-classes-constructors.md b/docs/oop/01-classes-constructors.md index ac558d2..77e4430 100644 --- a/docs/oop/01-classes-constructors.md +++ b/docs/oop/01-classes-constructors.md @@ -49,7 +49,7 @@ fun main(args: Array) { } ``` -**๐Ÿ“ File:** [26_class_and_constructor.kt](src/26_class_and_constructor.kt) +**๐Ÿ“ File:** [26_class_and_constructor.kt](https://github.com/gpl-gowthamchand/KotlinTutorial/blob/feature/documentation-improvements/src/26_class_and_constructor.kt) ## ๐Ÿ” Code Breakdown From d5fa06d9e83a6f7229ce69bc91f71ab462079215 Mon Sep 17 00:00:00 2001 From: gpl-gowthamchand Date: 2025ๅนด8ๆœˆ25ๆ—ฅ 11:31:37 +0530 Subject: [PATCH 14/23] fix: Update more broken links in documentation files to use correct GitHub URLs --- docs/advanced/01-kotlin-interoperability.md | 4 ++-- docs/collections/02-lists.md | 2 +- docs/collections/03-maps.md | 2 +- docs/collections/04-sets.md | 2 +- docs/collections/05-filter-map-sorting.md | 2 +- docs/coroutines/02-launch-async.md | 2 +- docs/coroutines/03-exception-handling.md | 2 +- docs/coroutines/04-context-dispatchers.md | 2 +- docs/functional-programming/01-lambdas.md | 2 +- docs/functional-programming/02-scope-functions.md | 2 +- docs/null-safety/01-null-safety.md | 2 +- docs/null-safety/02-lateinit-lazy.md | 2 +- 12 files changed, 13 insertions(+), 13 deletions(-) diff --git a/docs/advanced/01-kotlin-interoperability.md b/docs/advanced/01-kotlin-interoperability.md index e030ac2..f357bd8 100644 --- a/docs/advanced/01-kotlin-interoperability.md +++ b/docs/advanced/01-kotlin-interoperability.md @@ -30,8 +30,8 @@ By the end of this lesson, you will be able to: Let's examine the interoperability examples: -**๐Ÿ“ File:** [MyJavaFile.java](../src/MyJavaFile.java) -**๐Ÿ“ File:** [myKotlinInteroperability.kt](../src/myKotlinInteroperability.kt) +**๐Ÿ“ File:** [MyJavaFile.java](https://github.com/gpl-gowthamchand/KotlinTutorial/blob/feature/documentation-improvements/src/MyJavaFile.java) +**๐Ÿ“ File:** [myKotlinInteroperability.kt](https://github.com/gpl-gowthamchand/KotlinTutorial/blob/feature/documentation-improvements/src/myKotlinInteroperability.kt) ## ๐Ÿ” Code Breakdown diff --git a/docs/collections/02-lists.md b/docs/collections/02-lists.md index d287d8f..c31ac52 100644 --- a/docs/collections/02-lists.md +++ b/docs/collections/02-lists.md @@ -29,7 +29,7 @@ By the end of this lesson, you will be able to: Let's examine the lists example: -**๐Ÿ“ File:** [41_list.kt](../src/41_list.kt) +**๐Ÿ“ File:** [41_list.kt](https://github.com/gpl-gowthamchand/KotlinTutorial/blob/feature/documentation-improvements/src/41_list.kt) ## ๐Ÿ” Code Breakdown diff --git a/docs/collections/03-maps.md b/docs/collections/03-maps.md index 73e7be5..48e81a7 100644 --- a/docs/collections/03-maps.md +++ b/docs/collections/03-maps.md @@ -29,7 +29,7 @@ By the end of this lesson, you will be able to: Let's examine the maps example: -**๐Ÿ“ File:** [42_map_hashmap.kt](../src/42_map_hashmap.kt) +**๐Ÿ“ File:** [42_map_hashmap.kt](https://github.com/gpl-gowthamchand/KotlinTutorial/blob/feature/documentation-improvements/src/42_map_hashmap.kt) ## ๐Ÿ” Code Breakdown diff --git a/docs/collections/04-sets.md b/docs/collections/04-sets.md index 0df6da8..b00c462 100644 --- a/docs/collections/04-sets.md +++ b/docs/collections/04-sets.md @@ -29,7 +29,7 @@ By the end of this lesson, you will be able to: Let's examine the sets example: -**๐Ÿ“ File:** [43_set_hashset.kt](../src/43_set_hashset.kt) +**๐Ÿ“ File:** [43_set_hashset.kt](https://github.com/gpl-gowthamchand/KotlinTutorial/blob/feature/documentation-improvements/src/43_set_hashset.kt) ## ๐Ÿ” Code Breakdown diff --git a/docs/collections/05-filter-map-sorting.md b/docs/collections/05-filter-map-sorting.md index 114324a..578574f 100644 --- a/docs/collections/05-filter-map-sorting.md +++ b/docs/collections/05-filter-map-sorting.md @@ -29,7 +29,7 @@ By the end of this lesson, you will be able to: Let's examine the functional operations examples: -**๐Ÿ“ File:** [44_filter_map_sorting.kt](../src/44_filter_map_sorting.kt) +**๐Ÿ“ File:** [44_filter_map_sorting.kt](https://github.com/gpl-gowthamchand/KotlinTutorial/blob/feature/documentation-improvements/src/44_filter_map_sorting.kt) ## ๐Ÿ” Code Breakdown diff --git a/docs/coroutines/02-launch-async.md b/docs/coroutines/02-launch-async.md index 7e84482..0eaf5b6 100644 --- a/docs/coroutines/02-launch-async.md +++ b/docs/coroutines/02-launch-async.md @@ -29,7 +29,7 @@ By the end of this lesson, you will be able to: Let's examine the launch and async examples: -**๐Ÿ“ File:** [64_launch_coroutine_builder.kt](../src/64_launch_coroutine_builder.kt) +**๐Ÿ“ File:** [64_launch_coroutine_builder.kt](https://github.com/gpl-gowthamchand/KotlinTutorial/blob/feature/documentation-improvements/src/64_launch_coroutine_builder.kt) ## ๐Ÿ” Code Breakdown diff --git a/docs/coroutines/03-exception-handling.md b/docs/coroutines/03-exception-handling.md index f88a32a..1ba9c27 100644 --- a/docs/coroutines/03-exception-handling.md +++ b/docs/coroutines/03-exception-handling.md @@ -29,7 +29,7 @@ By the end of this lesson, you will be able to: Let's examine the exception handling examples: -**๐Ÿ“ File:** [70_exception_handling.kt](../src/70_exception_handling.kt) +**๐Ÿ“ File:** [70_exception_handling.kt](https://github.com/gpl-gowthamchand/KotlinTutorial/blob/feature/documentation-improvements/src/70_exception_handling.kt) ## ๐Ÿ” Code Breakdown diff --git a/docs/coroutines/04-context-dispatchers.md b/docs/coroutines/04-context-dispatchers.md index 92d2dcf..7b357b1 100644 --- a/docs/coroutines/04-context-dispatchers.md +++ b/docs/coroutines/04-context-dispatchers.md @@ -29,7 +29,7 @@ By the end of this lesson, you will be able to: Let's examine the context and dispatcher examples: -**๐Ÿ“ File:** [78_CoroutineContext_and_Dispatchers.kt](../src/78_CoroutineContext_and_Dispatchers.kt) +**๐Ÿ“ File:** [78_CoroutineContext_and_Dispatchers.kt](https://github.com/gpl-gowthamchand/KotlinTutorial/blob/feature/documentation-improvements/src/78_CoroutineContext_and_Dispatchers.kt) ## ๐Ÿ” Code Breakdown diff --git a/docs/functional-programming/01-lambdas.md b/docs/functional-programming/01-lambdas.md index 76fa783..fcbe31c 100644 --- a/docs/functional-programming/01-lambdas.md +++ b/docs/functional-programming/01-lambdas.md @@ -29,7 +29,7 @@ By the end of this lesson, you will be able to: Let's examine the lambdas and higher-order functions examples: -**๐Ÿ“ File:** [35_lambdas_higher_order_functions.kt](../src/35_lambdas_higher_order_functions.kt) +**๐Ÿ“ File:** [35_lambdas_higher_order_functions.kt](https://github.com/gpl-gowthamchand/KotlinTutorial/blob/feature/documentation-improvements/src/35_lambdas_higher_order_functions.kt) ## ๐Ÿ” Code Breakdown diff --git a/docs/functional-programming/02-scope-functions.md b/docs/functional-programming/02-scope-functions.md index 57738f0..978d011 100644 --- a/docs/functional-programming/02-scope-functions.md +++ b/docs/functional-programming/02-scope-functions.md @@ -29,7 +29,7 @@ By the end of this lesson, you will be able to: Let's examine the scope functions examples: -**๐Ÿ“ File:** [39_with_apply_functions.kt](../src/39_with_apply_functions.kt) +**๐Ÿ“ File:** [39_with_apply_functions.kt](https://github.com/gpl-gowthamchand/KotlinTutorial/blob/feature/documentation-improvements/src/39_with_apply_functions.kt) ## ๐Ÿ” Code Breakdown diff --git a/docs/null-safety/01-null-safety.md b/docs/null-safety/01-null-safety.md index 8f244a7..f61dda7 100644 --- a/docs/null-safety/01-null-safety.md +++ b/docs/null-safety/01-null-safety.md @@ -29,7 +29,7 @@ By the end of this lesson, you will be able to: Let's examine the null safety examples: -**๐Ÿ“ File:** [46_null_safety.kt](../src/46_null_safety.kt) +**๐Ÿ“ File:** [46_null_safety.kt](https://github.com/gpl-gowthamchand/KotlinTutorial/blob/feature/documentation-improvements/src/46_null_safety.kt) ## ๐Ÿ” Code Breakdown diff --git a/docs/null-safety/02-lateinit-lazy.md b/docs/null-safety/02-lateinit-lazy.md index 936f61b..3bdfccf 100644 --- a/docs/null-safety/02-lateinit-lazy.md +++ b/docs/null-safety/02-lateinit-lazy.md @@ -29,7 +29,7 @@ By the end of this lesson, you will be able to: Let's examine the lateinit and lazy examples: -**๐Ÿ“ File:** [47_lateinit_keyword.kt](../src/47_lateinit_keyword.kt) +**๐Ÿ“ File:** [47_lateinit_keyword.kt](https://github.com/gpl-gowthamchand/KotlinTutorial/blob/feature/documentation-improvements/src/47_lateinit_keyword.kt) ## ๐Ÿ” Code Breakdown From ad203e6b2fbe87a04f225f3aafd6a7598711c737 Mon Sep 17 00:00:00 2001 From: gpl-gowthamchand Date: 2025ๅนด8ๆœˆ25ๆ—ฅ 11:34:47 +0530 Subject: [PATCH 15/23] fix: Update remaining broken links in documentation files to use correct GitHub URLs --- docs/basics/04-data-types.md | 2 +- docs/basics/05-string-interpolation.md | 2 +- docs/control-flow/03-for-loops.md | 2 +- docs/control-flow/04-while-loops.md | 2 +- docs/control-flow/05-break-continue.md | 2 +- docs/functional-programming/03-let-also-run.md | 6 +++--- docs/functional-programming/04-predicates.md | 2 +- docs/functions/02-functions-as-expressions.md | 2 +- docs/functions/03-named-parameters.md | 2 +- docs/functions/04-extension-functions.md | 4 ++-- docs/oop/02-inheritance.md | 2 +- docs/oop/03-method-overriding.md | 2 +- docs/oop/04-abstract-classes.md | 2 +- 13 files changed, 16 insertions(+), 16 deletions(-) diff --git a/docs/basics/04-data-types.md b/docs/basics/04-data-types.md index c35fd70..c181042 100644 --- a/docs/basics/04-data-types.md +++ b/docs/basics/04-data-types.md @@ -45,7 +45,7 @@ fun main(args: Array) { } ``` -**๐Ÿ“ File:** [07_data_types.kt](src/07_data_types.kt) +**๐Ÿ“ File:** [07_data_types.kt](https://github.com/gpl-gowthamchand/KotlinTutorial/blob/feature/documentation-improvements/src/07_data_types.kt) ## ๐Ÿ” Code Breakdown diff --git a/docs/basics/05-string-interpolation.md b/docs/basics/05-string-interpolation.md index 3f83310..290a4de 100644 --- a/docs/basics/05-string-interpolation.md +++ b/docs/basics/05-string-interpolation.md @@ -43,7 +43,7 @@ class Rectangle { } ``` -**๐Ÿ“ File:** [08_string_interpolation.kt](src/08_string_interpolation.kt) +**๐Ÿ“ File:** [08_string_interpolation.kt](https://github.com/gpl-gowthamchand/KotlinTutorial/blob/feature/documentation-improvements/src/08_string_interpolation.kt) ## ๐Ÿ” Code Breakdown diff --git a/docs/control-flow/03-for-loops.md b/docs/control-flow/03-for-loops.md index 8617e77..cf2f603 100644 --- a/docs/control-flow/03-for-loops.md +++ b/docs/control-flow/03-for-loops.md @@ -46,7 +46,7 @@ fun main(args: Array) { } ``` -**๐Ÿ“ File:** [12_for_loop.kt](src/12_for_loop.kt) +**๐Ÿ“ File:** [12_for_loop.kt](https://github.com/gpl-gowthamchand/KotlinTutorial/blob/feature/documentation-improvements/src/12_for_loop.kt) ## ๐Ÿ” Code Breakdown diff --git a/docs/control-flow/04-while-loops.md b/docs/control-flow/04-while-loops.md index b3161e7..877b3da 100644 --- a/docs/control-flow/04-while-loops.md +++ b/docs/control-flow/04-while-loops.md @@ -41,7 +41,7 @@ fun main(args: Array) { } ``` -**๐Ÿ“ File:** [13_while_loop.kt](src/13_while_loop.kt) +**๐Ÿ“ File:** [13_while_loop.kt](https://github.com/gpl-gowthamchand/KotlinTutorial/blob/feature/documentation-improvements/src/13_while_loop.kt) ## ๐Ÿ” Code Breakdown diff --git a/docs/control-flow/05-break-continue.md b/docs/control-flow/05-break-continue.md index 9fad418..639560e 100644 --- a/docs/control-flow/05-break-continue.md +++ b/docs/control-flow/05-break-continue.md @@ -41,7 +41,7 @@ fun main(args: Array) { } ``` -**๐Ÿ“ File:** [15_break_keyword.kt](src/15_break_keyword.kt) +**๐Ÿ“ File:** [15_break_keyword.kt](https://github.com/gpl-gowthamchand/KotlinTutorial/blob/feature/documentation-improvements/src/15_break_keyword.kt) ## ๐Ÿ” Code Breakdown diff --git a/docs/functional-programming/03-let-also-run.md b/docs/functional-programming/03-let-also-run.md index b88d220..0996100 100644 --- a/docs/functional-programming/03-let-also-run.md +++ b/docs/functional-programming/03-let-also-run.md @@ -29,9 +29,9 @@ By the end of this lesson, you will be able to: Let's examine the scope function examples: -**๐Ÿ“ File:** [52_let_scope_function.kt](../src/52_let_scope_function.kt) -**๐Ÿ“ File:** [53_run_scope_function.kt](../src/53_run_scope_function.kt) -**๐Ÿ“ File:** [51_also_scope_function.kt](../src/51_also_scope_function.kt) +**๐Ÿ“ File:** [52_let_scope_function.kt](https://github.com/gpl-gowthamchand/KotlinTutorial/blob/feature/documentation-improvements/src/52_let_scope_function.kt) +**๐Ÿ“ File:** [53_run_scope_function.kt](https://github.com/gpl-gowthamchand/KotlinTutorial/blob/feature/documentation-improvements/src/53_run_scope_function.kt) +**๐Ÿ“ File:** [51_also_scope_function.kt](https://github.com/gpl-gowthamchand/KotlinTutorial/blob/feature/documentation-improvements/src/51_also_scope_function.kt) ## ๐Ÿ” Code Breakdown diff --git a/docs/functional-programming/04-predicates.md b/docs/functional-programming/04-predicates.md index b5665e1..727bb2c 100644 --- a/docs/functional-programming/04-predicates.md +++ b/docs/functional-programming/04-predicates.md @@ -29,7 +29,7 @@ By the end of this lesson, you will be able to: Let's examine the predicate examples: -**๐Ÿ“ File:** [45_predicate.kt](../src/45_predicate.kt) +**๐Ÿ“ File:** [45_predicate.kt](https://github.com/gpl-gowthamchand/KotlinTutorial/blob/feature/documentation-improvements/src/45_predicate.kt) ## ๐Ÿ” Code Breakdown diff --git a/docs/functions/02-functions-as-expressions.md b/docs/functions/02-functions-as-expressions.md index 3869313..11f05ad 100644 --- a/docs/functions/02-functions-as-expressions.md +++ b/docs/functions/02-functions-as-expressions.md @@ -39,7 +39,7 @@ fun max(a: Int, b: Int): Int = if (a> b) { } ``` -**๐Ÿ“ File:** [18_functions_as_expressions.kt](src/18_functions_as_expressions.kt) +**๐Ÿ“ File:** [18_functions_as_expressions.kt](https://github.com/gpl-gowthamchand/KotlinTutorial/blob/feature/documentation-improvements/src/18_functions_as_expressions.kt) ## ๐Ÿ” Code Breakdown diff --git a/docs/functions/03-named-parameters.md b/docs/functions/03-named-parameters.md index 1303a1b..375f8c0 100644 --- a/docs/functions/03-named-parameters.md +++ b/docs/functions/03-named-parameters.md @@ -40,7 +40,7 @@ fun main(args: Array) { } ``` -**๐Ÿ“ File:** [21_named_parameters.kt](src/21_named_parameters.kt) +**๐Ÿ“ File:** [21_named_parameters.kt](https://github.com/gpl-gowthamchand/KotlinTutorial/blob/feature/documentation-improvements/src/21_named_parameters.kt) ## ๐Ÿ” Code Breakdown diff --git a/docs/functions/04-extension-functions.md b/docs/functions/04-extension-functions.md index abaca2c..e115009 100644 --- a/docs/functions/04-extension-functions.md +++ b/docs/functions/04-extension-functions.md @@ -55,8 +55,8 @@ fun Int.greaterValue(other: Int): Int { ``` **๐Ÿ“ Files:** -- [22_extension_function_one.kt](src/22_extension_function_one.kt) -- [23_extension_function_two.kt](src/23_extension_function_two.kt) +- [22_extension_function_one.kt](https://github.com/gpl-gowthamchand/KotlinTutorial/blob/feature/documentation-improvements/src/22_extension_function_one.kt) +- [23_extension_function_two.kt](https://github.com/gpl-gowthamchand/KotlinTutorial/blob/feature/documentation-improvements/src/23_extension_function_two.kt) ## ๐Ÿ” Code Breakdown diff --git a/docs/oop/02-inheritance.md b/docs/oop/02-inheritance.md index 0eabdc2..704a257 100644 --- a/docs/oop/02-inheritance.md +++ b/docs/oop/02-inheritance.md @@ -73,7 +73,7 @@ fun main(args: Array) { } ``` -**๐Ÿ“ File:** [27_inheritance.kt](src/27_inheritance.kt) +**๐Ÿ“ File:** [27_inheritance.kt](https://github.com/gpl-gowthamchand/KotlinTutorial/blob/feature/documentation-improvements/src/27_inheritance.kt) ## ๐Ÿ” Code Breakdown diff --git a/docs/oop/03-method-overriding.md b/docs/oop/03-method-overriding.md index 3702675..348bb10 100644 --- a/docs/oop/03-method-overriding.md +++ b/docs/oop/03-method-overriding.md @@ -60,7 +60,7 @@ fun main(args: Array) { } ``` -**๐Ÿ“ File:** [28_overriding_methods_properties.kt](src/28_overriding_methods_properties.kt) +**๐Ÿ“ File:** [28_overriding_methods_properties.kt](https://github.com/gpl-gowthamchand/KotlinTutorial/blob/feature/documentation-improvements/src/28_overriding_methods_properties.kt) ## ๐Ÿ” Code Breakdown diff --git a/docs/oop/04-abstract-classes.md b/docs/oop/04-abstract-classes.md index 9aac335..0049686 100644 --- a/docs/oop/04-abstract-classes.md +++ b/docs/oop/04-abstract-classes.md @@ -58,7 +58,7 @@ fun main(args: Array) { } ``` -**๐Ÿ“ File:** [30_abstract_class.kt](src/30_abstract_class.kt) +**๐Ÿ“ File:** [30_abstract_class.kt](https://github.com/gpl-gowthamchand/KotlinTutorial/blob/feature/documentation-improvements/src/30_abstract_class.kt) ## ๐Ÿ” Code Breakdown From 39d464a2edbfd7b9baebc344455615190d988e8d Mon Sep 17 00:00:00 2001 From: gpl-gowthamchand Date: 2025ๅนด8ๆœˆ25ๆ—ฅ 11:53:26 +0530 Subject: [PATCH 16/23] feat: Add missing documentation files for Infix Functions, Default Parameters, Tailrec Functions, and INIT Block --- docs/functions/05-infix-functions.md | 260 +++++++++++++ docs/functions/06-default-parameters.md | 325 +++++++++++++++++ docs/functions/07-tailrec-functions.md | 318 ++++++++++++++++ docs/oop/05-init-block.md | 463 ++++++++++++++++++++++++ 4 files changed, 1366 insertions(+) create mode 100644 docs/functions/05-infix-functions.md create mode 100644 docs/functions/06-default-parameters.md create mode 100644 docs/functions/07-tailrec-functions.md create mode 100644 docs/oop/05-init-block.md diff --git a/docs/functions/05-infix-functions.md b/docs/functions/05-infix-functions.md new file mode 100644 index 0000000..0de8b75 --- /dev/null +++ b/docs/functions/05-infix-functions.md @@ -0,0 +1,260 @@ +# ๐Ÿ”— Infix Functions in Kotlin + +Infix functions are a special type of function in Kotlin that allows you to call functions using a more natural, operator-like syntax. They make your code more readable and expressive. + +## ๐Ÿ“‹ Learning Objectives + +By the end of this lesson, you will be able to: +- โœ… Understand what infix functions are +- โœ… Create your own infix functions +- โœ… Use infix functions effectively +- โœ… Know the limitations and best practices +- โœ… Apply infix functions in real-world scenarios + +## ๐Ÿ” What You'll Learn + +- **Infix function syntax** - How to declare and use infix functions +- **Operator-like calls** - Calling functions without dots and parentheses +- **Use cases** - When and where to use infix functions +- **Limitations** - Understanding the constraints of infix functions +- **Best practices** - Guidelines for effective usage + +## ๐Ÿ“ Prerequisites + +- โœ… Basic understanding of functions in Kotlin +- โœ… Knowledge of function parameters and return types +- โœ… Familiarity with Kotlin syntax + +## ๐Ÿ’ป The Code + +Let's examine the infix functions examples: + +**๐Ÿ“ File:** [24_infix_function.kt](https://github.com/gpl-gowthamchand/KotlinTutorial/blob/feature/documentation-improvements/src/24_infix_function.kt) + +## ๐Ÿ” Code Breakdown + +### **Basic Infix Function Declaration** +```kotlin +infix fun Int.times(str: String) = str.repeat(this) +``` + +**Key Components:** +- `infix` - Keyword that makes the function infix +- `fun` - Function declaration keyword +- `Int.times` - Extension function on Int type +- `str: String` - Parameter of type String +- `str.repeat(this)` - Function body using the receiver object + +### **Infix Function Call** +```kotlin +// Regular function call +3.times("Hello ") + +// Infix function call (more natural) +3 times "Hello " +``` + +## ๐ŸŽฏ Key Concepts Explained + +### **1. What Makes a Function Infix?** + +An infix function must: +- Be a **member function** or an **extension function** +- Have **exactly one parameter** +- Be marked with the `infix` keyword +- Not have default parameter values +- Not be a vararg function + +### **2. Infix vs Regular Function Calls** + +| Regular Call | Infix Call | Description | +|--------------|------------|-------------| +| `a.plus(b)` | `a plus b` | Addition operation | +| `str.repeat(n)` | `str repeat n` | String repetition | +| `list.add(item)` | `list add item` | List addition | + +### **3. Common Use Cases** + +#### **Mathematical Operations** +```kotlin +infix fun Int.pow(exponent: Int): Int { + return this.toDouble().pow(exponent.toDouble()).toInt() +} + +// Usage +val result = 2 pow 3 // Returns 8 +``` + +#### **String Operations** +```kotlin +infix fun String.repeat(times: Int): String { + return this.repeat(times) +} + +// Usage +val repeated = "Hello " repeat 3 // Returns "Hello Hello Hello " +``` + +#### **Collection Operations** +```kotlin +infix fun MutableList.add(item: T) { + this.add(item) +} + +// Usage +val list = mutableListOf() +list add "First" +list add "Second" +``` + +## ๐Ÿงช Examples and Exercises + +### **Example 1: Custom Infix Function** +```kotlin +infix fun String.containsIgnoreCase(other: String): Boolean { + return this.contains(other, ignoreCase = true) +} + +// Usage +val text = "Hello World" +val contains = text containsIgnoreCase "world" // true +``` + +### **Example 2: Range Creation** +```kotlin +infix fun Int.until(end: Int): IntRange { + return this until end +} + +// Usage +val range = 1 until 5 // Creates range 1..4 +``` + +### **Example 3: Pair Creation** +```kotlin +infix fun A.to(that: B): Pair = Pair(this, that) + +// Usage +val pair = "key" to "value" // Creates Pair("key", "value") +``` + +## โš ๏ธ Limitations and Considerations + +### **1. Parameter Constraints** +```kotlin +// โŒ Won't work - multiple parameters +infix fun Int.operation(a: Int, b: Int) = this + a + b + +// โŒ Won't work - default parameter +infix fun Int.operation(a: Int = 0) = this + a + +// โŒ Won't work - vararg parameter +infix fun Int.operation(vararg items: Int) = this + items.sum() + +// โœ… Will work - single parameter, no defaults +infix fun Int.operation(a: Int) = this + a +``` + +### **2. Precedence and Associativity** +```kotlin +infix fun Int.add(other: Int) = this + other +infix fun Int.multiply(other: Int) = this * other + +// Be careful with operator precedence +val result = 2 add 3 multiply 4 // Result: 14 (not 20) +// Equivalent to: 2.add(3.multiply(4)) +``` + +## ๐ŸŽฏ Best Practices + +### **1. Use for Simple Operations** +```kotlin +// โœ… Good - simple, clear operation +infix fun String.append(other: String) = this + other + +// โŒ Avoid - complex logic in infix +infix fun String.processComplex(other: String): String { + // Complex processing logic... + return processedResult +} +``` + +### **2. Maintain Readability** +```kotlin +// โœ… Good - clear meaning +infix fun Int.isDivisibleBy(other: Int) = this % other == 0 + +// โŒ Avoid - unclear meaning +infix fun Int.x(other: Int) = this * other +``` + +### **3. Follow Kotlin Conventions** +```kotlin +// โœ… Good - follows Kotlin naming +infix fun Int.times(str: String) = str.repeat(this) + +// โŒ Avoid - non-Kotlin style +infix fun Int.multiplyString(str: String) = str.repeat(this) +``` + +## ๐Ÿ”ง Practical Applications + +### **1. DSL (Domain Specific Language)** +```kotlin +class Configuration { + infix fun set(key: String, value: Any) { + // Set configuration value + } +} + +val config = Configuration() +config set ("timeout", 5000) +``` + +### **2. Testing Frameworks** +```kotlin +infix fun String.should(condition: String) { + // Assertion logic +} + +"Hello" should "contain 'H'" +``` + +### **3. Custom Operators** +```kotlin +infix fun Int.`+`(other: Int) = this + other * 2 + +val result = 5 `+` 3 // Returns 11 (5 + 3*2) +``` + +## ๐Ÿ“š Summary + +**Infix functions** are a powerful feature in Kotlin that: +- Make function calls more natural and readable +- Are useful for simple, single-parameter operations +- Follow specific rules and limitations +- Can improve code expressiveness when used appropriately + +## ๐ŸŽฏ Key Takeaways + +1. **Use `infix` keyword** for functions that benefit from operator-like syntax +2. **Single parameter only** - infix functions can't have multiple parameters +3. **Extension functions** work great with infix notation +4. **Maintain readability** - don't overuse infix functions +5. **Follow conventions** - use clear, descriptive names + +## ๐Ÿš€ Next Steps + +- Practice creating your own infix functions +- Explore the standard library infix functions +- Learn about operator overloading +- Understand function precedence and associativity + +--- + +**๐Ÿ“ Source Code:** [24_infix_function.kt](https://github.com/gpl-gowthamchand/KotlinTutorial/blob/feature/documentation-improvements/src/24_infix_function.kt) + +**๐Ÿ”— Related Topics:** +- [Functions Basics](../functions/01-functions-basics.md) +- [Extension Functions](../functions/04-extension-functions.md) +- [Function Expressions](../functions/02-functions-as-expressions.md) diff --git a/docs/functions/06-default-parameters.md b/docs/functions/06-default-parameters.md new file mode 100644 index 0000000..bac8191 --- /dev/null +++ b/docs/functions/06-default-parameters.md @@ -0,0 +1,325 @@ +# ๐ŸŽฏ Default Parameters in Kotlin + +Default parameters in Kotlin allow you to specify default values for function parameters, making your functions more flexible and reducing the need for multiple overloaded versions. + +## ๐Ÿ“‹ Learning Objectives + +By the end of this lesson, you will be able to: +- โœ… Understand how default parameters work +- โœ… Create functions with default parameters +- โœ… Use default parameters effectively +- โœ… Know when and how to use them +- โœ… Apply default parameters in real-world scenarios + +## ๐Ÿ” What You'll Learn + +- **Default parameter syntax** - How to declare parameters with default values +- **Function overloading** - How default parameters reduce the need for multiple functions +- **Parameter order** - Understanding the importance of parameter positioning +- **Use cases** - When and where to use default parameters +- **Best practices** - Guidelines for effective usage + +## ๐Ÿ“ Prerequisites + +- โœ… Basic understanding of functions in Kotlin +- โœ… Knowledge of function parameters and return types +- โœ… Familiarity with Kotlin syntax + +## ๐Ÿ’ป The Code + +Let's examine the default parameters examples: + +**๐Ÿ“ File:** [10_default_functions.kt](https://github.com/gpl-gowthamchand/KotlinTutorial/blob/feature/documentation-improvements/src/10_default_functions.kt) + +## ๐Ÿ” Code Breakdown + +### **Basic Default Parameter Declaration** +```kotlin +fun greet(name: String, greeting: String = "Hello") { + println("$greeting, $name!") +} +``` + +**Key Components:** +- `fun greet` - Function declaration +- `name: String` - Required parameter (no default) +- `greeting: String = "Hello"` - Parameter with default value +- `"Hello"` - Default value for the greeting parameter + +### **Function Calls with Default Parameters** +```kotlin +// Using default parameter +greet("John") // Prints: Hello, John! + +// Overriding default parameter +greet("John", "Good morning") // Prints: Good morning, John! +``` + +## ๐ŸŽฏ Key Concepts Explained + +### **1. How Default Parameters Work** + +Default parameters allow you to: +- **Skip parameters** when calling functions +- **Reduce function overloads** - one function instead of many +- **Provide sensible defaults** for common use cases +- **Maintain backward compatibility** when adding new parameters + +### **2. Parameter Order and Defaults** + +```kotlin +fun createUser( + name: String, + email: String = "", + age: Int = 18, + isActive: Boolean = true +) { + // Function implementation +} + +// Valid calls +createUser("John") // Uses all defaults +createUser("John", "john@email.com") // Overrides email, uses defaults for age and isActive +createUser("John", "john@email.com", 25) // Overrides email and age, uses default for isActive +createUser("John", "john@email.com", 25, false) // Overrides all parameters +``` + +### **3. Named Parameters with Defaults** + +```kotlin +fun createUser( + name: String, + email: String = "", + age: Int = 18, + isActive: Boolean = true +) { + // Function implementation +} + +// Using named parameters to skip some defaults +createUser( + name = "John", + age = 25, + isActive = false + // email will use default value "" +) +``` + +## ๐Ÿงช Examples and Exercises + +### **Example 1: String Formatting Function** +```kotlin +fun formatText( + text: String, + prefix: String = "", + suffix: String = "", + maxLength: Int = 100 +): String { + val formatted = "$prefix$text$suffix" + return if (formatted.length <= maxLength) formatted else formatted.take(maxLength) + "..." +} + +// Usage +val result1 = formatText("Hello World") // Uses all defaults +val result2 = formatText("Hello World", ">>> ", " <<<") // Overrides prefix and suffix +val result3 = formatText("Very long text that exceeds the maximum length", maxLength = 20) // Named parameter +``` + +### **Example 2: Configuration Function** +```kotlin +fun configureDatabase( + host: String = "localhost", + port: Int = 5432, + username: String = "admin", + password: String = "", + database: String = "mydb" +) { + println("Connecting to $host:$port/$database as $username") +} + +// Usage +configureDatabase() // Uses all defaults +configureDatabase(host = "192.168.1.100", password = "secret123") // Named parameters +``` + +### **Example 3: Collection Processing** +```kotlin +fun processList( + items: List, + separator: String = ", ", + prefix: String = "[", + suffix: String = "]", + maxItems: Int = 10 +): String { + val limitedItems = items.take(maxItems) + return "$prefix${limitedItems.joinToString(separator)}$suffix" +} + +// Usage +val list = listOf("apple", "banana", "cherry", "date", "elderberry") +val result1 = processList(list) // [apple, banana, cherry, date, elderberry] +val result2 = processList(list, separator = " | ", maxItems = 3) // [apple | banana | cherry] +``` + +## โš ๏ธ Important Considerations + +### **1. Parameter Order Matters** +```kotlin +// โŒ Won't work - can't skip required parameters +fun greet(greeting: String = "Hello", name: String) { + println("$greeting, $name!") +} + +// โŒ This call will fail +greet("John") // Error: missing required parameter 'name' + +// โœ… Correct order - required parameters first +fun greet(name: String, greeting: String = "Hello") { + println("$greeting, $name!") +} + +// โœ… This call works +greet("John") // Prints: Hello, John! +``` + +### **2. Default Values Are Evaluated Once** +```kotlin +fun createId(prefix: String = "ID_", timestamp: Long = System.currentTimeMillis()): String { + return "$prefix$timestamp" +} + +// Both calls will have different timestamps +val id1 = createId() // ID_1234567890 +val id2 = createId() // ID_1234567891 +``` + +### **3. Mutable Default Parameters** +```kotlin +// โš ๏ธ Be careful with mutable default parameters +fun addToList(item: String, list: MutableList = mutableListOf()): MutableList { + list.add(item) + return list +} + +// This can lead to unexpected behavior +val list1 = addToList("first") +val list2 = addToList("second") +// list1 and list2 might reference the same list! +``` + +## ๐ŸŽฏ Best Practices + +### **1. Put Required Parameters First** +```kotlin +// โœ… Good - required parameters first +fun sendEmail(to: String, subject: String, body: String = "", cc: List = emptyList()) + +// โŒ Avoid - required parameters after defaults +fun sendEmail(subject: String = "", body: String = "", to: String, cc: List = emptyList()) +``` + +### **2. Use Sensible Defaults** +```kotlin +// โœ… Good - sensible defaults +fun createFile(name: String, extension: String = "txt", overwrite: Boolean = false) + +// โŒ Avoid - confusing defaults +fun createFile(name: String, extension: String = "xyz", overwrite: Boolean = true) +``` + +### **3. Document Complex Defaults** +```kotlin +/** + * Creates a user with default values + * @param name User's full name (required) + * @param email User's email address (defaults to empty string) + * @param age User's age (defaults to 18) + * @param isActive Whether the user account is active (defaults to true) + */ +fun createUser( + name: String, + email: String = "", + age: Int = 18, + isActive: Boolean = true +) { + // Implementation +} +``` + +## ๐Ÿ”ง Practical Applications + +### **1. Builder Pattern Alternative** +```kotlin +// Instead of builder pattern +fun createPerson( + name: String, + age: Int = 0, + city: String = "", + occupation: String = "", + hobbies: List = emptyList() +) = Person(name, age, city, occupation, hobbies) + +// Usage +val person = createPerson( + name = "John Doe", + age = 30, + city = "New York" + // occupation and hobbies use defaults +) +``` + +### **2. Configuration Functions** +```kotlin +fun configureLogger( + level: String = "INFO", + format: String = "JSON", + output: String = "console", + maxSize: Int = 100 +) { + // Logger configuration logic +} +``` + +### **3. Utility Functions** +```kotlin +fun formatCurrency( + amount: Double, + currency: String = "USD", + locale: String = "en_US", + decimalPlaces: Int = 2 +): String { + // Currency formatting logic +} +``` + +## ๐Ÿ“š Summary + +**Default parameters** in Kotlin provide: +- **Flexibility** in function calls +- **Reduced code duplication** through fewer overloaded functions +- **Better readability** with sensible defaults +- **Maintainability** when adding new parameters + +## ๐ŸŽฏ Key Takeaways + +1. **Use `parameter = value`** syntax for default parameters +2. **Required parameters first** - put parameters with defaults after required ones +3. **Named parameters** help when skipping some defaults +4. **Sensible defaults** make functions easier to use +5. **Document complex defaults** for better understanding + +## ๐Ÿš€ Next Steps + +- Practice creating functions with default parameters +- Learn about function overloading +- Explore named parameters in detail +- Understand parameter order and its importance + +--- + +**๐Ÿ“ Source Code:** [10_default_functions.kt](https://github.com/gpl-gowthamchand/KotlinTutorial/blob/feature/documentation-improvements/src/10_default_functions.kt) + +**๐Ÿ”— Related Topics:** +- [Functions Basics](../functions/01-functions-basics.md) +- [Named Parameters](../functions/03-named-parameters.md) +- [Function Expressions](../functions/02-functions-as-expressions.md) diff --git a/docs/functions/07-tailrec-functions.md b/docs/functions/07-tailrec-functions.md new file mode 100644 index 0000000..2a95319 --- /dev/null +++ b/docs/functions/07-tailrec-functions.md @@ -0,0 +1,318 @@ +# ๐Ÿ”„ Tailrec Functions in Kotlin + +Tailrec functions in Kotlin are a powerful optimization feature that converts recursive functions into efficient loops, preventing stack overflow errors and improving performance. + +## ๐Ÿ“‹ Learning Objectives + +By the end of this lesson, you will be able to: +- โœ… Understand what tailrec functions are +- โœ… Create tailrec functions correctly +- โœ… Know when to use tailrec optimization +- โœ… Understand the limitations and requirements +- โœ… Apply tailrec functions in real-world scenarios + +## ๐Ÿ” What You'll Learn + +- **Tailrec function syntax** - How to declare and use tailrec functions +- **Tail recursion** - Understanding what makes a function tail recursive +- **Optimization benefits** - How tailrec prevents stack overflow +- **Use cases** - When and where to use tailrec functions +- **Best practices** - Guidelines for effective usage + +## ๐Ÿ“ Prerequisites + +- โœ… Basic understanding of functions in Kotlin +- โœ… Knowledge of recursion concepts +- โœ… Familiarity with Kotlin syntax +- โœ… Understanding of stack and memory concepts + +## ๐Ÿ’ป The Code + +Let's examine the tailrec functions examples: + +**๐Ÿ“ File:** [25_tailrec_function.kt](https://github.com/gpl-gowthamchand/KotlinTutorial/blob/feature/documentation-improvements/src/25_tailrec_function.kt) + +## ๐Ÿ” Code Breakdown + +### **Basic Tailrec Function Declaration** +```kotlin +tailrec fun factorial(n: Int, accumulator: Int = 1): Int { + return if (n <= 1) accumulator else factorial(n - 1, n * accumulator) +} +``` + +**Key Components:** +- `tailrec` - Keyword that enables tail recursion optimization +- `fun factorial` - Function declaration +- `n: Int, accumulator: Int = 1` - Parameters with default value +- `factorial(n - 1, n * accumulator)` - Tail recursive call + +### **Tailrec vs Regular Recursion** +```kotlin +// โŒ Regular recursion (can cause stack overflow) +fun factorialRegular(n: Int): Int { + return if (n <= 1) 1 else n * factorialRegular(n - 1) +} + +// โœ… Tailrec function (optimized to loop) +tailrec fun factorialTailrec(n: Int, accumulator: Int = 1): Int { + return if (n <= 1) accumulator else factorialTailrec(n - 1, n * accumulator) +} +``` + +## ๐ŸŽฏ Key Concepts Explained + +### **1. What is Tail Recursion?** + +A function is **tail recursive** when: +- The recursive call is the **last operation** in the function +- No additional computation is performed after the recursive call +- The result of the recursive call is returned directly + +### **2. How Tailrec Optimization Works** + +The Kotlin compiler converts tailrec functions into loops: +- **Eliminates stack frames** for each recursive call +- **Prevents stack overflow** errors +- **Improves performance** by reducing memory usage +- **Maintains readability** of recursive code + +### **3. Tail Recursion vs Regular Recursion** + +| Regular Recursion | Tail Recursion | +|-------------------|----------------| +| Performs computation after recursive call | No computation after recursive call | +| Can cause stack overflow | Optimized to prevent stack overflow | +| Less efficient | More efficient | +| Harder to optimize | Easy to optimize | + +## ๐Ÿงช Examples and Exercises + +### **Example 1: Factorial Function** +```kotlin +tailrec fun factorial(n: Int, accumulator: Int = 1): Int { + return if (n <= 1) accumulator else factorial(n - 1, n * accumulator) +} + +// Usage +val result = factorial(5) // Returns 120 (5 * 4 * 3 * 2 * 1) +``` + +**How it works:** +1. `factorial(5, 1)` โ†’ `factorial(4, 5)` +2. `factorial(4, 5)` โ†’ `factorial(3, 20)` +3. `factorial(3, 20)` โ†’ `factorial(2, 60)` +4. `factorial(2, 60)` โ†’ `factorial(1, 120)` +5. `factorial(1, 120)` โ†’ returns `120` + +### **Example 2: Fibonacci Function** +```kotlin +tailrec fun fibonacci(n: Int, a: Int = 0, b: Int = 1): Int { + return when (n) { + 0 -> a + 1 -> b + else -> fibonacci(n - 1, b, a + b) + } +} + +// Usage +val fib10 = fibonacci(10) // Returns 55 +``` + +### **Example 3: Sum of Numbers** +```kotlin +tailrec fun sumUpTo(n: Int, accumulator: Int = 0): Int { + return if (n <= 0) accumulator else sumUpTo(n - 1, accumulator + n) +} + +// Usage +val sum = sumUpTo(100) // Returns 5050 +``` + +### **Example 4: List Processing** +```kotlin +tailrec fun reverseList( + list: List, + accumulator: List = emptyList() +): List { + return if (list.isEmpty()) { + accumulator + } else { + reverseList(list.drop(1), listOf(list.first()) + accumulator) + } +} + +// Usage +val original = listOf(1, 2, 3, 4, 5) +val reversed = reverseList(original) // Returns [5, 4, 3, 2, 1] +``` + +## โš ๏ธ Important Requirements + +### **1. The Recursive Call Must Be Last** +```kotlin +// โŒ Won't work - computation after recursive call +tailrec fun factorial(n: Int): Int { + return if (n <= 1) 1 else n * factorial(n - 1) // Multiplication after call +} + +// โœ… Will work - recursive call is last operation +tailrec fun factorial(n: Int, accumulator: Int = 1): Int { + return if (n <= 1) accumulator else factorial(n - 1, n * accumulator) +} +``` + +### **2. No Additional Operations After Recursive Call** +```kotlin +// โŒ Won't work - additional operation after call +tailrec fun process(n: Int): Int { + return if (n <= 0) 0 else { + val result = process(n - 1) // Recursive call + result + 1 // Additional operation + } +} + +// โœ… Will work - no operations after recursive call +tailrec fun process(n: Int, accumulator: Int = 0): Int { + return if (n <= 0) accumulator else process(n - 1, accumulator + 1) +} +``` + +### **3. Only Direct Recursive Calls** +```kotlin +// โŒ Won't work - indirect recursive call +tailrec fun functionA(n: Int): Int { + return if (n <= 0) 0 else functionB(n - 1) +} + +fun functionB(n: Int): Int { + return if (n <= 0) 0 else functionA(n - 1) +} + +// โœ… Will work - direct recursive call +tailrec fun functionA(n: Int, accumulator: Int = 0): Int { + return if (n <= 0) accumulator else functionA(n - 1, accumulator + 1) +} +``` + +## ๐ŸŽฏ Best Practices + +### **1. Use Accumulator Pattern** +```kotlin +// โœ… Good - uses accumulator for tail recursion +tailrec fun factorial(n: Int, accumulator: Int = 1): Int { + return if (n <= 1) accumulator else factorial(n - 1, n * accumulator) +} + +// โŒ Avoid - doesn't use accumulator +fun factorial(n: Int): Int { + return if (n <= 1) 1 else n * factorial(n - 1) +} +``` + +### **2. Keep Functions Simple** +```kotlin +// โœ… Good - simple and clear +tailrec fun countDown(n: Int): Int { + return if (n <= 0) 0 else countDown(n - 1) +} + +// โŒ Avoid - complex logic in tailrec +tailrec fun complexFunction(n: Int, data: ComplexData): ComplexResult { + // Complex processing logic... + return if (n <= 0) data.result else complexFunction(n - 1, processData(data)) +} +``` + +### **3. Document the Tailrec Nature** +```kotlin +/** + * Calculates factorial using tail recursion for efficiency + * @param n The number to calculate factorial for + * @param accumulator Current accumulated result (internal use) + * @return The factorial of n + */ +tailrec fun factorial(n: Int, accumulator: Int = 1): Int { + return if (n <= 1) accumulator else factorial(n - 1, n * accumulator) +} +``` + +## ๐Ÿ”ง Practical Applications + +### **1. Mathematical Calculations** +```kotlin +tailrec fun power(base: Int, exponent: Int, accumulator: Int = 1): Int { + return if (exponent <= 0) accumulator else power(base, exponent - 1, base * accumulator) +} + +tailrec fun gcd(a: Int, b: Int): Int { + return if (b == 0) a else gcd(b, a % b) +} +``` + +### **2. List Processing** +```kotlin +tailrec fun findElement( + list: List, + predicate: (T) -> Boolean, + index: Int = 0 +): T? { + return when { + index>= list.size -> null + predicate(list[index]) -> list[index] + else -> findElement(list, predicate, index + 1) + } +} +``` + +### **3. Tree Traversal** +```kotlin +data class TreeNode(val value: T, val left: TreeNode?, val right: TreeNode?) + +tailrec fun findInTree( + node: TreeNode?, + target: T, + path: List = emptyList() +): List? { + return when { + node == null -> null + node.value == target -> path + node.value + else -> { + val leftResult = findInTree(node.left, target, path + node.value) + if (leftResult != null) leftResult else findInTree(node.right, target, path + node.value) + } + } +} +``` + +## ๐Ÿ“š Summary + +**Tailrec functions** in Kotlin provide: +- **Stack overflow prevention** through compiler optimization +- **Performance improvement** by converting recursion to loops +- **Maintainable code** with recursive logic +- **Memory efficiency** through stack frame elimination + +## ๐ŸŽฏ Key Takeaways + +1. **Use `tailrec` keyword** for functions that can be optimized +2. **Recursive call must be last** operation in the function +3. **Use accumulator pattern** to make functions tail recursive +4. **No computation after** recursive calls +5. **Compiler handles optimization** automatically + +## ๐Ÿš€ Next Steps + +- Practice converting regular recursive functions to tailrec +- Learn about other optimization techniques +- Explore functional programming patterns +- Understand compiler optimizations + +--- + +**๐Ÿ“ Source Code:** [25_tailrec_function.kt](https://github.com/gpl-gowthamchand/KotlinTutorial/blob/feature/documentation-improvements/src/25_tailrec_function.kt) + +**๐Ÿ”— Related Topics:** +- [Functions Basics](../functions/01-functions-basics.md) +- [Function Expressions](../functions/02-functions-as-expressions.md) +- [Default Parameters](../functions/06-default-parameters.md) diff --git a/docs/oop/05-init-block.md b/docs/oop/05-init-block.md new file mode 100644 index 0000000..32aaee4 --- /dev/null +++ b/docs/oop/05-init-block.md @@ -0,0 +1,463 @@ +# ๐Ÿ—๏ธ INIT Block in Kotlin + +The `init` block in Kotlin is a special initialization block that runs when an object is created. It's part of the primary constructor and allows you to execute initialization logic during object creation. + +## ๐Ÿ“‹ Learning Objectives + +By the end of this lesson, you will be able to: +- โœ… Understand what the init block is +- โœ… Create and use init blocks effectively +- โœ… Know when to use init blocks +- โœ… Understand the execution order +- โœ… Apply init blocks in real-world scenarios + +## ๐Ÿ” What You'll Learn + +- **Init block syntax** - How to declare and use init blocks +- **Initialization order** - Understanding when init blocks execute +- **Use cases** - When and where to use init blocks +- **Best practices** - Guidelines for effective usage +- **Common patterns** - Typical initialization scenarios + +## ๐Ÿ“ Prerequisites + +- โœ… Basic understanding of classes in Kotlin +- โœ… Knowledge of constructors +- โœ… Familiarity with Kotlin syntax +- โœ… Understanding of object-oriented programming + +## ๐Ÿ’ป The Code + +Let's examine the init block examples: + +**๐Ÿ“ File:** [26_class_and_constructor.kt](https://github.com/gpl-gowthamchand/KotlinTutorial/blob/feature/documentation-improvements/src/26_class_and_constructor.kt) + +## ๐Ÿ” Code Breakdown + +### **Basic Init Block Declaration** +```kotlin +class Person(val name: String, val age: Int) { + init { + println("Creating a new Person: $name, age $age") + } +} +``` + +**Key Components:** +- `class Person` - Class declaration +- `val name: String, val age: Int` - Primary constructor parameters +- `init { }` - Initialization block +- `println(...)` - Initialization logic + +### **Init Block with Validation** +```kotlin +class Student(val name: String, val age: Int) { + init { + require(age>= 0) { "Age cannot be negative" } + require(name.isNotBlank()) { "Name cannot be empty" } + println("Student $name created successfully") + } +} +``` + +## ๐ŸŽฏ Key Concepts Explained + +### **1. What is the Init Block?** + +The `init` block is: +- **Part of the primary constructor** - runs when object is created +- **An initialization block** - executes setup code +- **Executed in order** - multiple init blocks run sequentially +- **Access to constructor parameters** - can use all constructor values + +### **2. When Init Blocks Execute** + +Init blocks execute: +- **During object creation** - when constructor is called +- **After constructor parameters** - have been assigned +- **Before object is ready** - for use +- **In declaration order** - multiple init blocks run sequentially + +### **3. Execution Order** + +```kotlin +class Example(val param: String) { + init { + println("First init block: $param") + } + + val property = "Property initialized" + + init { + println("Second init block: $param, $property") + } +} + +// Execution order: +// 1. Constructor parameters assigned +// 2. First init block executes +// 3. Properties initialized +// 4. Second init block executes +// 5. Object ready for use +``` + +## ๐Ÿงช Examples and Exercises + +### **Example 1: Basic Initialization** +```kotlin +class Rectangle(val width: Int, val height: Int) { + init { + println("Creating rectangle: ${width}x${height}") + } + + val area: Int = width * height + + init { + println("Rectangle area: $area") + } +} + +// Usage +val rect = Rectangle(5, 3) +// Output: +// Creating rectangle: 5x3 +// Rectangle area: 15 +``` + +### **Example 2: Parameter Validation** +```kotlin +class BankAccount(val accountNumber: String, val initialBalance: Double) { + init { + require(accountNumber.isNotBlank()) { "Account number cannot be empty" } + require(initialBalance>= 0) { "Initial balance cannot be negative" } + require(accountNumber.length>= 8) { "Account number must be at least 8 characters" } + } + + var balance = initialBalance + private set + + fun deposit(amount: Double) { + require(amount> 0) { "Deposit amount must be positive" } + balance += amount + } + + fun withdraw(amount: Double) { + require(amount> 0) { "Withdrawal amount must be positive" } + require(amount <= balance) { "Insufficient funds" } + balance -= amount + } +} + +// Usage +val account = BankAccount("12345678", 1000.0) +account.deposit(500.0) +account.withdraw(200.0) +println("Balance: ${account.balance}") // 1300.0 +``` + +### **Example 3: Complex Initialization** +```kotlin +class DatabaseConnection( + val host: String, + val port: Int, + val database: String, + val username: String, + val password: String +) { + private val connectionString: String + + init { + // Validate connection parameters + require(host.isNotBlank()) { "Host cannot be empty" } + require(port in 1..65535) { "Port must be between 1 and 65535" } + require(database.isNotBlank()) { "Database name cannot be empty" } + + // Build connection string + connectionString = "jdbc:postgresql://$host:$port/$database" + + // Log connection attempt + println("Attempting to connect to: $connectionString") + } + + fun connect(): Boolean { + // Simulate connection logic + println("Connected to database: $database on $host:$port") + return true + } +} + +// Usage +val db = DatabaseConnection("localhost", 5432, "mydb", "user", "pass") +db.connect() +``` + +### **Example 4: Lazy Initialization with Init** +```kotlin +class Configuration(val configFile: String) { + private val config: Map + + init { + // Load configuration from file + config = try { + readConfigFile(configFile) + } catch (e: Exception) { + println("Failed to load config file: ${e.message}") + emptyMap() + } + + // Validate required configuration + val requiredKeys = listOf("database_url", "api_key", "timeout") + val missingKeys = requiredKeys.filter { !config.containsKey(it) } + + if (missingKeys.isNotEmpty()) { + println("Warning: Missing required configuration keys: $missingKeys") + } + } + + fun get(key: String): String? = config[key] + + private fun readConfigFile(filename: String): Map { + // Simulate reading config file + return mapOf( + "database_url" to "jdbc:postgresql://localhost:5432/mydb", + "api_key" to "abc123", + "timeout" to "30" + ) + } +} + +// Usage +val config = Configuration("app.conf") +println("Database URL: ${config.get("database_url")}") +``` + +## โš ๏ธ Important Considerations + +### **1. Init Block Execution Order** +```kotlin +class Example { + init { + println("Init block 1") + } + + val property1 = "Property 1".also { println("Property 1 initialized") } + + init { + println("Init block 2") + } + + val property2 = "Property 2".also { println("Property 2 initialized") } + + init { + println("Init block 3") + } +} + +// Execution order: +// Init block 1 +// Property 1 initialized +// Init block 2 +// Property 2 initialized +// Init block 3 +``` + +### **2. Access to Constructor Parameters** +```kotlin +class Person(val name: String, val age: Int) { + init { + // โœ… Can access constructor parameters + println("Name: $name, Age: $age") + + // โœ… Can perform validation + require(age>= 0) { "Age cannot be negative" } + + // โœ… Can initialize derived properties + val isAdult = age>= 18 + println("Is adult: $isAdult") + } +} +``` + +### **3. Multiple Init Blocks** +```kotlin +class ComplexClass(val param: String) { + init { + println("First init: $param") + } + + val property1 = "Property 1" + + init { + println("Second init: $param, $property1") + } + + val property2 = "Property 2" + + init { + println("Third init: $param, $property1, $property2") + } +} +``` + +## ๐ŸŽฏ Best Practices + +### **1. Use for Validation** +```kotlin +// โœ… Good - validation in init block +class User(val email: String, val age: Int) { + init { + require(email.contains("@")) { "Invalid email format" } + require(age>= 13) { "User must be at least 13 years old" } + } +} + +// โŒ Avoid - validation in separate method +class User(val email: String, val age: Int) { + fun validate() { + require(email.contains("@")) { "Invalid email format" } + require(age>= 13) { "User must be at least 13 years old" } + } +} +``` + +### **2. Keep Init Blocks Focused** +```kotlin +// โœ… Good - focused initialization +class Product(val name: String, val price: Double) { + init { + require(name.isNotBlank()) { "Product name cannot be empty" } + require(price>= 0) { "Price cannot be negative" } + } + + val formattedPrice = "$${String.format("%.2f", price)}" +} + +// โŒ Avoid - complex logic in init +class Product(val name: String, val price: Double) { + init { + // Complex business logic... + val marketAnalysis = analyzeMarket() + val competitorPricing = getCompetitorPrices() + val optimalPrice = calculateOptimalPrice(marketAnalysis, competitorPricing) + // ... more complex logic + } +} +``` + +### **3. Document Complex Initialization** +```kotlin +/** + * Represents a network connection with validation and setup + * @param host The server hostname + * @param port The server port number + * @param timeout Connection timeout in milliseconds + */ +class NetworkConnection(val host: String, val port: Int, val timeout: Long) { + init { + // Validate network parameters + require(host.isNotBlank()) { "Host cannot be empty" } + require(port in 1..65535) { "Port must be between 1 and 65535" } + require(timeout> 0) { "Timeout must be positive" } + + // Log connection setup + println("Setting up connection to $host:$port with ${timeout}ms timeout") + } +} +``` + +## ๐Ÿ”ง Practical Applications + +### **1. Configuration Classes** +```kotlin +class AppConfig(val configPath: String) { + private val properties: Properties + + init { + properties = Properties() + try { + FileInputStream(configPath).use { properties.load(it) } + } catch (e: Exception) { + println("Using default configuration: ${e.message}") + loadDefaultConfig() + } + } + + private fun loadDefaultConfig() { + properties.setProperty("database.url", "jdbc:default") + properties.setProperty("database.user", "default") + properties.setProperty("database.password", "default") + } + + fun getProperty(key: String): String? = properties.getProperty(key) +} +``` + +### **2. Data Validation Classes** +```kotlin +class EmailValidator(val email: String) { + private val isValid: Boolean + + init { + val emailRegex = Regex("^[A-Za-z0-9+_.-]+@[A-Za-z0-9.-]+\\.[A-Za-z]{2,}\$") + isValid = emailRegex.matches(email) + + if (!isValid) { + println("Warning: Invalid email format: $email") + } + } + + fun isValidEmail(): Boolean = isValid +} +``` + +### **3. Resource Management** +```kotlin +class FileProcessor(val filePath: String) { + private val file: File + + init { + file = File(filePath) + require(file.exists()) { "File does not exist: $filePath" } + require(file.canRead()) { "File is not readable: $filePath" } + require(file.length()> 0) { "File is empty: $filePath" } + + println("Processing file: ${file.name} (${file.length()} bytes)") + } + + fun process(): String { + return file.readText() + } +} +``` + +## ๐Ÿ“š Summary + +**Init blocks** in Kotlin provide: +- **Automatic execution** during object creation +- **Parameter validation** at construction time +- **Initialization logic** in a clean, organized way +- **Execution order control** for complex initialization + +## ๐ŸŽฏ Key Takeaways + +1. **Use `init { }` blocks** for initialization logic +2. **Execute in declaration order** - multiple init blocks run sequentially +3. **Access constructor parameters** directly in init blocks +4. **Use for validation** and setup logic +5. **Keep focused** - avoid complex business logic in init blocks + +## ๐Ÿš€ Next Steps + +- Practice creating classes with init blocks +- Learn about secondary constructors +- Explore property initialization +- Understand object creation lifecycle + +--- + +**๐Ÿ“ Source Code:** [26_class_and_constructor.kt](https://github.com/gpl-gowthamchand/KotlinTutorial/blob/feature/documentation-improvements/src/26_class_and_constructor.kt) + +**๐Ÿ”— Related Topics:** +- [Classes and Constructors](../oop/01-classes-constructors.md) +- [Inheritance](../oop/02-inheritance.md) +- [Properties](../oop/06-properties.md) From 7da914bcffde87a70eb20344188d2e0969aa4619 Mon Sep 17 00:00:00 2001 From: gpl-gowthamchand Date: 2025ๅนด8ๆœˆ25ๆ—ฅ 12:03:22 +0530 Subject: [PATCH 17/23] feat: Add missing documentation files for Properties, Polymorphism, and Interface --- docs/oop/06-properties.md | 439 ++++++++++++++++++++++++++++ docs/oop/08-polymorphism.md | 519 +++++++++++++++++++++++++++++++++ docs/oop/09-interface.md | 566 ++++++++++++++++++++++++++++++++++++ 3 files changed, 1524 insertions(+) create mode 100644 docs/oop/06-properties.md create mode 100644 docs/oop/08-polymorphism.md create mode 100644 docs/oop/09-interface.md diff --git a/docs/oop/06-properties.md b/docs/oop/06-properties.md new file mode 100644 index 0000000..e08569a --- /dev/null +++ b/docs/oop/06-properties.md @@ -0,0 +1,439 @@ +# ๐Ÿท๏ธ Properties (Field Variables) in Kotlin + +Properties in Kotlin are a powerful feature that combines fields, getters, and setters into a single declaration. They provide a clean, concise way to define class attributes with automatic backing fields and custom access logic. + +## ๐Ÿ“‹ Learning Objectives + +By the end of this lesson, you will be able to: +- โœ… Understand what properties are in Kotlin +- โœ… Create different types of properties +- โœ… Use custom getters and setters +- โœ… Know when to use properties vs fields +- โœ… Apply properties in real-world scenarios + +## ๐Ÿ” What You'll Learn + +- **Property syntax** - How to declare and use properties +- **Backing fields** - Understanding automatic field generation +- **Custom accessors** - Creating custom getters and setters +- **Property types** - Val, var, and computed properties +- **Best practices** - Guidelines for effective usage + +## ๐Ÿ“ Prerequisites + +- โœ… Basic understanding of classes in Kotlin +- โœ… Knowledge of constructors +- โœ… Familiarity with Kotlin syntax +- โœ… Understanding of object-oriented programming + +## ๐Ÿ’ป The Code + +Let's examine the properties examples: + +**๐Ÿ“ File:** [26_class_and_constructor.kt](https://github.com/gpl-gowthamchand/KotlinTutorial/blob/feature/documentation-improvements/src/26_class_and_constructor.kt) + +## ๐Ÿ” Code Breakdown + +### **Basic Property Declaration** +```kotlin +class Person(val name: String, var age: Int) { + var email: String = "" + val isAdult: Boolean + get() = age>= 18 +} +``` + +**Key Components:** +- `val name: String` - Read-only property (immutable) +- `var age: Int` - Mutable property +- `var email: String = ""` - Property with default value +- `val isAdult: Boolean` - Computed property with custom getter + +### **Property with Custom Getter** +```kotlin +class Rectangle(val width: Int, val height: Int) { + val area: Int + get() = width * height + + val perimeter: Int + get() = 2 * (width + height) +} +``` + +## ๐ŸŽฏ Key Concepts Explained + +### **1. What are Properties?** + +Properties in Kotlin: +- **Combine fields and accessors** in a single declaration +- **Automatically generate backing fields** when needed +- **Provide clean syntax** for class attributes +- **Support custom logic** through getters and setters + +### **2. Property Types** + +| Type | Declaration | Mutability | Backing Field | +|------|-------------|------------|----------------| +| `val` | Read-only | Immutable | Generated automatically | +| `var` | Read-write | Mutable | Generated automatically | +| Custom | Custom logic | Depends on implementation | Optional | + +### **3. Backing Fields** + +```kotlin +class Example { + var counter: Int = 0 // Backing field automatically generated + + var customCounter: Int = 0 + get() = field // Access to backing field + set(value) { + field = value.coerceAtLeast(0) // Modify backing field + } +} +``` + +## ๐Ÿงช Examples and Exercises + +### **Example 1: Basic Properties** +```kotlin +class Student( + val studentId: String, + var name: String, + var grade: Double +) { + var email: String = "" + val isPassing: Boolean + get() = grade>= 60.0 + + val letterGrade: String + get() = when { + grade>= 90 -> "A" + grade>= 80 -> "B" + grade>= 70 -> "C" + grade>= 60 -> "D" + else -> "F" + } +} + +// Usage +val student = Student("S001", "John Doe", 85.5) +student.email = "john.doe@school.edu" +println("${student.name} has grade ${student.letterGrade}") // John Doe has grade B +println("Is passing: ${student.isPassing}") // Is passing: true +``` + +### **Example 2: Properties with Validation** +```kotlin +class BankAccount( + val accountNumber: String, + initialBalance: Double +) { + var balance: Double = initialBalance + private set // Only class can modify balance + + var accountHolder: String = "" + set(value) { + require(value.isNotBlank()) { "Account holder name cannot be empty" } + field = value.trim() + } + + val isOverdrawn: Boolean + get() = balance < 0 + + val formattedBalance: String + get() = "$${String.format("%.2f", balance)}" + + fun deposit(amount: Double) { + require(amount> 0) { "Deposit amount must be positive" } + balance += amount + } + + fun withdraw(amount: Double) { + require(amount> 0) { "Withdrawal amount must be positive" } + require(amount <= balance) { "Insufficient funds" } + balance -= amount + } +} + +// Usage +val account = BankAccount("12345", 1000.0) +account.accountHolder = " Jane Smith " // Trims whitespace +account.deposit(500.0) +println("${account.accountHolder}: ${account.formattedBalance}") // Jane Smith: 1500ใƒ‰ใƒซ.00 +``` + +### **Example 3: Computed Properties** +```kotlin +class Circle(val radius: Double) { + val diameter: Double + get() = 2 * radius + + val circumference: Double + get() = Math.PI * diameter + + val area: Double + get() = Math.PI * radius * radius + + val isLarge: Boolean + get() = radius> 10.0 +} + +class Rectangle(val width: Double, val height: Double) { + val area: Double + get() = width * height + + val perimeter: Double + get() = 2 * (width + height) + + val isSquare: Boolean + get() = width == height + + val diagonal: Double + get() = Math.sqrt(width * width + height * height) +} + +// Usage +val circle = Circle(5.0) +println("Circle area: ${circle.area}") // Circle area: 78.54... +println("Is large: ${circle.isLarge}") // Is large: false + +val rect = Rectangle(4.0, 3.0) +println("Rectangle area: ${rect.area}") // Rectangle area: 12.0 +println("Is square: ${rect.isSquare}") // Is square: false +``` + +### **Example 4: Lazy Properties** +```kotlin +class DataProcessor(val data: List) { + val processedData: List by lazy { + println("Processing data...") // Only executed once + data.map { it.trim().uppercase() } + } + + val statistics: Map by lazy { + println("Calculating statistics...") // Only executed once + mapOf( + "count" to data.size, + "totalLength" to data.sumOf { it.length }, + "averageLength" to data.map { it.length }.average(), + "longest" to data.maxByOrNull { it.length } ?: "", + "shortest" to data.minByOrNull { it.length } ?: "" + ) + } +} + +// Usage +val processor = DataProcessor(listOf("hello", "world", "kotlin")) +println("Data processor created") + +// First access triggers computation +println("Processed data: ${processor.processedData}") +// Output: Processing data... +// Processed data: [HELLO, WORLD, KOTLIN] + +// Second access uses cached result +println("Processed data again: ${processor.processedData}") +// Output: Processed data again: [HELLO, WORLD, KOTLIN] + +// Statistics computed on first access +println("Statistics: ${processor.statistics}") +// Output: Calculating statistics... +// Statistics: {count=3, totalLength=16, averageLength=5.33..., longest=kotlin, shortest=hello} +``` + +## โš ๏ธ Important Considerations + +### **1. Backing Field Access** +```kotlin +class Example { + var counter: Int = 0 + get() = field // Access backing field + set(value) { + field = value // Modify backing field + } + + // โŒ Won't work - infinite recursion + var wrongCounter: Int = 0 + get() = wrongCounter // Calls getter infinitely + set(value) { + wrongCounter = value // Calls setter infinitely + } +} +``` + +### **2. Property Initialization** +```kotlin +class Example { + // โœ… Valid - property initialized + var name: String = "Default" + + // โœ… Valid - property initialized in init block + var description: String + + init { + description = "Initial description" + } + + // โŒ Invalid - property not initialized + var status: String // Error: Property must be initialized +} +``` + +### **3. Custom Setter Validation** +```kotlin +class User { + var age: Int = 0 + set(value) { + require(value>= 0) { "Age cannot be negative" } + require(value <= 150) { "Age cannot exceed 150" } + field = value + } + + var email: String = "" + set(value) { + require(value.contains("@")) { "Invalid email format" } + field = value.lowercase() + } +} +``` + +## ๐ŸŽฏ Best Practices + +### **1. Use Properties for Simple Access** +```kotlin +// โœ… Good - simple property +class Person(val name: String, var age: Int) + +// โŒ Avoid - unnecessary getter/setter +class Person(private val _name: String, private var _age: Int) { + fun getName(): String = _name + fun getAge(): Int = _age + fun setAge(age: Int) { _age = age } +} +``` + +### **2. Use Custom Accessors for Logic** +```kotlin +// โœ… Good - custom logic in getter +class Rectangle(val width: Int, val height: Int) { + val area: Int + get() = width * height +} + +// โŒ Avoid - storing computed values +class Rectangle(val width: Int, val height: Int) { + val area: Int = width * height // Recalculated every time +} +``` + +### **3. Use Lazy for Expensive Operations** +```kotlin +// โœ… Good - lazy initialization +class DataProcessor(val data: List) { + val processedData: List by lazy { + // Expensive processing + data.map { it.trim().uppercase() } + } +} + +// โŒ Avoid - immediate computation +class DataProcessor(val data: List) { + val processedData: List = data.map { it.trim().uppercase() } +} +``` + +## ๐Ÿ”ง Practical Applications + +### **1. Configuration Classes** +```kotlin +class AppConfig { + var databaseUrl: String = "jdbc:default" + set(value) { + require(value.startsWith("jdbc:")) { "Invalid database URL format" } + field = value + } + + var maxConnections: Int = 10 + set(value) { + require(value in 1..100) { "Max connections must be between 1 and 100" } + field = value + } + + val isConfigured: Boolean + get() = databaseUrl != "jdbc:default" +} +``` + +### **2. Data Validation** +```kotlin +class EmailValidator { + var email: String = "" + set(value) { + val emailRegex = Regex("^[A-Za-z0-9+_.-]+@[A-Za-z0-9.-]+\\.[A-Za-z]{2,}\$") + require(emailRegex.matches(value)) { "Invalid email format" } + field = value.lowercase() + } + + val isValid: Boolean + get() = email.isNotBlank() + + val domain: String + get() = email.substringAfter("@") +} +``` + +### **3. Business Logic** +```kotlin +class Order(val items: List) { + val subtotal: Double + get() = items.sumOf { it.price * it.quantity } + + val tax: Double + get() = subtotal * 0.08 // 8% tax + + val total: Double + get() = subtotal + tax + + val isEligibleForDiscount: Boolean + get() = subtotal>= 100.0 + + val discount: Double + get() = if (isEligibleForDiscount) subtotal * 0.10 else 0.0 + + val finalTotal: Double + get() = total - discount +} +``` + +## ๐Ÿ“š Summary + +**Properties** in Kotlin provide: +- **Clean syntax** for class attributes +- **Automatic backing fields** when needed +- **Custom accessors** for validation and logic +- **Type safety** and compile-time checks +- **Performance optimization** through lazy initialization + +## ๐ŸŽฏ Key Takeaways + +1. **Use `val` for immutable** properties +2. **Use `var` for mutable** properties +3. **Custom getters/setters** for validation and logic +4. **Lazy properties** for expensive operations +5. **Backing fields** with `field` keyword + +## ๐Ÿš€ Next Steps + +- Practice creating properties with custom accessors +- Learn about delegated properties +- Explore property delegation patterns +- Understand property initialization order + +--- + +**๐Ÿ“ Source Code:** [26_class_and_constructor.kt](https://github.com/gpl-gowthamchand/KotlinTutorial/blob/feature/documentation-improvements/src/26_class_and_constructor.kt) + +**๐Ÿ”— Related Topics:** +- [Classes and Constructors](../oop/01-classes-constructors.md) +- [INIT Block](../oop/05-init-block.md) +- [Inheritance](../oop/02-inheritance.md) diff --git a/docs/oop/08-polymorphism.md b/docs/oop/08-polymorphism.md new file mode 100644 index 0000000..230f6a8 --- /dev/null +++ b/docs/oop/08-polymorphism.md @@ -0,0 +1,519 @@ +# ๐Ÿ”„ Polymorphism in Kotlin + +Polymorphism is a fundamental concept in object-oriented programming that allows objects of different types to be treated as objects of a common base type. In Kotlin, polymorphism enables flexible, extensible code through method overriding and interface implementation. + +## ๐Ÿ“‹ Learning Objectives + +By the end of this lesson, you will be able to: +- โœ… Understand what polymorphism is +- โœ… Implement method overriding +- โœ… Use interface polymorphism +- โœ… Know when to use polymorphism +- โœ… Apply polymorphism in real-world scenarios + +## ๐Ÿ” What You'll Learn + +- **Polymorphism concepts** - Understanding the different types of polymorphism +- **Method overriding** - How to override methods in subclasses +- **Interface polymorphism** - Using interfaces for polymorphic behavior +- **Use cases** - When and where to use polymorphism +- **Best practices** - Guidelines for effective usage + +## ๐Ÿ“ Prerequisites + +- โœ… Basic understanding of classes in Kotlin +- โœ… Knowledge of inheritance +- โœ… Understanding of interfaces +- โœ… Familiarity with method overriding + +## ๐Ÿ’ป The Code + +Let's examine the polymorphism examples: + +**๐Ÿ“ File:** [27_inheritance.kt](https://github.com/gpl-gowthamchand/KotlinTutorial/blob/feature/documentation-improvements/src/27_inheritance.kt) + +## ๐Ÿ” Code Breakdown + +### **Basic Polymorphism with Inheritance** +```kotlin +open class Animal(val name: String) { + open fun makeSound(): String = "Some sound" +} + +class Dog(name: String) : Animal(name) { + override fun makeSound(): String = "Woof!" +} + +class Cat(name: String) : Animal(name) { + override fun makeSound(): String = "Meow!" +} +``` + +**Key Components:** +- `open class Animal` - Base class that can be inherited +- `open fun makeSound()` - Method that can be overridden +- `override fun makeSound()` - Overridden method in subclasses +- Different implementations for each animal type + +### **Polymorphic Usage** +```kotlin +val animals: List = listOf( + Dog("Buddy"), + Cat("Whiskers"), + Animal("Generic Animal") +) + +// Polymorphic behavior +animals.forEach { animal -> + println("${animal.name} says: ${animal.makeSound()}") +} +``` + +## ๐ŸŽฏ Key Concepts Explained + +### **1. What is Polymorphism?** + +Polymorphism means "many forms" and allows: +- **Different objects** to respond to the same method call +- **Code flexibility** through common interfaces +- **Extensibility** without modifying existing code +- **Type safety** while maintaining flexibility + +### **2. Types of Polymorphism** + +| Type | Description | Example | +|------|-------------|---------| +| **Compile-time** | Method overloading | Multiple methods with same name, different parameters | +| **Runtime** | Method overriding | Subclasses provide different implementations | +| **Interface** | Interface implementation | Different classes implement same interface | + +### **3. Polymorphism vs Inheritance** + +- **Inheritance** establishes the "is-a" relationship +- **Polymorphism** enables the "behaves-like" relationship +- **Polymorphism** works with inheritance and interfaces +- **Polymorphism** provides runtime flexibility + +## ๐Ÿงช Examples and Exercises + +### **Example 1: Animal Hierarchy** +```kotlin +open class Animal(val name: String, val species: String) { + open fun makeSound(): String = "Some sound" + open fun move(): String = "Moving" + open fun eat(): String = "Eating" + + fun describe(): String = "$name is a $species" +} + +class Dog(name: String) : Animal(name, "Dog") { + override fun makeSound(): String = "Woof!" + override fun move(): String = "Running on four legs" + override fun eat(): String = "Eating dog food" +} + +class Cat(name: String) : Animal(name, "Cat") { + override fun makeSound(): String = "Meow!" + override fun move(): String = "Walking gracefully" + override fun eat(): String = "Eating cat food" +} + +class Bird(name: String) : Animal(name, "Bird") { + override fun makeSound(): String = "Tweet!" + override fun move(): String = "Flying" + override fun eat(): String = "Eating seeds" +} + +// Polymorphic usage +fun demonstratePolymorphism() { + val animals: List = listOf( + Dog("Buddy"), + Cat("Whiskers"), + Bird("Polly") + ) + + animals.forEach { animal -> + println("${animal.describe()}") + println(" Sound: ${animal.makeSound()}") + println(" Movement: ${animal.move()}") + println(" Eating: ${animal.eat()}") + println() + } +} + +// Output: +// Buddy is a Dog +// Sound: Woof! +// Movement: Running on four legs +// Eating: Eating dog food +// +// Whiskers is a Cat +// Sound: Meow! +// Movement: Walking gracefully +// Eating: Eating cat food +// +// Polly is a Bird +// Sound: Tweet! +// Movement: Flying +// Eating: Eating seeds +``` + +### **Example 2: Shape Hierarchy** +```kotlin +abstract class Shape(val name: String) { + abstract fun calculateArea(): Double + abstract fun calculatePerimeter(): Double + + fun describe(): String = "$name - Area: ${calculateArea()}, Perimeter: ${calculatePerimeter()}" +} + +class Circle(val radius: Double) : Shape("Circle") { + override fun calculateArea(): Double = Math.PI * radius * radius + override fun calculatePerimeter(): Double = 2 * Math.PI * radius +} + +class Rectangle(val width: Double, val height: Double) : Shape("Rectangle") { + override fun calculateArea(): Double = width * height + override fun calculatePerimeter(): Double = 2 * (width + height) +} + +class Triangle(val sideA: Double, val sideB: Double, val sideC: Double) : Shape("Triangle") { + override fun calculateArea(): Double { + val s = (sideA + sideB + sideC) / 2 + return Math.sqrt(s * (s - sideA) * (s - sideB) * (s - sideC)) + } + + override fun calculatePerimeter(): Double = sideA + sideB + sideC +} + +// Polymorphic usage +fun demonstrateShapePolymorphism() { + val shapes: List = listOf( + Circle(5.0), + Rectangle(4.0, 6.0), + Triangle(3.0, 4.0, 5.0) + ) + + shapes.forEach { shape -> + println(shape.describe()) + } + + // Calculate total area + val totalArea = shapes.sumOf { it.calculateArea() } + println("Total area of all shapes: $totalArea") +} +``` + +### **Example 3: Interface Polymorphism** +```kotlin +interface PaymentMethod { + fun processPayment(amount: Double): Boolean + fun getPaymentInfo(): String +} + +class CreditCard(val cardNumber: String, val expiryDate: String) : PaymentMethod { + override fun processPayment(amount: Double): Boolean { + println("Processing credit card payment: $$amount") + return true + } + + override fun getPaymentInfo(): String = "Credit Card ending in ${cardNumber.takeLast(4)}" +} + +class PayPal(val email: String) : PaymentMethod { + override fun processPayment(amount: Double): Boolean { + println("Processing PayPal payment: $$amount") + return true + } + + override fun getPaymentInfo(): String = "PayPal: $email" +} + +class BankTransfer(val accountNumber: String) : PaymentMethod { + override fun processPayment(amount: Double): Boolean { + println("Processing bank transfer: $$amount") + return true + } + + override fun getPaymentInfo(): String = "Bank Transfer to account $accountNumber" +} + +// Polymorphic usage +fun demonstratePaymentPolymorphism() { + val paymentMethods: List = listOf( + CreditCard("1234567890123456", "12/25"), + PayPal("user@example.com"), + BankTransfer("987654321") + ) + + val amount = 100.0 + + paymentMethods.forEach { method -> + println("Using: ${method.getPaymentInfo()}") + val success = method.processPayment(amount) + println("Payment ${if (success) "succeeded" else "failed"}") + println() + } +} +``` + +### **Example 4: Employee Management System** +```kotlin +abstract class Employee(val name: String, val id: String) { + abstract fun calculateSalary(): Double + abstract fun getRole(): String + + fun displayInfo() { + println("Employee: $name (ID: $id)") + println("Role: ${getRole()}") + println("Salary: $${calculateSalary()}") + println() + } +} + +class Manager(name: String, id: String, val department: String) : Employee(name, id) { + override fun calculateSalary(): Double = 80000.0 + override fun getRole(): String = "Manager of $department" +} + +class Developer(name: String, id: String, val programmingLanguage: String) : Employee(name, id) { + override fun calculateSalary(): Double = 70000.0 + override fun getRole(): String = "$programmingLanguage Developer" +} + +class Designer(name: String, id: String, val designTool: String) : Employee(name, id) { + override fun calculateSalary(): Double = 65000.0 + override fun getRole(): String = "$designTool Designer" +} + +// Polymorphic usage +fun demonstrateEmployeePolymorphism() { + val employees: List = listOf( + Manager("John Smith", "M001", "Engineering"), + Developer("Alice Johnson", "D001", "Kotlin"), + Designer("Bob Wilson", "DS001", "Figma") + ) + + // Display all employee information + employees.forEach { it.displayInfo() } + + // Calculate total payroll + val totalPayroll = employees.sumOf { it.calculateSalary() } + println("Total monthly payroll: $$totalPayroll") + + // Find employees by role type + val developers = employees.filterIsInstance() + println("Number of developers: ${developers.size}") +} +``` + +## โš ๏ธ Important Considerations + +### **1. Method Overriding Rules** +```kotlin +open class Base { + open fun method1() = "Base method1" + open fun method2() = "Base method2" + fun method3() = "Base method3" // Not open, cannot override +} + +class Derived : Base() { + override fun method1() = "Derived method1" // โœ… Can override + override fun method2() = "Derived method2" // โœ… Can override + // override fun method3() = "Derived method3" // โŒ Cannot override +} +``` + +### **2. Property Overriding** +```kotlin +open class Base { + open val property1: String = "Base property1" + open var property2: String = "Base property2" +} + +class Derived : Base() { + override val property1: String = "Derived property1" // โœ… Can override + override var property2: String = "Derived property2" // โœ… Can override +} +``` + +### **3. Constructor Parameters** +```kotlin +open class Base(val name: String) { + open fun greet() = "Hello, I'm $name" +} + +class Derived(name: String, val age: Int) : Base(name) { + override fun greet() = "Hello, I'm $name and I'm $age years old" +} +``` + +## ๐ŸŽฏ Best Practices + +### **1. Use Abstract Classes for Common Behavior** +```kotlin +// โœ… Good - abstract class with common behavior +abstract class Vehicle(val name: String) { + fun start() = println("$name is starting") + fun stop() = println("$name is stopping") + abstract fun move() +} + +class Car(name: String) : Vehicle(name) { + override fun move() = println("$name is driving on roads") +} + +class Boat(name: String) : Vehicle(name) { + override fun move() = println("$name is sailing on water") +} +``` + +### **2. Use Interfaces for Capabilities** +```kotlin +// โœ… Good - interface for capabilities +interface Flyable { + fun fly() +} + +interface Swimmable { + fun swim() +} + +class Duck : Animal("Duck"), Flyable, Swimmable { + override fun fly() = println("Duck is flying") + override fun swim() = println("Duck is swimming") +} +``` + +### **3. Keep Overridden Methods Simple** +```kotlin +// โœ… Good - simple, focused overrides +class Dog(name: String) : Animal(name) { + override fun makeSound(): String = "Woof!" +} + +// โŒ Avoid - complex logic in overrides +class Dog(name: String) : Animal(name) { + override fun makeSound(): String { + // Complex sound generation logic... + val time = System.currentTimeMillis() + val volume = (time % 100) / 100.0 + return if (volume> 0.5) "WOOF!" else "woof" + } +} +``` + +## ๐Ÿ”ง Practical Applications + +### **1. Plugin Systems** +```kotlin +interface Plugin { + fun initialize() + fun execute() + fun cleanup() +} + +class DatabasePlugin : Plugin { + override fun initialize() = println("Database plugin initialized") + override fun execute() = println("Database plugin executed") + override fun cleanup() = println("Database plugin cleaned up") +} + +class FilePlugin : Plugin { + override fun initialize() = println("File plugin initialized") + override fun execute() = println("File plugin executed") + override fun cleanup() = println("File plugin cleaned up") +} + +class PluginManager { + private val plugins: MutableList = mutableListOf() + + fun addPlugin(plugin: Plugin) { + plugins.add(plugin) + } + + fun runAllPlugins() { + plugins.forEach { plugin -> + plugin.initialize() + plugin.execute() + plugin.cleanup() + } + } +} +``` + +### **2. Strategy Pattern** +```kotlin +interface SortingStrategy { + fun sort(list: List): List +} + +class BubbleSort : SortingStrategy { + override fun sort(list: List): List { + // Bubble sort implementation + return list.sorted() + } +} + +class QuickSort : SortingStrategy { + override fun sort(list: List): List { + // Quick sort implementation + return list.sorted() + } +} + +class Sorter(private val strategy: SortingStrategy) { + fun sort(list: List): List = strategy.sort(list) +} +``` + +### **3. Factory Pattern** +```kotlin +interface AnimalFactory { + fun createAnimal(type: String, name: String): Animal +} + +class ConcreteAnimalFactory : AnimalFactory { + override fun createAnimal(type: String, name: String): Animal { + return when (type.lowercase()) { + "dog" -> Dog(name) + "cat" -> Cat(name) + "bird" -> Bird(name) + else -> throw IllegalArgumentException("Unknown animal type: $type") + } + } +} +``` + +## ๐Ÿ“š Summary + +**Polymorphism** in Kotlin provides: +- **Code flexibility** through common interfaces +- **Extensibility** without modifying existing code +- **Runtime behavior** selection +- **Type safety** with flexibility +- **Clean architecture** through abstraction + +## ๐ŸŽฏ Key Takeaways + +1. **Use `open` keyword** for classes and methods that can be overridden +2. **Use `override` keyword** when overriding methods +3. **Interfaces provide** polymorphic behavior +4. **Abstract classes** combine common behavior with polymorphism +5. **Polymorphism enables** flexible, extensible code + +## ๐Ÿš€ Next Steps + +- Practice creating polymorphic hierarchies +- Learn about sealed classes +- Explore generic polymorphism +- Understand covariance and contravariance + +--- + +**๐Ÿ“ Source Code:** [27_inheritance.kt](https://github.com/gpl-gowthamchand/KotlinTutorial/blob/feature/documentation-improvements/src/27_inheritance.kt) + +**๐Ÿ”— Related Topics:** +- [Inheritance](../oop/02-inheritance.md) +- [Method Overriding](../oop/03-method-overriding.md) +- [Abstract Classes](../oop/04-abstract-classes.md) diff --git a/docs/oop/09-interface.md b/docs/oop/09-interface.md new file mode 100644 index 0000000..7d43e35 --- /dev/null +++ b/docs/oop/09-interface.md @@ -0,0 +1,566 @@ +# ๐Ÿ”Œ Interface in Kotlin + +Interfaces in Kotlin are a powerful abstraction mechanism that defines a contract for classes to implement. They provide a way to achieve multiple inheritance and define common behavior that can be shared across different class hierarchies. + +## ๐Ÿ“‹ Learning Objectives + +By the end of this lesson, you will be able to: +- โœ… Understand what interfaces are in Kotlin +- โœ… Create and implement interfaces +- โœ… Use interface properties and methods +- โœ… Know when to use interfaces vs abstract classes +- โœ… Apply interfaces in real-world scenarios + +## ๐Ÿ” What You'll Learn + +- **Interface syntax** - How to declare and use interfaces +- **Interface implementation** - How classes implement interfaces +- **Interface properties** - Properties in interfaces +- **Multiple inheritance** - Implementing multiple interfaces +- **Best practices** - Guidelines for effective usage + +## ๐Ÿ“ Prerequisites + +- โœ… Basic understanding of classes in Kotlin +- โœ… Knowledge of inheritance +- โœ… Understanding of abstract classes +- โœ… Familiarity with Kotlin syntax + +## ๐Ÿ’ป The Code + +Let's examine the interface examples: + +**๐Ÿ“ File:** [31_interface.kt](https://github.com/gpl-gowthamchand/KotlinTutorial/blob/feature/documentation-improvements/src/31_interface.kt) + +## ๐Ÿ” Code Breakdown + +### **Basic Interface Declaration** +```kotlin +interface Drawable { + fun draw() + val color: String +} +``` + +**Key Components:** +- `interface Drawable` - Interface declaration +- `fun draw()` - Abstract method (no implementation) +- `val color: String` - Abstract property + +### **Interface Implementation** +```kotlin +class Circle : Drawable { + override val color: String = "Red" + override fun draw() { + println("Drawing a $color circle") + } +} +``` + +## ๐ŸŽฏ Key Concepts Explained + +### **1. What are Interfaces?** + +Interfaces in Kotlin: +- **Define contracts** that classes must implement +- **Provide abstraction** without implementation details +- **Enable multiple inheritance** (unlike classes) +- **Support default implementations** for methods +- **Can contain properties** and methods + +### **2. Interface vs Abstract Class** + +| Feature | Interface | Abstract Class | +|---------|-----------|----------------| +| **Inheritance** | Multiple | Single | +| **Constructor** | No | Yes | +| **State** | No | Yes | +| **Default methods** | Yes | Yes | +| **Properties** | Abstract only | Can have state | + +### **3. Interface Implementation** + +```kotlin +interface A { + fun methodA() +} + +interface B { + fun methodB() +} + +class MyClass : A, B { // Multiple interface implementation + override fun methodA() = println("Method A") + override fun methodB() = println("Method B") +} +``` + +## ๐Ÿงช Examples and Exercises + +### **Example 1: Basic Interface Implementation** +```kotlin +interface Animal { + val name: String + fun makeSound() + fun move() +} + +class Dog(override val name: String) : Animal { + override fun makeSound() = println("$name says: Woof!") + override fun move() = println("$name is running") +} + +class Cat(override val name: String) : Animal { + override fun makeSound() = println("$name says: Meow!") + override fun move() = println("$name is walking") +} + +class Bird(override val name: String) : Animal { + override fun makeSound() = println("$name says: Tweet!") + override fun move() = println("$name is flying") +} + +// Usage +fun demonstrateInterfaces() { + val animals: List = listOf( + Dog("Buddy"), + Cat("Whiskers"), + Bird("Polly") + ) + + animals.forEach { animal -> + println("${animal.name}:") + animal.makeSound() + animal.move() + println() + } +} +``` + +### **Example 2: Interface with Default Methods** +```kotlin +interface Logger { + fun log(message: String) + fun logError(error: String) { + log("ERROR: $error") // Default implementation + } + fun logWarning(warning: String) { + log("WARNING: $warning") // Default implementation + } +} + +class ConsoleLogger : Logger { + override fun log(message: String) { + println("[${System.currentTimeMillis()}] $message") + } +} + +class FileLogger : Logger { + override fun log(message: String) { + // Write to file implementation + println("Writing to file: $message") + } + + override fun logError(error: String) { + log("CRITICAL ERROR: $error") // Override default implementation + } +} + +// Usage +fun demonstrateDefaultMethods() { + val consoleLogger = ConsoleLogger() + val fileLogger = FileLogger() + + consoleLogger.log("Application started") + consoleLogger.logWarning("Low memory") + consoleLogger.logError("Connection failed") + + fileLogger.log("Application started") + fileLogger.logWarning("Low memory") + fileLogger.logError("Connection failed") +} +``` + +### **Example 3: Multiple Interface Implementation** +```kotlin +interface Flyable { + fun fly() + val maxAltitude: Int +} + +interface Swimmable { + fun swim() + val maxDepth: Int +} + +interface Walkable { + fun walk() + val maxSpeed: Int +} + +class Duck : Flyable, Swimmable, Walkable { + override val maxAltitude: Int = 1000 + override val maxDepth: Int = 10 + override val maxSpeed: Int = 20 + + override fun fly() = println("Duck is flying up to ${maxAltitude}m") + override fun swim() = println("Duck is swimming down to ${maxDepth}m") + override fun walk() = println("Duck is walking at ${maxSpeed}km/h") +} + +class Fish : Swimmable { + override val maxDepth: Int = 100 + + override fun swim() = println("Fish is swimming down to ${maxDepth}m") +} + +class Eagle : Flyable, Walkable { + override val maxAltitude: Int = 5000 + override val maxSpeed: Int = 5 + + override fun fly() = println("Eagle is flying up to ${maxAltitude}m") + override fun walk() = println("Eagle is walking at ${maxSpeed}km/h") +} + +// Usage +fun demonstrateMultipleInterfaces() { + val duck = Duck() + val fish = Fish() + val eagle = Eagle() + + println("Duck capabilities:") + duck.fly() + duck.swim() + duck.walk() + + println("\nFish capabilities:") + fish.swim() + + println("\nEagle capabilities:") + eagle.fly() + eagle.walk() +} +``` + +### **Example 4: Interface Properties** +```kotlin +interface Vehicle { + val brand: String + val model: String + val year: Int + + val fullName: String + get() = "$year $brand $model" // Default implementation + + fun start() + fun stop() + fun getInfo(): String +} + +class Car( + override val brand: String, + override val model: String, + override val year: Int +) : Vehicle { + override fun start() = println("$fullName is starting") + override fun stop() = println("$fullName is stopping") + override fun getInfo() = "Car: $fullName" +} + +class Motorcycle( + override val brand: String, + override val model: String, + override val year: Int +) : Vehicle { + override fun start() = println("$fullName is starting") + override fun stop() = println("$fullName is stopping") + override fun getInfo() = "Motorcycle: $fullName" +} + +// Usage +fun demonstrateInterfaceProperties() { + val car = Car("Toyota", "Camry", 2023) + val motorcycle = Motorcycle("Honda", "CBR", 2023) + + println(car.getInfo()) + car.start() + car.stop() + + println(motorcycle.getInfo()) + motorcycle.start() + motorcycle.stop() +} +``` + +## โš ๏ธ Important Considerations + +### **1. Interface Method Implementation** +```kotlin +interface MyInterface { + fun method1() // Abstract method + fun method2() = println("Default implementation") // Default method +} + +class MyClass : MyInterface { + override fun method1() = println("Custom implementation") // Must implement + // method2() uses default implementation +} +``` + +### **2. Interface Property Implementation** +```kotlin +interface MyInterface { + val property1: String // Abstract property + val property2: String + get() = "Default value" // Default implementation +} + +class MyClass : MyInterface { + override val property1: String = "Custom value" // Must implement + // property2 uses default implementation +} +``` + +### **3. Multiple Interface Conflicts** +```kotlin +interface A { + fun method() = "A" +} + +interface B { + fun method() = "B" +} + +class MyClass : A, B { + override fun method(): String { + return super.method() + " " + super.method() // Resolve conflict + } +} +``` + +## ๐ŸŽฏ Best Practices + +### **1. Use Interfaces for Capabilities** +```kotlin +// โœ… Good - interface for capabilities +interface Flyable { + fun fly() +} + +interface Swimmable { + fun swim() +} + +class Duck : Flyable, Swimmable { + override fun fly() = println("Flying") + override fun swim() = println("Swimming") +} + +// โŒ Avoid - interface for everything +interface Animal { + val name: String + fun makeSound() + fun move() + fun eat() + fun sleep() + // Too many responsibilities +} +``` + +### **2. Keep Interfaces Focused** +```kotlin +// โœ… Good - focused interface +interface PaymentProcessor { + fun processPayment(amount: Double): Boolean + fun getPaymentInfo(): String +} + +// โŒ Avoid - unfocused interface +interface PaymentProcessor { + fun processPayment(amount: Double): Boolean + fun getPaymentInfo(): String + fun validateCard(): Boolean + fun encryptData(): String + fun sendReceipt(): Boolean + // Too many responsibilities +} +``` + +### **3. Use Default Implementations Sparingly** +```kotlin +// โœ… Good - useful default implementation +interface Logger { + fun log(message: String) + fun logError(error: String) { + log("ERROR: $error") // Useful default + } +} + +// โŒ Avoid - complex default implementation +interface Logger { + fun log(message: String) + fun logError(error: String) { + // Complex error handling logic... + val timestamp = System.currentTimeMillis() + val formattedError = "[$timestamp] ERROR: $error" + val severity = calculateSeverity(error) + log("$formattedError (Severity: $severity)") + } + + private fun calculateSeverity(error: String): String { + // Complex logic... + return "HIGH" + } +} +``` + +## ๐Ÿ”ง Practical Applications + +### **1. Plugin Architecture** +```kotlin +interface Plugin { + val name: String + val version: String + + fun initialize() + fun execute() + fun cleanup() +} + +class DatabasePlugin : Plugin { + override val name: String = "Database Plugin" + override val version: String = "1.0.0" + + override fun initialize() = println("Database plugin initialized") + override fun execute() = println("Database plugin executed") + override fun cleanup() = println("Database plugin cleaned up") +} + +class FilePlugin : Plugin { + override val name: String = "File Plugin" + override val version: String = "1.0.0" + + override fun initialize() = println("File plugin initialized") + override fun execute() = println("File plugin executed") + override fun cleanup() = println("File plugin cleaned up") +} + +class PluginManager { + private val plugins: MutableList = mutableListOf() + + fun addPlugin(plugin: Plugin) { + plugins.add(plugin) + } + + fun runAllPlugins() { + plugins.forEach { plugin -> + println("Running ${plugin.name} v${plugin.version}") + plugin.initialize() + plugin.execute() + plugin.cleanup() + println() + } + } +} +``` + +### **2. Strategy Pattern** +```kotlin +interface SortingStrategy { + fun sort(list: List): List + val name: String +} + +class BubbleSort : SortingStrategy { + override val name: String = "Bubble Sort" + override fun sort(list: List): List { + println("Using $name") + return list.sorted() + } +} + +class QuickSort : SortingStrategy { + override val name: String = "Quick Sort" + override fun sort(list: List): List { + println("Using $name") + return list.sorted() + } +} + +class Sorter(private val strategy: SortingStrategy) { + fun sort(list: List): List = strategy.sort(list) +} +``` + +### **3. Observer Pattern** +```kotlin +interface Observer { + fun update(data: String) +} + +interface Subject { + fun attach(observer: Observer) + fun detach(observer: Observer) + fun notify(data: String) +} + +class NewsAgency : Subject { + private val observers: MutableList = mutableListOf() + + override fun attach(observer: Observer) { + observers.add(observer) + } + + override fun detach(observer: Observer) { + observers.remove(observer) + } + + override fun notify(data: String) { + observers.forEach { it.update(data) } + } + + fun publishNews(news: String) { + println("News Agency: Publishing - $news") + notify(news) + } +} + +class NewsChannel(val name: String) : Observer { + override fun update(data: String) { + println("$name received: $data") + } +} +``` + +## ๐Ÿ“š Summary + +**Interfaces** in Kotlin provide: +- **Contract definition** for classes to implement +- **Multiple inheritance** capability +- **Abstraction** without implementation details +- **Default method implementations** +- **Clean separation** of concerns + +## ๐ŸŽฏ Key Takeaways + +1. **Use interfaces** for defining contracts +2. **Implement multiple interfaces** for different capabilities +3. **Use default methods** sparingly and wisely +4. **Keep interfaces focused** on specific responsibilities +5. **Interfaces enable** flexible, extensible code + +## ๐Ÿš€ Next Steps + +- Practice creating and implementing interfaces +- Learn about interface delegation +- Explore functional interfaces +- Understand interface vs abstract class usage + +--- + +**๐Ÿ“ Source Code:** [31_interface.kt](https://github.com/gpl-gowthamchand/KotlinTutorial/blob/feature/documentation-improvements/src/31_interface.kt) + +**๐Ÿ”— Related Topics:** +- [Abstract Classes](../oop/04-abstract-classes.md) +- [Polymorphism](../oop/08-polymorphism.md) +- [Inheritance](../oop/02-inheritance.md) From 09f1a8ae595a282991baef54c9e906e6fa283845 Mon Sep 17 00:00:00 2001 From: gpl-gowthamchand Date: 2025ๅนด8ๆœˆ25ๆ—ฅ 12:11:37 +0530 Subject: [PATCH 18/23] feat: Add missing documentation files for Data Class and Object Declaration --- docs/oop/10-data-class.md | 489 ++++++++++++++++++++++++++ docs/oop/11-object-declaration.md | 562 ++++++++++++++++++++++++++++++ 2 files changed, 1051 insertions(+) create mode 100644 docs/oop/10-data-class.md create mode 100644 docs/oop/11-object-declaration.md diff --git a/docs/oop/10-data-class.md b/docs/oop/10-data-class.md new file mode 100644 index 0000000..db605c6 --- /dev/null +++ b/docs/oop/10-data-class.md @@ -0,0 +1,489 @@ +# ๐Ÿ“Š Data Class in Kotlin + +Data classes in Kotlin are a special type of class designed to hold data. They automatically provide useful methods like `equals()`, `hashCode()`, `toString()`, and `copy()`, making them perfect for representing data structures, DTOs, and value objects. + +## ๐Ÿ“‹ Learning Objectives + +By the end of this lesson, you will be able to: +- โœ… Understand what data classes are in Kotlin +- โœ… Create and use data classes effectively +- โœ… Know when to use data classes vs regular classes +- โœ… Use data class features like copy and destructuring +- โœ… Apply data classes in real-world scenarios + +## ๐Ÿ” What You'll Learn + +- **Data class syntax** - How to declare and use data classes +- **Automatic methods** - Understanding generated methods +- **Copy functionality** - Using the copy method for immutability +- **Destructuring** - Extracting values from data classes +- **Best practices** - Guidelines for effective usage + +## ๐Ÿ“ Prerequisites + +- โœ… Basic understanding of classes in Kotlin +- โœ… Knowledge of constructors and properties +- โœ… Familiarity with Kotlin syntax +- โœ… Understanding of object-oriented programming + +## ๐Ÿ’ป The Code + +Let's examine the data class examples: + +**๐Ÿ“ File:** [32_data_class.kt](https://github.com/gpl-gowthamchand/KotlinTutorial/blob/feature/documentation-improvements/src/32_data_class.kt) + +## ๐Ÿ” Code Breakdown + +### **Basic Data Class Declaration** +```kotlin +data class Person( + val name: String, + val age: Int, + val email: String +) +``` + +**Key Components:** +- `data class` - Keyword that makes this a data class +- `Person` - Class name +- `val name: String` - Immutable property +- `val age: Int` - Immutable property +- `val email: String` - Immutable property + +### **Data Class Usage** +```kotlin +val person = Person("John Doe", 30, "john@example.com") +println(person) // Person(name=John Doe, age=30, email=john@example.com) +``` + +## ๐ŸŽฏ Key Concepts Explained + +### **1. What are Data Classes?** + +Data classes in Kotlin: +- **Automatically generate** useful methods +- **Are designed for data storage** rather than behavior +- **Provide immutability** by default (with `val` properties) +- **Support destructuring** for easy value extraction +- **Generate copy methods** for creating modified instances + +### **2. Automatically Generated Methods** + +| Method | Purpose | Example | +|--------|---------|---------| +| `equals()` | Compare instances | `person1 == person2` | +| `hashCode()` | Generate hash codes | `person.hashCode()` | +| `toString()` | String representation | `person.toString()` | +| `copy()` | Create modified copy | `person.copy(age = 31)` | +| `componentN()` | Destructuring support | `val (name, age) = person` | + +### **3. Data Class vs Regular Class** + +| Feature | Data Class | Regular Class | +|---------|------------|---------------| +| **Generated methods** | Yes | No | +| **Copy method** | Yes | No | +| **Destructuring** | Yes | No | +| **Inheritance** | No | Yes | +| **Custom methods** | Yes | Yes | + +## ๐Ÿงช Examples and Exercises + +### **Example 1: Basic Data Class** +```kotlin +data class Student( + val id: String, + val name: String, + val grade: Double, + val subjects: List +) + +// Usage +val student = Student("S001", "Alice Johnson", 85.5, listOf("Math", "Science", "English")) +println(student) + +// Copy with modifications +val updatedStudent = student.copy(grade = 90.0) +println(updatedStudent) + +// Destructuring +val (id, name, grade, subjects) = student +println("Student $name (ID: $id) has grade $grade in subjects: $subjects") +``` + +### **Example 2: Complex Data Class** +```kotlin +data class Address( + val street: String, + val city: String, + val state: String, + val zipCode: String, + val country: String = "USA" // Default value +) { + val fullAddress: String + get() = "$street, $city, $state $zipCode, $country" +} + +data class Employee( + val id: String, + val name: String, + val position: String, + val salary: Double, + val address: Address, + val skills: Set +) { + val isSenior: Boolean + get() = salary> 80000 + + val formattedSalary: String + get() = "$${String.format("%.2f", salary)}" +} + +// Usage +val address = Address("123 Main St", "New York", "NY", "10001") +val employee = Employee("E001", "Bob Smith", "Senior Developer", 95000.0, address, setOf("Kotlin", "Java", "Python")) + +println(employee) +println("Address: ${employee.address.fullAddress}") +println("Is Senior: ${employee.isSenior}") +println("Salary: ${employee.formattedSalary}") + +// Copy with nested modifications +val newAddress = address.copy(city = "Los Angeles", state = "CA", zipCode = "90210") +val relocatedEmployee = employee.copy(address = newAddress) +println("Relocated: $relocatedEmployee") +``` + +### **Example 3: Data Class with Collections** +```kotlin +data class Order( + val orderId: String, + val customerId: String, + val items: List, + val orderDate: String, + val status: OrderStatus +) { + val totalAmount: Double + get() = items.sumOf { it.price * it.quantity } + + val itemCount: Int + get() = items.size + + val isCompleted: Boolean + get() = status == OrderStatus.COMPLETED +} + +data class OrderItem( + val productId: String, + val productName: String, + val quantity: Int, + val price: Double +) + +enum class OrderStatus { + PENDING, PROCESSING, SHIPPED, DELIVERED, COMPLETED, CANCELLED +} + +// Usage +val items = listOf( + OrderItem("P001", "Laptop", 1, 999.99), + OrderItem("P002", "Mouse", 2, 29.99), + OrderItem("P003", "Keyboard", 1, 79.99) +) + +val order = Order("O001", "C001", items, "2024-01-15", OrderStatus.PROCESSING) + +println("Order: $order") +println("Total Amount: $${order.totalAmount}") +println("Item Count: ${order.itemCount}") +println("Status: ${order.status}") + +// Update order status +val updatedOrder = order.copy(status = OrderStatus.SHIPPED) +println("Updated Order: $updatedOrder") +``` + +### **Example 4: Data Class for API Responses** +```kotlin +data class ApiResponse( + val success: Boolean, + val data: T?, + val message: String, + val timestamp: Long = System.currentTimeMillis() +) { + val isError: Boolean + get() = !success + + val formattedTimestamp: String + get() = java.time.Instant.ofEpochMilli(timestamp).toString() +} + +data class User( + val id: String, + val username: String, + val email: String, + val profile: UserProfile +) + +data class UserProfile( + val firstName: String, + val lastName: String, + val avatar: String?, + val bio: String? +) { + val fullName: String + get() = "$firstName $lastName" +} + +// Usage +val userProfile = UserProfile("John", "Doe", "avatar.jpg", "Software Developer") +val user = User("U001", "johndoe", "john@example.com", userProfile) + +val successResponse = ApiResponse( + success = true, + data = user, + message = "User retrieved successfully" +) + +val errorResponse = ApiResponse( + success = false, + data = null, + message = "User not found" +) + +println("Success Response: $successResponse") +println("Error Response: $errorResponse") +println("User Full Name: ${user.profile.fullName}") +``` + +## โš ๏ธ Important Considerations + +### **1. Data Class Limitations** +```kotlin +// โŒ Won't work - data class cannot inherit from another class +data class MyDataClass : SomeOtherClass() { + // Error: Data class cannot have a parent class +} + +// โŒ Won't work - data class cannot be abstract +abstract data class AbstractDataClass { + // Error: Data class cannot be abstract +} + +// โŒ Won't work - data class cannot be sealed +sealed data class SealedDataClass { + // Error: Data class cannot be sealed +} + +// โŒ Won't work - data class cannot be inner +class OuterClass { + inner data class InnerDataClass { + // Error: Data class cannot be inner + } +} +``` + +### **2. Property Requirements** +```kotlin +data class Example( + val required: String, // โœ… Required parameter + val optional: String = "default" // โœ… Optional parameter with default + // var mutable: String = "mutable" // โš ๏ธ Mutable properties work but not recommended +) + +// โœ… Good - immutable properties +data class GoodExample(val name: String, val age: Int) + +// โš ๏ธ Avoid - mutable properties in data classes +data class AvoidExample(var name: String, var age: Int) +``` + +### **3. Copy Method Behavior** +```kotlin +data class Person(val name: String, val age: Int, val email: String) + +val person = Person("John", 30, "john@example.com") + +// Copy with specific changes +val olderPerson = person.copy(age = 31) // Only age changes +val newPerson = person.copy(name = "Jane", email = "jane@example.com") // Multiple changes + +// Copy without changes (creates identical copy) +val identicalPerson = person.copy() + +println("Original: $person") +println("Older: $olderPerson") +println("New: $newPerson") +println("Identical: $identicalPerson") +``` + +## ๐ŸŽฏ Best Practices + +### **1. Use Immutable Properties** +```kotlin +// โœ… Good - immutable properties +data class User(val id: String, val name: String, val email: String) + +// โŒ Avoid - mutable properties +data class User(var id: String, var name: String, var email: String) +``` + +### **2. Keep Data Classes Simple** +```kotlin +// โœ… Good - simple data class +data class Point(val x: Double, val y: Double) + +// โŒ Avoid - complex logic in data class +data class ComplexPoint(val x: Double, val y: Double) { + fun distance(other: ComplexPoint): Double { + val dx = x - other.x + val dy = y - other.y + return Math.sqrt(dx * dx + dy * dy) + } + + fun rotate(angle: Double): ComplexPoint { + val cos = Math.cos(angle) + val sin = Math.sin(angle) + return copy( + x = x * cos - y * sin, + y = x * sin + y * cos + ) + } +} +``` + +### **3. Use for Value Objects** +```kotlin +// โœ… Good - represents a value +data class Money(val amount: Double, val currency: String) + +// โŒ Avoid - represents behavior +data class Calculator(val operation: String) { + fun calculate(a: Double, b: Double): Double { + return when (operation) { + "add" -> a + b + "subtract" -> a - b + "multiply" -> a * b + "divide" -> a / b + else -> throw IllegalArgumentException("Unknown operation") + } + } +} +``` + +## ๐Ÿ”ง Practical Applications + +### **1. Configuration Objects** +```kotlin +data class DatabaseConfig( + val host: String, + val port: Int, + val database: String, + val username: String, + val password: String, + val maxConnections: Int = 10, + val timeout: Int = 30 +) { + val connectionString: String + get() = "jdbc:postgresql://$host:$port/$database" +} + +data class AppConfig( + val name: String, + val version: String, + val debug: Boolean = false, + val database: DatabaseConfig, + val api: ApiConfig +) + +data class ApiConfig( + val baseUrl: String, + val timeout: Int = 5000, + val retryAttempts: Int = 3 +) +``` + +### **2. DTOs (Data Transfer Objects)** +```kotlin +data class UserDto( + val id: String, + val username: String, + val email: String, + val firstName: String, + val lastName: String, + val isActive: Boolean, + val createdAt: String +) + +data class CreateUserRequest( + val username: String, + val email: String, + val firstName: String, + val lastName: String, + val password: String +) + +data class UpdateUserRequest( + val firstName: String? = null, + val lastName: String? = null, + val email: String? = null +) +``` + +### **3. Event Objects** +```kotlin +data class UserEvent( + val eventId: String, + val eventType: EventType, + val userId: String, + val timestamp: Long, + val data: Map +) + +enum class EventType { + USER_CREATED, USER_UPDATED, USER_DELETED, USER_LOGIN, USER_LOGOUT +} + +data class AuditLog( + val id: String, + val action: String, + val userId: String, + val resourceType: String, + val resourceId: String, + val timestamp: Long, + val details: String? +) +``` + +## ๐Ÿ“š Summary + +**Data classes** in Kotlin provide: +- **Automatic method generation** (equals, hashCode, toString, copy) +- **Immutable data structures** by default +- **Destructuring support** for easy value extraction +- **Copy functionality** for creating modified instances +- **Clean, concise syntax** for data representation + +## ๐ŸŽฏ Key Takeaways + +1. **Use `data class`** for classes that primarily hold data +2. **Properties should be immutable** (`val` not `var`) +3. **Copy method** creates new instances with modifications +4. **Destructuring** extracts values easily +5. **Keep data classes simple** and focused on data storage + +## ๐Ÿš€ Next Steps + +- Practice creating data classes for different scenarios +- Learn about data class inheritance limitations +- Explore destructuring declarations +- Understand when to use data classes vs regular classes + +--- + +**๐Ÿ“ Source Code:** [32_data_class.kt](https://github.com/gpl-gowthamchand/KotlinTutorial/blob/feature/documentation-improvements/src/32_data_class.kt) + +**๐Ÿ”— Related Topics:** +- [Classes and Constructors](../oop/01-classes-constructors.md) +- [Properties](../oop/06-properties.md) +- [Object Declaration](../oop/11-object-declaration.md) diff --git a/docs/oop/11-object-declaration.md b/docs/oop/11-object-declaration.md new file mode 100644 index 0000000..a6c63c0 --- /dev/null +++ b/docs/oop/11-object-declaration.md @@ -0,0 +1,562 @@ +# ๐ŸŽฏ Object Declaration in Kotlin + +Object declarations in Kotlin are a way to create singleton objects that are declared and initialized at the same time. They provide a convenient way to implement the Singleton pattern and organize utility functions and constants. + +## ๐Ÿ“‹ Learning Objectives + +By the end of this lesson, you will be able to: +- โœ… Understand what object declarations are in Kotlin +- โœ… Create and use object declarations effectively +- โœ… Know when to use object declarations +- โœ… Implement singleton patterns +- โœ… Apply object declarations in real-world scenarios + +## ๐Ÿ” What You'll Learn + +- **Object declaration syntax** - How to declare and use object declarations +- **Singleton pattern** - Understanding singleton implementation +- **Utility objects** - Creating objects for utility functions +- **Companion objects** - Understanding companion object relationship +- **Best practices** - Guidelines for effective usage + +## ๐Ÿ“ Prerequisites + +- โœ… Basic understanding of classes in Kotlin +- โœ… Knowledge of constructors and properties +- โœ… Familiarity with Kotlin syntax +- โœ… Understanding of object-oriented programming + +## ๐Ÿ’ป The Code + +Let's examine the object declaration examples: + +**๐Ÿ“ File:** [33_object_declaration.kt](https://github.com/gpl-gowthamchand/KotlinTutorial/blob/feature/documentation-improvements/src/33_object_declaration.kt) + +## ๐Ÿ” Code Breakdown + +### **Basic Object Declaration** +```kotlin +object DatabaseConnection { + private var connection: String? = null + + fun connect(url: String) { + connection = url + println("Connected to: $url") + } + + fun disconnect() { + connection = null + println("Disconnected") + } +} +``` + +**Key Components:** +- `object DatabaseConnection` - Object declaration +- `private var connection` - Private property +- `fun connect()` - Method for connecting +- `fun disconnect()` - Method for disconnecting + +### **Object Usage** +```kotlin +// Usage +DatabaseConnection.connect("jdbc:postgresql://localhost:5432/mydb") +DatabaseConnection.disconnect() +``` + +## ๐ŸŽฏ Key Concepts Explained + +### **1. What are Object Declarations?** + +Object declarations in Kotlin: +- **Create singleton objects** - only one instance exists +- **Are initialized lazily** - when first accessed +- **Cannot have constructors** - they're instantiated automatically +- **Can inherit from classes** - but not from other objects +- **Provide thread-safe initialization** - by default + +### **2. Object Declaration vs Class** + +| Feature | Object Declaration | Class | +|---------|-------------------|-------| +| **Instances** | Single (singleton) | Multiple | +| **Constructor** | No | Yes | +| **Inheritance** | Can inherit from class | Can inherit from class | +| **Initialization** | Lazy, thread-safe | Manual | +| **Usage** | Direct access | Instantiation required | + +### **3. Singleton Pattern Implementation** + +```kotlin +object Singleton { + private var instance: Singleton? = null + + fun getInstance(): Singleton { + if (instance == null) { + instance = Singleton + } + return instance!! + } +} + +// In Kotlin, this is automatic - no need for manual singleton pattern +``` + +## ๐Ÿงช Examples and Exercises + +### **Example 1: Utility Object** +```kotlin +object StringUtils { + fun reverse(text: String): String = text.reversed() + + fun capitalize(text: String): String = text.replaceFirstChar { it.uppercase() } + + fun countWords(text: String): Int = text.split("\\s+".toRegex()).size + + fun isPalindrome(text: String): Boolean { + val cleanText = text.lowercase().replace(Regex("[^a-z0-9]"), "") + return cleanText == cleanText.reversed() + } + + fun truncate(text: String, maxLength: Int): String { + return if (text.length <= maxLength) text else text.take(maxLength) + "..." + } +} + +// Usage +fun demonstrateStringUtils() { + val text = "Hello World from Kotlin" + + println("Original: $text") + println("Reversed: ${StringUtils.reverse(text)}") + println("Capitalized: ${StringUtils.capitalize(text)}") + println("Word count: ${StringUtils.countWords(text)}") + println("Is palindrome: ${StringUtils.isPalindrome("racecar")}") + println("Truncated: ${StringUtils.truncate(text, 15)}") +} +``` + +### **Example 2: Configuration Object** +```kotlin +object AppConfig { + val appName: String = "Kotlin Tutorial App" + val version: String = "1.0.0" + val debug: Boolean = true + + val database: DatabaseConfig = DatabaseConfig() + val api: ApiConfig = ApiConfig() + + fun getInfo(): String { + return """ + App: $appName + Version: $version + Debug: $debug + Database: ${database.getInfo()} + API: ${api.getInfo()} + """.trimIndent() + } +} + +data class DatabaseConfig( + val host: String = "localhost", + val port: Int = 5432, + val name: String = "mydb" +) { + fun getInfo(): String = "$host:$port/$name" +} + +data class ApiConfig( + val baseUrl: String = "https://api.example.com", + val timeout: Int = 5000 +) { + fun getInfo(): String = "$baseUrl (timeout: ${timeout}ms)" +} + +// Usage +fun demonstrateConfig() { + println(AppConfig.getInfo()) + + // Access specific configuration + println("Database host: ${AppConfig.database.host}") + println("API timeout: ${AppConfig.api.timeout}") +} +``` + +### **Example 3: Logger Object** +```kotlin +object Logger { + private val logs: MutableList = mutableListOf() + private var logLevel: LogLevel = LogLevel.INFO + + enum class LogLevel { + DEBUG, INFO, WARNING, ERROR + } + + data class LogEntry( + val level: LogLevel, + val message: String, + val timestamp: Long = System.currentTimeMillis() + ) + + fun setLogLevel(level: LogLevel) { + logLevel = level + } + + fun debug(message: String) = log(LogLevel.DEBUG, message) + fun info(message: String) = log(LogLevel.INFO, message) + fun warning(message: String) = log(LogLevel.WARNING, message) + fun error(message: String) = log(LogLevel.ERROR, message) + + private fun log(level: LogLevel, message: String) { + if (level.ordinal>= logLevel.ordinal) { + val entry = LogEntry(level, message) + logs.add(entry) + println("[${level.name}] $message") + } + } + + fun getLogs(): List = logs.toList() + + fun clearLogs() { + logs.clear() + } + + fun exportLogs(): String { + return logs.joinToString("\n") { entry -> + "[${entry.timestamp}] ${entry.level.name}: ${entry.message}" + } + } +} + +// Usage +fun demonstrateLogger() { + Logger.setLogLevel(Logger.LogLevel.DEBUG) + + Logger.debug("Application starting...") + Logger.info("User logged in: john@example.com") + Logger.warning("Low memory warning") + Logger.error("Database connection failed") + + println("\nAll logs:") + Logger.getLogs().forEach { println(it) } + + println("\nExported logs:") + println(Logger.exportLogs()) +} +``` + +### **Example 4: Cache Object** +```kotlin +object Cache { + private val cache: MutableMap = mutableMapOf() + private val maxSize: Int = 100 + private val defaultExpiry: Long = 300000 // 5 minutes + + data class CacheEntry( + val value: Any, + val timestamp: Long = System.currentTimeMillis(), + val expiry: Long = defaultExpiry + ) + + fun put(key: String, value: Any, expiry: Long = defaultExpiry) { + if (cache.size>= maxSize) { + cleanup() + } + cache[key] = CacheEntry(value, System.currentTimeMillis(), expiry) + } + + fun get(key: String): Any? { + val entry = cache[key] ?: return null + + if (isExpired(entry)) { + cache.remove(key) + return null + } + + return entry.value + } + + fun remove(key: String) { + cache.remove(key) + } + + fun clear() { + cache.clear() + } + + fun size(): Int = cache.size + + private fun isExpired(entry: CacheEntry): Boolean { + return System.currentTimeMillis() - entry.timestamp> entry.expiry + } + + private fun cleanup() { + val expiredKeys = cache.filter { isExpired(it.value) }.keys + expiredKeys.forEach { cache.remove(it) } + + if (cache.size>= maxSize) { + val oldestKey = cache.minByOrNull { it.value.timestamp }?.key + oldestKey?.let { cache.remove(it) } + } + } +} + +// Usage +fun demonstrateCache() { + Cache.put("user:123", "John Doe") + Cache.put("user:456", "Jane Smith", 600000) // 10 minutes expiry + + println("User 123: ${Cache.get("user:123")}") + println("User 456: ${Cache.get("user:456")}") + println("Cache size: ${Cache.size()}") + + Cache.remove("user:123") + println("After removal - User 123: ${Cache.get("user:123")}") + println("Cache size: ${Cache.size()}") +} +``` + +## โš ๏ธ Important Considerations + +### **1. Object Declaration Limitations** +```kotlin +// โŒ Won't work - object cannot have constructor +object MyObject(val param: String) { + // Error: Object cannot have constructor +} + +// โŒ Won't work - object cannot be instantiated +val obj = MyObject() // Error: Cannot create instance of object + +// โŒ Won't work - object cannot inherit from object +object ParentObject +object ChildObject : ParentObject // Error: Object cannot inherit from object + +// โœ… Will work - object can inherit from class +open class ParentClass +object ChildObject : ParentClass // This works +``` + +### **2. Thread Safety** +```kotlin +object ThreadSafeObject { + private var counter: Int = 0 + + @Synchronized + fun increment() { + counter++ + } + + fun getCount(): Int = counter +} + +// Alternative using @Volatile +object VolatileObject { + @Volatile + private var flag: Boolean = false + + fun setFlag(value: Boolean) { + flag = value + } + + fun getFlag(): Boolean = flag +} +``` + +### **3. Object vs Companion Object** +```kotlin +class MyClass { + companion object { + fun create(): MyClass = MyClass() + const val CONSTANT = "value" + } +} + +object MyObject { + fun create(): MyClass = MyClass() + const val CONSTANT = "value" +} + +// Usage differences +val obj1 = MyClass.create() // Through companion object +val obj2 = MyObject.create() // Through object declaration +``` + +## ๐ŸŽฏ Best Practices + +### **1. Use for Utility Functions** +```kotlin +// โœ… Good - utility functions +object MathUtils { + fun factorial(n: Int): Long = if (n <= 1) 1 else n * factorial(n - 1) + fun fibonacci(n: Int): Long = if (n <= 1) n else fibonacci(n - 1) + fibonacci(n - 2) +} + +// โŒ Avoid - complex business logic +object BusinessLogic { + fun processOrder(order: Order): OrderResult { + // Complex business logic... + return OrderResult() + } +} +``` + +### **2. Use for Configuration** +```kotlin +// โœ… Good - configuration +object AppConfig { + val databaseUrl: String = System.getenv("DB_URL") ?: "jdbc:default" + val maxConnections: Int = System.getenv("MAX_CONN")?.toIntOrNull() ?: 10 +} + +// โŒ Avoid - mutable configuration +object BadConfig { + var databaseUrl: String = "jdbc:default" // Mutable + var maxConnections: Int = 10 // Mutable +} +``` + +### **3. Use for Singleton Services** +```kotlin +// โœ… Good - singleton service +object DatabaseService { + private var connection: Connection? = null + + fun getConnection(): Connection { + if (connection == null || connection!!.isClosed) { + connection = createConnection() + } + return connection!! + } + + private fun createConnection(): Connection { + // Connection creation logic + return Connection() + } +} + +// โŒ Avoid - complex object with many responsibilities +object GodObject { + fun doEverything() { + // Too many responsibilities + } +} +``` + +## ๐Ÿ”ง Practical Applications + +### **1. Service Locator Pattern** +```kotlin +object ServiceLocator { + private val services: MutableMap, Any> = mutableMapOf() + + fun register(serviceClass: Class, implementation: T) { + services[serviceClass] = implementation + } + + fun get(serviceClass: Class): T { + return services[serviceClass] as T? + ?: throw IllegalStateException("Service not found: ${serviceClass.name}") + } +} + +// Usage +interface UserService { + fun getUser(id: String): User +} + +class UserServiceImpl : UserService { + override fun getUser(id: String): User = User(id, "John Doe") +} + +ServiceLocator.register(UserService::class.java, UserServiceImpl()) +val userService = ServiceLocator.get(UserService::class.java) +``` + +### **2. Factory Pattern** +```kotlin +object AnimalFactory { + fun createAnimal(type: String, name: String): Animal { + return when (type.lowercase()) { + "dog" -> Dog(name) + "cat" -> Cat(name) + "bird" -> Bird(name) + else -> throw IllegalArgumentException("Unknown animal type: $type") + } + } +} + +// Usage +val dog = AnimalFactory.createAnimal("dog", "Buddy") +val cat = AnimalFactory.createAnimal("cat", "Whiskers") +``` + +### **3. Registry Pattern** +```kotlin +object PluginRegistry { + private val plugins: MutableMap = mutableMapOf() + + fun register(name: String, plugin: Plugin) { + plugins[name] = plugin + } + + fun get(name: String): Plugin? = plugins[name] + + fun getAll(): List = plugins.values.toList() + + fun unregister(name: String) { + plugins.remove(name) + } +} + +interface Plugin { + fun execute() +} + +class DatabasePlugin : Plugin { + override fun execute() = println("Database plugin executed") +} + +class FilePlugin : Plugin { + override fun execute() = println("File plugin executed") +} + +// Usage +PluginRegistry.register("database", DatabasePlugin()) +PluginRegistry.register("file", FilePlugin()) + +PluginRegistry.getAll().forEach { it.execute() } +``` + +## ๐Ÿ“š Summary + +**Object declarations** in Kotlin provide: +- **Singleton pattern** implementation +- **Utility organization** for functions and constants +- **Thread-safe initialization** by default +- **Clean syntax** for single-instance objects +- **Configuration management** capabilities + +## ๐ŸŽฏ Key Takeaways + +1. **Use `object` keyword** for singleton declarations +2. **Objects are initialized lazily** when first accessed +3. **Objects cannot have constructors** or be instantiated +4. **Use for utilities, configuration, and services** +5. **Objects provide thread-safe initialization** + +## ๐Ÿš€ Next Steps + +- Practice creating object declarations for different scenarios +- Learn about companion objects +- Explore object expressions +- Understand when to use objects vs classes + +--- + +**๐Ÿ“ Source Code:** [33_object_declaration.kt](https://github.com/gpl-gowthamchand/KotlinTutorial/blob/feature/documentation-improvements/src/33_object_declaration.kt) + +**๐Ÿ”— Related Topics:** +- [Data Class](../oop/10-data-class.md) +- [Companion Object](../oop/12-companion-object.md) +- [Classes and Constructors](../oop/01-classes-constructors.md) From 5afce14b76c0148d103cc147dcd98802f8556210 Mon Sep 17 00:00:00 2001 From: gpl-gowthamchand Date: 2025ๅนด8ๆœˆ25ๆ—ฅ 12:17:02 +0530 Subject: [PATCH 19/23] feat: Add missing documentation file for Companion Object --- docs/oop/12-companion-object.md | 550 ++++++++++++++++++++++++++++++++ 1 file changed, 550 insertions(+) create mode 100644 docs/oop/12-companion-object.md diff --git a/docs/oop/12-companion-object.md b/docs/oop/12-companion-object.md new file mode 100644 index 0000000..189fbdd --- /dev/null +++ b/docs/oop/12-companion-object.md @@ -0,0 +1,550 @@ +# ๐ŸŽญ Companion Object in Kotlin + +Companion objects in Kotlin are a way to define static members that belong to a class rather than to instances of the class. They provide a way to organize class-level functionality and constants, similar to static members in Java. + +## ๐Ÿ“‹ Learning Objectives + +By the end of this lesson, you will be able to: +- โœ… Understand what companion objects are in Kotlin +- โœ… Create and use companion objects effectively +- โœ… Know when to use companion objects vs object declarations +- โœ… Use companion object properties and methods +- โœ… Apply companion objects in real-world scenarios + +## ๐Ÿ” What You'll Learn + +- **Companion object syntax** - How to declare and use companion objects +- **Static-like behavior** - Understanding class-level members +- **Factory methods** - Creating objects through companion objects +- **Constants and utilities** - Organizing class-level data +- **Best practices** - Guidelines for effective usage + +## ๐Ÿ“ Prerequisites + +- โœ… Basic understanding of classes in Kotlin +- โœ… Knowledge of object declarations +- โœ… Familiarity with Kotlin syntax +- โœ… Understanding of object-oriented programming + +## ๐Ÿ’ป The Code + +Let's examine the companion object examples: + +**๐Ÿ“ File:** [34_companion_object.kt](https://github.com/gpl-gowthamchand/KotlinTutorial/blob/feature/documentation-improvements/src/34_companion_object.kt) + +## ๐Ÿ” Code Breakdown + +### **Basic Companion Object Declaration** +```kotlin +class MyClass { + companion object { + const val CONSTANT = "value" + fun create(): MyClass = MyClass() + } +} +``` + +**Key Components:** +- `class MyClass` - Class declaration +- `companion object` - Companion object declaration +- `const val CONSTANT` - Constant property +- `fun create()` - Factory method + +### **Companion Object Usage** +```kotlin +// Usage +val constant = MyClass.CONSTANT +val instance = MyClass.create() +``` + +## ๐ŸŽฏ Key Concepts Explained + +### **1. What are Companion Objects?** + +Companion objects in Kotlin: +- **Belong to a class** - not to instances +- **Provide static-like behavior** - accessible without instantiation +- **Can have names** - or be anonymous +- **Support inheritance** - can implement interfaces +- **Organize class-level functionality** - constants, factory methods, utilities + +### **2. Companion Object vs Object Declaration** + +| Feature | Companion Object | Object Declaration | +|---------|------------------|-------------------| +| **Scope** | Belongs to class | Global singleton | +| **Access** | ClassName.member | ObjectName.member | +| **Inheritance** | Can implement interfaces | Can inherit from classes | +| **Usage** | Class-level members | Global utilities | +| **Naming** | Can be named or anonymous | Must have name | + +### **3. Companion Object vs Static Members** + +```kotlin +// Kotlin - Companion Object +class MyClass { + companion object { + const val CONSTANT = "value" + fun utility() = "utility" + } +} + +// Java equivalent +class MyClass { + public static final String CONSTANT = "value"; + public static String utility() { return "utility"; } +} +``` + +## ๐Ÿงช Examples and Exercises + +### **Example 1: Basic Companion Object** +```kotlin +class User private constructor( + val id: String, + val name: String, + val email: String +) { + companion object { + const val DEFAULT_ROLE = "user" + const val MAX_NAME_LENGTH = 50 + + fun create(id: String, name: String, email: String): User { + require(name.length <= MAX_NAME_LENGTH) { "Name too long" } + require(email.contains("@")) { "Invalid email" } + return User(id, name, email) + } + + fun createGuest(): User { + return User("guest_${System.currentTimeMillis()}", "Guest User", "guest@example.com") + } + + fun validateEmail(email: String): Boolean { + return email.contains("@") && email.contains(".") + } + } + + override fun toString(): String = "User(id=$id, name=$name, email=$email)" +} + +// Usage +fun demonstrateBasicCompanion() { + val user = User.create("U001", "John Doe", "john@example.com") + val guest = User.createGuest() + + println("Regular user: $user") + println("Guest user: $guest") + println("Default role: ${User.DEFAULT_ROLE}") + println("Max name length: ${User.MAX_NAME_LENGTH}") + println("Valid email: ${User.validateEmail("test@example.com")}") +} +``` + +### **Example 2: Named Companion Object** +```kotlin +class DatabaseConnection private constructor( + val host: String, + val port: Int, + val database: String +) { + companion object Factory { + const val DEFAULT_PORT = 5432 + const val DEFAULT_HOST = "localhost" + + fun create(host: String = DEFAULT_HOST, port: Int = DEFAULT_PORT, database: String): DatabaseConnection { + require(port in 1..65535) { "Invalid port number" } + require(database.isNotBlank()) { "Database name cannot be empty" } + return DatabaseConnection(host, port, database) + } + + fun createLocal(database: String): DatabaseConnection { + return create(database = database) + } + + fun createRemote(host: String, database: String): DatabaseConnection { + return create(host = host, database = database) + } + } + + fun getConnectionString(): String = "jdbc:postgresql://$host:$port/$database" +} + +// Usage +fun demonstrateNamedCompanion() { + val localDb = DatabaseConnection.Factory.createLocal("mydb") + val remoteDb = DatabaseConnection.Factory.createRemote("192.168.1.100", "production") + val customDb = DatabaseConnection.Factory.create("custom.host", 5433, "customdb") + + println("Local: ${localDb.getConnectionString()}") + println("Remote: ${remoteDb.getConnectionString()}") + println("Custom: ${customDb.getConnectionString()}") + println("Default port: ${DatabaseConnection.Factory.DEFAULT_PORT}") +} +``` + +### **Example 3: Companion Object with Interface** +```kotlin +interface Factory { + fun create(): T + fun createWithParams(vararg params: Any): T +} + +class Product private constructor( + val id: String, + val name: String, + val price: Double +) { + companion object : Factory { + const val MIN_PRICE = 0.0 + const val MAX_NAME_LENGTH = 100 + + override fun create(): Product { + return Product("P${System.currentTimeMillis()}", "Default Product", 0.0) + } + + override fun createWithParams(vararg params: Any): Product { + require(params.size>= 3) { "Need id, name, and price" } + val id = params[0] as String + val name = params[1] as String + val price = params[2] as Double + + require(price>= MIN_PRICE) { "Price cannot be negative" } + require(name.length <= MAX_NAME_LENGTH) { "Name too long" } + + return Product(id, name, price) + } + + fun createFromMap(data: Map): Product { + return createWithParams( + data["id"] ?: "P${System.currentTimeMillis()}", + data["name"] ?: "Unknown Product", + data["price"] ?: 0.0 + ) + } + } + + override fun toString(): String = "Product(id=$id, name=$name, price=$$price)" +} + +// Usage +fun demonstrateCompanionWithInterface() { + val defaultProduct = Product.create() + val customProduct = Product.createWithParams("P001", "Laptop", 999.99) + val mapProduct = Product.createFromMap(mapOf( + "id" to "P002", + "name" to "Mouse", + "price" to 29.99 + )) + + println("Default: $defaultProduct") + println("Custom: $customProduct") + println("From Map: $mapProduct") + println("Min price: ${Product.MIN_PRICE}") +} +``` + +### **Example 4: Utility Companion Object** +```kotlin +class StringProcessor { + companion object Utils { + const val DEFAULT_SEPARATOR = ", " + const val MAX_LENGTH = 1000 + + fun reverse(text: String): String = text.reversed() + + fun capitalize(text: String): String = text.replaceFirstChar { it.uppercase() } + + fun truncate(text: String, maxLength: Int = MAX_LENGTH): String { + return if (text.length <= maxLength) text else text.take(maxLength) + "..." + } + + fun join(items: List, separator: String = DEFAULT_SEPARATOR): String { + return items.joinToString(separator) + } + + fun split(text: String, separator: String = DEFAULT_SEPARATOR): List { + return text.split(separator) + } + + fun countWords(text: String): Int { + return text.split("\\s+".toRegex()).size + } + + fun isPalindrome(text: String): Boolean { + val cleanText = text.lowercase().replace(Regex("[^a-z0-9]"), "") + return cleanText == cleanText.reversed() + } + } +} + +// Usage +fun demonstrateUtilityCompanion() { + val text = "Hello World from Kotlin Programming Language" + + println("Original: $text") + println("Reversed: ${StringProcessor.Utils.reverse(text)}") + println("Capitalized: ${StringProcessor.Utils.capitalize(text)}") + println("Truncated: ${StringProcessor.Utils.truncate(text, 20)}") + println("Word count: ${StringProcessor.Utils.countWords(text)}") + println("Is palindrome: ${StringProcessor.Utils.isPalindrome("racecar")}") + + val items = listOf("apple", "banana", "cherry") + val joined = StringProcessor.Utils.join(items) + val split = StringProcessor.Utils.split(joined) + + println("Joined: $joined") + println("Split: $split") +} +``` + +## โš ๏ธ Important Considerations + +### **1. Companion Object Access** +```kotlin +class MyClass { + companion object { + const val CONSTANT = "value" + fun method() = "method" + } +} + +// โœ… Access through class name +val constant = MyClass.CONSTANT +val result = MyClass.method() + +// โœ… Access through companion object reference +val companion = MyClass.Companion +val constant2 = companion.CONSTANT +val result2 = companion.method() + +// โŒ Won't work - cannot access through instance +val instance = MyClass() +// val constant3 = instance.CONSTANT // Error +``` + +### **2. Companion Object Inheritance** +```kotlin +interface Factory { + fun create(): T +} + +class MyClass { + companion object : Factory { + override fun create(): MyClass = MyClass() + } +} + +// Usage +val instance = MyClass.create() // Through interface +val instance2 = MyClass.Companion.create() // Direct access +``` + +### **3. Companion Object vs Object Declaration** +```kotlin +// Companion Object - belongs to class +class MyClass { + companion object { + fun create(): MyClass = MyClass() + } +} + +// Object Declaration - global singleton +object MyObject { + fun create(): MyClass = MyClass() +} + +// Usage differences +val obj1 = MyClass.create() // Through companion object +val obj2 = MyObject.create() // Through object declaration +``` + +## ๐ŸŽฏ Best Practices + +### **1. Use for Factory Methods** +```kotlin +// โœ… Good - factory methods in companion object +class User private constructor(val name: String) { + companion object { + fun create(name: String): User { + require(name.isNotBlank()) { "Name cannot be empty" } + return User(name) + } + } +} + +// โŒ Avoid - factory methods in object declaration +object UserFactory { + fun create(name: String): User { + require(name.isNotBlank()) { "Name cannot be empty" } + return User(name) + } +} +``` + +### **2. Use for Constants** +```kotlin +// โœ… Good - constants in companion object +class Configuration { + companion object { + const val MAX_CONNECTIONS = 100 + const val TIMEOUT = 5000 + const val DEFAULT_HOST = "localhost" + } +} + +// โŒ Avoid - constants in object declaration +object Constants { + const val MAX_CONNECTIONS = 100 + const val TIMEOUT = 5000 + const val DEFAULT_HOST = "localhost" +} +``` + +### **3. Use for Class-Level Utilities** +```kotlin +// โœ… Good - class-specific utilities +class StringUtils { + companion object { + fun reverse(text: String): String = text.reversed() + fun capitalize(text: String): String = text.replaceFirstChar { it.uppercase() } + } +} + +// โŒ Avoid - global utilities in companion object +class SomeClass { + companion object { + fun globalUtility() = "This doesn't belong here" + } +} +``` + +## ๐Ÿ”ง Practical Applications + +### **1. Builder Pattern** +```kotlin +class DatabaseConfig private constructor( + val host: String, + val port: Int, + val database: String, + val username: String, + val password: String +) { + companion object Builder { + fun build(block: DatabaseConfigBuilder.() -> Unit): DatabaseConfig { + val builder = DatabaseConfigBuilder() + builder.block() + return builder.build() + } + } + + class DatabaseConfigBuilder { + var host: String = "localhost" + var port: Int = 5432 + var database: String = "mydb" + var username: String = "admin" + var password: String = "" + + fun build(): DatabaseConfig = DatabaseConfig(host, port, database, username, password) + } +} + +// Usage +val config = DatabaseConfig.Builder.build { + host = "192.168.1.100" + port = 5433 + database = "production" + username = "prod_user" + password = "secret123" +} +``` + +### **2. Serialization/Deserialization** +```kotlin +class User private constructor( + val id: String, + val name: String, + val email: String +) { + companion object Serializer { + fun fromJson(json: String): User { + // JSON parsing logic + val data = json.trim('{', '}').split(",") + val id = data[0].split(":")[1].trim('"') + val name = data[1].split(":")[1].trim('"') + val email = data[2].split(":")[1].trim('"') + return User(id, name, email) + } + + fun toJson(user: User): String { + return """{"id":"${user.id}","name":"${user.name}","email":"${user.email}"}""" + } + } +} + +// Usage +val user = User.Serializer.fromJson("""{"id":"U001","name":"John Doe","email":"john@example.com"}""") +val json = User.Serializer.toJson(user) +``` + +### **3. Validation and Creation** +```kotlin +class Email private constructor(val value: String) { + companion object Validator { + private val EMAIL_REGEX = Regex("^[A-Za-z0-9+_.-]+@[A-Za-z0-9.-]+\\.[A-Za-z]{2,}\$") + + fun create(email: String): Email { + require(email.isNotBlank()) { "Email cannot be empty" } + require(EMAIL_REGEX.matches(email)) { "Invalid email format" } + return Email(email) + } + + fun isValid(email: String): Boolean { + return EMAIL_REGEX.matches(email) + } + + fun getDomain(email: String): String? { + return if (isValid(email)) email.substringAfter("@") else null + } + } + + override fun toString(): String = value +} + +// Usage +val email = Email.Validator.create("user@example.com") +val isValid = Email.Validator.isValid("invalid-email") +val domain = Email.Validator.getDomain("admin@company.com") +``` + +## ๐Ÿ“š Summary + +**Companion objects** in Kotlin provide: +- **Class-level functionality** similar to static members +- **Factory methods** for object creation +- **Constants and utilities** organized within classes +- **Interface implementation** capabilities +- **Clean organization** of class-related functionality + +## ๐ŸŽฏ Key Takeaways + +1. **Use `companion object`** for class-level members +2. **Access through class name** (ClassName.member) +3. **Support factory methods** and constants +4. **Can implement interfaces** and inherit +5. **Organize class-related functionality** cleanly + +## ๐Ÿš€ Next Steps + +- Practice creating companion objects for different scenarios +- Learn about object expressions +- Explore companion object inheritance +- Understand when to use companion objects vs object declarations + +--- + +**๐Ÿ“ Source Code:** [34_companion_object.kt](https://github.com/gpl-gowthamchand/KotlinTutorial/blob/feature/documentation-improvements/src/34_companion_object.kt) + +**๐Ÿ”— Related Topics:** +- [Object Declaration](../oop/11-object-declaration.md) +- [Classes and Constructors](../oop/01-classes-constructors.md) +- [Data Class](../oop/10-data-class.md) From 445f536a0f49288487fdd71685f598e7112b7c85 Mon Sep 17 00:00:00 2001 From: gpl-gowthamchand Date: 2025ๅนด8ๆœˆ25ๆ—ฅ 13:04:08 +0530 Subject: [PATCH 20/23] docs: Add contributor section --- README.md | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 1f2c47e..5659693 100644 --- a/README.md +++ b/README.md @@ -97,4 +97,8 @@ Learn Kotlin Programming, its basics and Fundamentals from scratch. ## Authors -* **Sriyank Siddhartha** +* **Sriyank Siddhartha** - Original creator + +## Contributors + +* **GPL Gowtham chand** ([@gpl-gowthamchand](https://github.com/gpl-gowthamchand)) - Documentation, explanations, and content organization From 9e9abd3cd89bf71ae8502c3b8e07838d21ab2974 Mon Sep 17 00:00:00 2001 From: gpl-gowthamchand Date: 2025ๅนด8ๆœˆ26ๆ—ฅ 05:45:55 +0530 Subject: [PATCH 21/23] Complete documentation --- README.md | 44 +- docs/README.md | 50 +- docs/conclusion.md | 136 ++++ docs/control-flow/06-iterators.md | 421 ++++++++++++ docs/control-flow/07-labelled-loops.md | 543 ++++++++++++++++ .../05-higher-order-functions.md | 599 ++++++++++++++++++ docs/functional-programming/06-closures.md | 578 +++++++++++++++++ docs/functional-programming/07-it-keyword.md | 597 +++++++++++++++++ docs/installation.md | 142 +++++ 9 files changed, 3065 insertions(+), 45 deletions(-) create mode 100644 docs/conclusion.md create mode 100644 docs/control-flow/06-iterators.md create mode 100644 docs/control-flow/07-labelled-loops.md create mode 100644 docs/functional-programming/05-higher-order-functions.md create mode 100644 docs/functional-programming/06-closures.md create mode 100644 docs/functional-programming/07-it-keyword.md create mode 100644 docs/installation.md diff --git a/README.md b/README.md index 5659693..3c831f2 100644 --- a/README.md +++ b/README.md @@ -5,7 +5,7 @@ Learn Kotlin Programming, its basics and Fundamentals from scratch. 0. Overview - [Course introduction, prerequisites and software required](docs/basics/01-hello-world.md) 1. Installation - - Install required softwares for Windows, MAC and Linux ( Ubuntu ) + - [Install required softwares for Windows, MAC and Linux ( Ubuntu )](docs/installation.md) 2. Getting Started with Kotlin Programming - [Run your first app in Kotlin](docs/basics/01-hello-world.md) 3. Exploring Data Types and Variables @@ -14,48 +14,48 @@ Learn Kotlin Programming, its basics and Fundamentals from scratch. - [Comments](docs/basics/03-comments.md) 4. Constants, Variables and Data Types - [Data Types Deep Dive](docs/basics/04-data-types.md) - - [Ranges and Sequences](docs/basics/06-ranges.md) + - [String Interpolation](docs/basics/05-string-interpolation.md) 5. Control Flow Statements - [IF ELSE](docs/control-flow/01-if-expressions.md) - [IF Expressions](docs/control-flow/01-if-expressions.md) - [WHEN Expressions](docs/control-flow/02-when-expressions.md) 6. Loop Control Statements - - What are Iterators? + - [What are Iterators?](docs/control-flow/06-iterators.md) - [FOR Loop and how it works](docs/control-flow/03-for-loops.md) - [WHILE Loop](docs/control-flow/04-while-loops.md) - [DO WHILE Loop](docs/control-flow/04-while-loops.md) - [BREAK statements](docs/control-flow/05-break-continue.md) - [CONTINUE keyword](docs/control-flow/05-break-continue.md) - - Labelled FOR Loop + - [Labelled FOR Loop](docs/control-flow/07-labelled-loops.md) 7. Functions and Interoperability - [Declaring functions](docs/functions/01-functions-basics.md) - [Interoperability with Java code](docs/advanced/01-kotlin-interoperability.md) - - [Function as Expressions](docs/functions/02-functions-expressions.md) - - [Extension Functions](docs/functions/03-extension-functions.md) - - [Infix Functions](docs/functions/04-infix-functions.md) - - Default Parameters - - [Named Parameters](docs/functions/05-named-parameters.md) - - Tailrec Functions + - [Function as Expressions](docs/functions/02-functions-as-expressions.md) + - [Extension Functions](docs/functions/04-extension-functions.md) + - [Infix Functions](docs/functions/05-infix-functions.md) + - [Default Parameters](docs/functions/06-default-parameters.md) + - [Named Parameters](docs/functions/03-named-parameters.md) + - [Tailrec Functions](docs/functions/07-tailrec-functions.md) 8. Object Oriented Programming in Kotlin - [Defining Class and creating Objects](docs/oop/01-classes-constructors.md) - - INIT block + - [INIT block](docs/oop/05-init-block.md) - [Primary and Secondary Constructors](docs/oop/01-classes-constructors.md) - - Properties ( Field variables ) + - [Properties ( Field variables )](docs/oop/06-properties.md) - [Inheritance](docs/oop/02-inheritance.md) - [Method and Property Overriding](docs/oop/03-method-overriding.md) - - Polymorphism + - [Polymorphism](docs/oop/08-polymorphism.md) - [Abstract Class, Property and Method](docs/oop/04-abstract-classes.md) - - [Interface](docs/oop/05-interfaces.md) - - [Data Class](docs/oop/06-data-classes.md) - - Object Declaration + - [Interface](docs/oop/09-interface.md) + - [Data Class](docs/oop/10-data-class.md) + - [Object Declaration](docs/oop/11-object-declaration.md) - [Enum class](docs/oop/07-enums-sealed-classes.md) - [Sealed class](docs/oop/07-enums-sealed-classes.md) - - [Companion Object](docs/oop/07-enums-sealed-classes.md) + - [Companion Object](docs/oop/12-companion-object.md) 9. Functional Programming in Koltin - [Lambdas](docs/functional-programming/01-lambdas.md) - - [Higher-Order Functions](docs/functional-programming/01-lambdas.md) - - Closures - - 'it' keyword + - [Higher-Order Functions](docs/functional-programming/05-higher-order-functions.md) + - [Closures](docs/functional-programming/06-closures.md) + - ['it' keyword](docs/functional-programming/07-it-keyword.md) - ['with' function](docs/functional-programming/02-scope-functions.md) - ['apply' function](docs/functional-programming/02-scope-functions.md) - [let, also, run](docs/functional-programming/03-let-also-run.md) @@ -70,7 +70,7 @@ Learn Kotlin Programming, its basics and Fundamentals from scratch. - [Predicates: all, any, find, count](docs/functional-programming/04-predicates.md) 12. Kotlin NULL Safety - [Safe call](docs/null-safety/01-null-safety.md) - - with Let + - [with Let](docs/null-safety/01-null-safety.md) - [Elvis](docs/null-safety/01-null-safety.md) - [Lateinit keyword](docs/null-safety/02-lateinit-lazy.md) - [Lazy delegation and 'lateinit' vs. 'lazy'](docs/null-safety/02-lateinit-lazy.md) @@ -93,7 +93,7 @@ Learn Kotlin Programming, its basics and Fundamentals from scratch. - [lazy 'async'](docs/coroutines/02-launch-async.md) - [CoroutineScope and CoroutineContext](docs/coroutines/04-context-dispatchers.md) - [Dispatchers: Confined dispatcher, Default Dispatcher, and Unconfined Dispatcher](docs/coroutines/04-context-dispatchers.md) -15. Conclusion +15. [Conclusion](docs/conclusion.md) ## Authors diff --git a/docs/README.md b/docs/README.md index ab5f884..0ed0d71 100644 --- a/docs/README.md +++ b/docs/README.md @@ -5,7 +5,7 @@ Learn Kotlin Programming, its basics and Fundamentals from scratch. 0. Overview - [Course introduction, prerequisites and software required](basics/01-hello-world.md) 1. Installation - - Install required softwares for Windows, MAC and Linux ( Ubuntu ) + - [Install required softwares for Windows, MAC and Linux ( Ubuntu )](installation.md) 2. Getting Started with Kotlin Programming - [Run your first app in Kotlin](basics/01-hello-world.md) 3. Exploring Data Types and Variables @@ -14,48 +14,48 @@ Learn Kotlin Programming, its basics and Fundamentals from scratch. - [Comments](basics/03-comments.md) 4. Constants, Variables and Data Types - [Data Types Deep Dive](basics/04-data-types.md) - - [Ranges and Sequences](basics/06-ranges.md) + - [Ranges and Sequences](basics/05-string-interpolation.md) 5. Control Flow Statements - [IF ELSE](control-flow/01-if-expressions.md) - [IF Expressions](control-flow/01-if-expressions.md) - [WHEN Expressions](control-flow/02-when-expressions.md) 6. Loop Control Statements - - What are Iterators? + - [What are Iterators?](control-flow/06-iterators.md) - [FOR Loop and how it works](control-flow/03-for-loops.md) - [WHILE Loop](control-flow/04-while-loops.md) - [DO WHILE Loop](control-flow/04-while-loops.md) - [BREAK statements](control-flow/05-break-continue.md) - [CONTINUE keyword](control-flow/05-break-continue.md) - - Labelled FOR Loop + - [Labelled FOR Loop](control-flow/07-labelled-loops.md) 7. Functions and Interoperability - [Declaring functions](functions/01-functions-basics.md) - [Interoperability with Java code](advanced/01-kotlin-interoperability.md) - - [Function as Expressions](functions/02-functions-expressions.md) - - [Extension Functions](functions/03-extension-functions.md) - - [Infix Functions](functions/04-infix-functions.md) - - Default Parameters - - [Named Parameters](functions/05-named-parameters.md) - - Tailrec Functions + - [Function as Expressions](functions/02-functions-as-expressions.md) + - [Extension Functions](functions/04-extension-functions.md) + - [Infix Functions](functions/05-infix-functions.md) + - [Default Parameters](functions/06-default-parameters.md) + - [Named Parameters](functions/03-named-parameters.md) + - [Tailrec Functions](functions/07-tailrec-functions.md) 8. Object Oriented Programming in Kotlin - [Defining Class and creating Objects](oop/01-classes-constructors.md) - - INIT block + - [INIT block](oop/05-init-block.md) - [Primary and Secondary Constructors](oop/01-classes-constructors.md) - - Properties ( Field variables ) + - [Properties ( Field variables )](oop/06-properties.md) - [Inheritance](oop/02-inheritance.md) - [Method and Property Overriding](oop/03-method-overriding.md) - - Polymorphism + - [Polymorphism](oop/08-polymorphism.md) - [Abstract Class, Property and Method](oop/04-abstract-classes.md) - - [Interface](oop/05-interfaces.md) - - [Data Class](oop/06-data-classes.md) - - Object Declaration + - [Interface](oop/09-interface.md) + - [Data Class](oop/10-data-class.md) + - [Object Declaration](oop/11-object-declaration.md) - [Enum class](oop/07-enums-sealed-classes.md) - [Sealed class](oop/07-enums-sealed-classes.md) - - [Companion Object](oop/07-enums-sealed-classes.md) + - [Companion Object](oop/12-companion-object.md) 9. Functional Programming in Koltin - [Lambdas](functional-programming/01-lambdas.md) - - [Higher-Order Functions](functional-programming/01-lambdas.md) - - Closures - - 'it' keyword + - [Higher-Order Functions](functional-programming/05-higher-order-functions.md) + - [Closures](functional-programming/06-closures.md) + - ['it' keyword](functional-programming/07-it-keyword.md) - ['with' function](functional-programming/02-scope-functions.md) - ['apply' function](functional-programming/02-scope-functions.md) - [let, also, run](functional-programming/03-let-also-run.md) @@ -70,7 +70,7 @@ Learn Kotlin Programming, its basics and Fundamentals from scratch. - [Predicates: all, any, find, count](functional-programming/04-predicates.md) 12. Kotlin NULL Safety - [Safe call](null-safety/01-null-safety.md) - - with Let + - [with Let](null-safety/01-null-safety.md) - [Elvis](null-safety/01-null-safety.md) - [Lateinit keyword](null-safety/02-lateinit-lazy.md) - [Lazy delegation and 'lateinit' vs. 'lazy'](null-safety/02-lateinit-lazy.md) @@ -93,8 +93,12 @@ Learn Kotlin Programming, its basics and Fundamentals from scratch. - [lazy 'async'](coroutines/02-launch-async.md) - [CoroutineScope and CoroutineContext](coroutines/04-context-dispatchers.md) - [Dispatchers: Confined dispatcher, Default Dispatcher, and Unconfined Dispatcher](coroutines/04-context-dispatchers.md) -15. Conclusion +15. [Conclusion](conclusion.md) ## Authors -* **Sriyank Siddhartha** +* **Sriyank Siddhartha** - Original creator + +## Contributors + +* **GPL Gowtham chand** ([@gpl-gowthamchand](https://github.com/gpl-gowthamchand)) - Documentation, explanations, and content organization diff --git a/docs/conclusion.md b/docs/conclusion.md new file mode 100644 index 0000000..772d346 --- /dev/null +++ b/docs/conclusion.md @@ -0,0 +1,136 @@ +# Conclusion - Next Steps in Your Kotlin Journey + +Congratulations! ๐ŸŽ‰ You've completed the comprehensive Kotlin Programming Tutorial for Beginners. You've learned the fundamentals, explored advanced concepts, and built a solid foundation in Kotlin programming. + +## ๐ŸŽฏ **What You've Accomplished** + +### โœ… **Core Concepts Mastered** +- **Variables, Data Types, and Control Flow** - The building blocks of any program +- **Functions and OOP** - Creating reusable code and organizing your programs +- **Collections and Functional Programming** - Working with data efficiently +- **Null Safety** - Writing robust, crash-free code +- **Coroutines** - Asynchronous programming for modern applications + +### ๐Ÿš€ **Skills Developed** +- Writing clean, readable Kotlin code +- Understanding Kotlin's modern language features +- Applying functional programming concepts +- Building object-oriented applications +- Handling asynchronous operations with coroutines + +## ๐ŸŒŸ **Key Takeaways** + +1. **Kotlin is Modern and Safe** - Built-in null safety, smart casts, and concise syntax +2. **Interoperability** - Seamlessly works with existing Java code +3. **Functional First** - Supports both OOP and functional programming paradigms +4. **Coroutines** - Powerful asynchronous programming without callbacks +5. **Android Development** - Official language for Android app development + +## ๐Ÿ› ๏ธ **Practice Projects to Build** + +### ๐ŸŽฎ **Beginner Level** +1. **Calculator App** - Practice functions and control flow +2. **Todo List** - Work with collections and data structures +3. **Number Guessing Game** - Implement loops and user input + +### ๐Ÿ—๏ธ **Intermediate Level** +1. **Student Management System** - Apply OOP concepts +2. **File Processor** - Work with I/O and error handling +3. **Weather App** - Integrate with APIs and coroutines + +### ๐Ÿš€ **Advanced Level** +1. **Chat Application** - Real-time communication with coroutines +2. **E-commerce Platform** - Full-stack application architecture +3. **Android App** - Mobile development with Kotlin + +## ๐Ÿ“š **Continue Learning** + +### **Official Resources** +- [Kotlin Official Documentation](https://kotlinlang.org/docs/home.html) +- [Kotlin Playground](https://play.kotlinlang.org/) - Try code online +- [Kotlin Blog](https://blog.kotlin.team/) - Latest updates and tips + +### **Advanced Topics to Explore** +- **Kotlin Multiplatform** - Share code between platforms +- **Kotlin DSLs** - Domain-specific languages +- **Kotlin Native** - Compile to native binaries +- **Kotlin for Server-Side** - Backend development +- **Kotlin for Data Science** - Scientific computing + +### **Frameworks and Libraries** +- **Spring Boot** - Enterprise applications +- **Ktor** - Asynchronous web framework +- **Exposed** - Database access library +- **Arrow** - Functional programming library +- **Koin** - Dependency injection + +## ๐ŸŽ“ **Certification and Courses** + +- **JetBrains Academy** - Interactive Kotlin courses +- **Google Codelabs** - Android development with Kotlin +- **Udemy/Coursera** - Comprehensive online courses +- **Local Meetups** - Connect with Kotlin developers + +## ๐Ÿค **Join the Community** + +### **Online Communities** +- [Kotlin Slack](https://kotlinlang.slack.com/) +- [Reddit r/Kotlin](https://reddit.com/r/Kotlin) +- [Stack Overflow](https://stackoverflow.com/questions/tagged/kotlin) +- [Kotlin Forum](https://discuss.kotlinlang.org/) + +### **Open Source Contributions** +- Contribute to Kotlin itself +- Help improve documentation +- Create tutorials for others +- Build useful libraries + +## ๐Ÿ’ก **Best Practices to Remember** + +1. **Write Readable Code** - Use meaningful names and clear structure +2. **Leverage Kotlin Features** - Use data classes, extension functions, and smart casts +3. **Handle Errors Gracefully** - Use null safety and exception handling +4. **Test Your Code** - Write unit tests for your functions +5. **Keep Learning** - Technology evolves, stay updated + +## ๐ŸŽฏ **Your Next 30 Days** + +### **Week 1: Practice Fundamentals** +- Build 2-3 small projects using what you've learned +- Solve coding challenges on platforms like LeetCode or HackerRank + +### **Week 2: Explore Advanced Features** +- Deep dive into coroutines and async programming +- Experiment with functional programming concepts + +### **Week 3: Build Something Real** +- Create a project that solves a real problem +- Use external libraries and APIs + +### **Week 4: Share and Learn** +- Write a blog post about what you've learned +- Contribute to open source projects +- Help other beginners + +## ๐ŸŒŸ **Final Words** + +You've taken an important step in your programming journey. Kotlin is not just a languageโ€”it's a gateway to modern software development. Whether you're building Android apps, web services, or desktop applications, the skills you've learned here will serve you well. + +**Remember**: The best way to learn is by doing. Keep coding, keep experimenting, and don't be afraid to make mistakes. Every error is a learning opportunity. + +## ๐Ÿš€ **Ready to Launch?** + +You now have the knowledge and skills to: +- โœ… Write professional Kotlin code +- โœ… Build real-world applications +- โœ… Contribute to open source projects +- โœ… Pursue advanced Kotlin topics +- โœ… Help others learn Kotlin + +**Go forth and code amazing things!** ๐ŸŽ‰ + +--- + +*"The only way to learn a new programming language is by writing programs in it."* - Dennis Ritchie + +**Happy coding with Kotlin!** ๐Ÿš€โœจ diff --git a/docs/control-flow/06-iterators.md b/docs/control-flow/06-iterators.md new file mode 100644 index 0000000..b66405b --- /dev/null +++ b/docs/control-flow/06-iterators.md @@ -0,0 +1,421 @@ +# ๐Ÿ”„ Iterators in Kotlin + +Iterators are the foundation of collection iteration in Kotlin. They provide a way to traverse through collections one element at a time, giving you fine-grained control over how you process data. + +## ๐Ÿ“‹ Learning Objectives + +By the end of this lesson, you will be able to: +- โœ… Understand what iterators are and how they work +- โœ… Use different types of iterators (forward, backward, bidirectional) +- โœ… Create custom iterators for your own classes +- โœ… Understand the relationship between iterators and collections +- โœ… Use iterator patterns effectively in your code + +## ๐Ÿ” What You'll Learn + +- **Iterator fundamentals** - What iterators are and how they work +- **Iterator interface** - Understanding the Iterator interface +- **Collection iterators** - How collections implement iteration +- **Custom iterators** - Creating iterators for your own classes +- **Iterator patterns** - Common patterns and best practices + +## ๐Ÿ“ Prerequisites + +- Understanding of [Collections](../collections/01-arrays.md) +- Knowledge of [For Loops](03-for-loops.md) +- Familiarity with [Control Flow](01-if-expressions.md) + +## ๐Ÿ’ป The Code + +Let's examine iterators in action: + +**๐Ÿ“ File:** [12_for_loop.kt](https://github.com/gpl-gowthamchand/KotlinTutorial/blob/feature/documentation-improvements/src/12_for_loop.kt) + +## ๐Ÿ” Code Breakdown + +### **1. Understanding Iterators** + +#### **What is an Iterator?** +An iterator is an object that allows you to traverse through a collection one element at a time. It provides three main operations: +- **`hasNext()`**: Check if there are more elements +- **`next()`**: Get the next element +- **`remove()`**: Remove the last returned element (optional) + +#### **Basic Iterator Usage** +```kotlin +fun main() { + val numbers = listOf(1, 2, 3, 4, 5) + + // Get iterator from list + val iterator = numbers.iterator() + + // Manual iteration + while (iterator.hasNext()) { + val number = iterator.next() + println("Number: $number") + } +} +``` + +### **2. Iterator Interface** + +#### **Iterator Interface Definition** +```kotlin +interface Iterator { + fun hasNext(): Boolean + fun next(): T + fun remove() // Optional, not always implemented +} +``` + +#### **Using Iterator Methods** +```kotlin +fun demonstrateIterator() { + val fruits = listOf("Apple", "Banana", "Cherry") + val iterator = fruits.iterator() + + println("Iterating through fruits:") + while (iterator.hasNext()) { + val fruit = iterator.next() + println("- $fruit") + } +} +``` + +### **3. Collection Iterators** + +#### **List Iterator** +```kotlin +fun listIteratorExample() { + val numbers = listOf(10, 20, 30, 40, 50) + + // Forward iteration + println("Forward:") + val forwardIterator = numbers.listIterator() + while (forwardIterator.hasNext()) { + println("${forwardIterator.nextIndex()}: ${forwardIterator.next()}") + } + + // Backward iteration + println("\nBackward:") + val backwardIterator = numbers.listIterator(numbers.size) + while (backwardIterator.hasPrevious()) { + println("${backwardIterator.previousIndex()}: ${backwardIterator.previous()}") + } +} +``` + +#### **Mutable List Iterator** +```kotlin +fun mutableIteratorExample() { + val mutableNumbers = mutableListOf(1, 2, 3, 4, 5) + val iterator = mutableNumbers.listIterator() + + while (iterator.hasNext()) { + val number = iterator.next() + if (number % 2 == 0) { + // Remove even numbers + iterator.remove() + } + } + + println("After removing even numbers: $mutableNumbers") +} +``` + +### **4. Custom Iterators** + +#### **Creating a Custom Iterator** +```kotlin +class NumberRange(private val start: Int, private val end: Int) { + fun iterator(): Iterator = object : Iterator { + private var current = start + + override fun hasNext(): Boolean = current <= end + + override fun next(): Int { + if (!hasNext()) { + throw NoSuchElementException() + } + return current++ + } + } +} + +fun customIteratorExample() { + val range = NumberRange(1, 5) + + for (number in range) { + println("Custom range: $number") + } +} +``` + +#### **Iterator with Custom Logic** +```kotlin +class StepIterator( + private val start: Int, + private val end: Int, + private val step: Int +) : Iterator { + private var current = start + + override fun hasNext(): Boolean = current <= end + + override fun next(): Int { + if (!hasNext()) { + throw NoSuchElementException() + } + val result = current + current += step + return result + } +} + +fun stepIteratorExample() { + val stepRange = object : Iterable { + override fun iterator(): Iterator = StepIterator(0, 10, 2) + } + + for (number in stepRange) { + println("Step by 2: $number") + } +} +``` + +### **5. Iterator Patterns** + +#### **Filtering with Iterator** +```kotlin +class FilteringIterator( + private val iterator: Iterator, + private val predicate: (T) -> Boolean +) : Iterator { + private var nextElement: T? = null + private var hasNextElement = false + + override fun hasNext(): Boolean { + if (hasNextElement) return true + + while (iterator.hasNext()) { + val element = iterator.next() + if (predicate(element)) { + nextElement = element + hasNextElement = true + return true + } + } + return false + } + + override fun next(): T { + if (!hasNext()) { + throw NoSuchElementException() + } + hasNextElement = false + return nextElement!! + } +} + +fun filteringIteratorExample() { + val numbers = listOf(1, 2, 3, 4, 5, 6, 7, 8, 9, 10) + val evenNumbers = object : Iterable { + override fun iterator(): Iterator = + FilteringIterator(numbers.iterator()) { it % 2 == 0 } + } + + for (number in evenNumbers) { + println("Even number: $number") + } +} +``` + +## ๐ŸŽฏ Key Concepts Explained + +### **1. Iterator vs For Loop** + +**Iterator (Manual Control):** +```kotlin +val iterator = list.iterator() +while (iterator.hasNext()) { + val element = iterator.next() + // Process element +} +``` + +**For Loop (Automatic):** +```kotlin +for (element in list) { + // Process element +} +``` + +**Key Differences:** +- **Iterator**: Manual control, can modify during iteration +- **For Loop**: Automatic, cleaner syntax, can't modify during iteration + +### **2. Iterator Types** + +- **`Iterator`**: Basic forward-only iterator +- **`ListIterator`**: Bidirectional iterator with index access +- **`MutableIterator`**: Iterator that can modify collections +- **Custom Iterators**: User-defined iteration behavior + +### **3. When to Use Iterators** + +**Use Iterators when:** +- You need to modify the collection during iteration +- You want custom iteration logic +- You need bidirectional traversal +- You're implementing custom collection types + +**Use For Loops when:** +- You want simple, readable iteration +- You don't need to modify the collection +- You're doing standard collection traversal + +## ๐Ÿš€ Advanced Iterator Concepts + +### **1. Lazy Iteration** +```kotlin +fun fibonacciIterator(): Iterator = object : Iterator { + private var current = 0L + private var next = 1L + + override fun hasNext(): Boolean = true // Infinite sequence + + override fun next(): Long { + val result = current + current = next + next = result + next + return result + } +} + +fun lazyIterationExample() { + val fibonacci = fibonacciIterator() + + // Take first 10 Fibonacci numbers + repeat(10) { + println("Fibonacci ${it + 1}: ${fibonacci.next()}") + } +} +``` + +### **2. Chaining Iterators** +```kotlin +fun Iterator.filter(predicate: (T) -> Boolean): Iterator { + return FilteringIterator(this, predicate) +} + +fun Iterator.map(transform: (T) -> R): Iterator { + return object : Iterator { + override fun hasNext(): Boolean = this@map.hasNext() + override fun next(): R = transform(this@map.next()) + } +} + +fun chainingExample() { + val numbers = listOf(1, 2, 3, 4, 5, 6, 7, 8, 9, 10) + + val result = numbers.iterator() + .filter { it % 2 == 0 } + .map { it * it } + + for (number in result) { + println("Even squared: $number") + } +} +``` + +## ๐Ÿ’ก Best Practices + +### **1. Iterator Safety** +- Always check `hasNext()` before calling `next()` +- Don't modify collections during iteration (unless using mutable iterators) +- Handle `NoSuchElementException` appropriately + +### **2. Performance Considerations** +- Iterators are memory-efficient for large collections +- Use `forEach` for simple operations +- Consider lazy evaluation for expensive operations + +### **3. Custom Iterator Design** +- Keep iterators simple and focused +- Implement proper error handling +- Consider thread safety if needed + +## ๐Ÿ”ง Common Pitfalls + +### **1. Concurrent Modification** +```kotlin +// โŒ Wrong - Concurrent modification +val list = mutableListOf(1, 2, 3, 4, 5) +for (i in list) { + if (i % 2 == 0) { + list.remove(i) // Throws ConcurrentModificationException + } +} + +// โœ… Correct - Use iterator +val list = mutableListOf(1, 2, 3, 4, 5) +val iterator = list.listIterator() +while (iterator.hasNext()) { + val i = iterator.next() + if (i % 2 == 0) { + iterator.remove() // Safe removal + } +} +``` + +### **2. Infinite Iterators** +```kotlin +// โŒ Wrong - Infinite loop +val infiniteIterator = object : Iterator { + override fun hasNext(): Boolean = true + override fun next(): Int = 1 +} + +// Always have a termination condition +val limitedIterator = object : Iterator { + private var count = 0 + private val limit = 10 + + override fun hasNext(): Boolean = count < limit + override fun next(): Int = ++count +} +``` + +## ๐Ÿ“š Summary + +Iterators are powerful tools that give you fine-grained control over collection iteration. They're essential for: +- **Custom iteration logic** +- **Modifying collections during iteration** +- **Implementing custom collection types** +- **Advanced iteration patterns** + +## ๐ŸŽฏ Practice Exercises + +1. **Basic Iterator**: Create an iterator that counts from 1 to 10 +2. **Filter Iterator**: Build an iterator that only returns even numbers +3. **Custom Collection**: Implement a custom collection with its own iterator +4. **Bidirectional Iterator**: Create an iterator that can go both forward and backward + +## ๐Ÿ”— Related Topics + +- [For Loops](03-for-loops.md) - Automatic iteration +- [Collections](../collections/01-arrays.md) - Data structures +- [Control Flow](01-if-expressions.md) - Program flow control +- [Functions](../functions/01-functions-basics.md) - Function fundamentals + +## ๐Ÿ“– Additional Resources + +- [Official Kotlin Collections Documentation](https://kotlinlang.org/docs/collections-overview.html) +- [Iterator Interface](https://kotlinlang.org/api/latest/jvm/stdlib/kotlin.collections/-iterator/) +- [Collection Iteration](https://kotlinlang.org/docs/iterators.html) + +--- + +**Next Lesson**: [For Loops](03-for-loops.md) - Learn how for loops use iterators internally + +**Happy coding with Iterators! ๐Ÿš€** diff --git a/docs/control-flow/07-labelled-loops.md b/docs/control-flow/07-labelled-loops.md new file mode 100644 index 0000000..837b7d6 --- /dev/null +++ b/docs/control-flow/07-labelled-loops.md @@ -0,0 +1,543 @@ +# ๐Ÿท๏ธ Labelled Loops in Kotlin + +Labelled loops are a powerful feature in Kotlin that allows you to control nested loops with precision. They're especially useful when you need to break out of or continue from specific loops in complex nested structures. + +## ๐Ÿ“‹ Learning Objectives + +By the end of this lesson, you will be able to: +- โœ… Understand what labelled loops are and when to use them +- โœ… Use labels with break and continue statements +- โœ… Control nested loops effectively +- โœ… Write cleaner, more readable nested loop code +- โœ… Avoid common pitfalls with labelled loops + +## ๐Ÿ” What You'll Learn + +- **Loop labels** - How to label loops for identification +- **Labelled break** - Breaking out of specific loops +- **Labelled continue** - Continuing specific loops +- **Nested loop control** - Managing complex loop structures +- **Best practices** - When and how to use labels effectively + +## ๐Ÿ“ Prerequisites + +- Understanding of [For Loops](03-for-loops.md) +- Knowledge of [While Loops](04-while-loops.md) +- Familiarity with [Break and Continue](05-break-continue.md) + +## ๐Ÿ’ป The Code + +Let's examine labelled loops in action: + +**๐Ÿ“ File:** [15_break_keyword.kt](https://github.com/gpl-gowthamchand/KotlinTutorial/blob/feature/documentation-improvements/src/15_break_keyword.kt) + +## ๐Ÿ” Code Breakdown + +### **1. Basic Labelled Loops** + +#### **What are Labels?** +A label is an identifier followed by `@` that you can use to mark loops, expressions, or functions. When used with `break` or `continue`, it allows you to control which specific loop to break out of or continue. + +#### **Label Syntax** +```kotlin +fun main() { + // Label the outer loop + outer@ for (i in 1..3) { + for (j in 1..3) { + if (i == 2 && j == 2) { + // Break out of the outer loop + break@outer + } + println("i=$i, j=$j") + } + } + println("Outer loop finished") +} +``` + +**Output:** +``` +i=1, j=1 +i=1, j=2 +i=1, j=3 +i=2, j=1 +Outer loop finished +``` + +### **2. Labelled Break Examples** + +#### **Breaking Out of Nested Loops** +```kotlin +fun findElementExample() { + val matrix = arrayOf( + intArrayOf(1, 2, 3), + intArrayOf(4, 5, 6), + intArrayOf(7, 8, 9) + ) + + val target = 5 + var found = false + + // Label the outer loop + search@ for (row in matrix.indices) { + for (col in matrix[row].indices) { + if (matrix[row][col] == target) { + println("Found $target at position [$row][$col]") + found = true + // Break out of both loops + break@search + } + } + } + + if (!found) { + println("$target not found in matrix") + } +} +``` + +#### **Complex Nested Structure** +```kotlin +fun complexNestedExample() { + val data = listOf( + listOf(1, 2, 3), + listOf(4, 5, 6), + listOf(7, 8, 9) + ) + + process@ for (list in data) { + for (item in list) { + if (item < 0) { + println("Found negative number: $item") + // Skip processing this entire list + continue@process + } + + if (item == 0) { + println("Found zero, stopping all processing") + // Stop processing all lists + break@process + } + + println("Processing: $item") + } + println("Finished processing list") + } +} +``` + +### **3. Labelled Continue Examples** + +#### **Continuing Specific Loops** +```kotlin +fun continueExample() { + val numbers = listOf(1, 2, 3, 4, 5, 6, 7, 8, 9, 10) + + outer@ for (i in 0 until numbers.size) { + if (i % 2 == 0) { + // Skip even indices + continue@outer + } + + for (j in 0 until i) { + if (j == 3) { + // Skip to next iteration of outer loop + continue@outer + } + println("Processing i=$i, j=$j") + } + + println("Completed processing for i=$i") + } +} +``` + +#### **Matrix Processing with Continue** +```kotlin +fun matrixProcessingExample() { + val matrix = arrayOf( + intArrayOf(1, 2, 3, 4), + intArrayOf(5, 6, 7, 8), + intArrayOf(9, 10, 11, 12) + ) + + row@ for (row in matrix.indices) { + var rowSum = 0 + + for (col in matrix[row].indices) { + val value = matrix[row][col] + + if (value < 0) { + println("Skipping negative value in row $row") + // Skip this entire row + continue@row + } + + if (value == 0) { + println("Found zero in row $row, column $col") + // Skip this element but continue with the row + continue + } + + rowSum += value + } + + println("Row $row sum: $rowSum") + } +} +``` + +### **4. Advanced Labelled Loop Patterns** + +#### **Multiple Labels** +```kotlin +fun multipleLabelsExample() { + val data = listOf( + listOf(1, 2, 3), + listOf(4, 5, 6), + listOf(7, 8, 9) + ) + + outer@ for (i in data.indices) { + middle@ for (j in data[i].indices) { + inner@ for (k in 0..2) { + val value = data[i][j] + k + + when { + value < 0 -> { + println("Negative value, skip inner loop") + continue@inner + } + value> 10 -> { + println("Value too high, skip middle loop") + continue@middle + } + value == 5 -> { + println("Found target value, exit all loops") + break@outer + } + else -> { + println("Processing value: $value") + } + } + } + } + } +} +``` + +#### **Conditional Labelling** +```kotlin +fun conditionalLabellingExample() { + val shouldUseLabels = true + + if (shouldUseLabels) { + labelled@ for (i in 1..5) { + for (j in 1..5) { + if (i * j> 20) { + println("Product too large, breaking outer loop") + break@labelled + } + println("i=$i, j=$j, product=${i * j}") + } + } + } else { + // Regular nested loops without labels + for (i in 1..5) { + for (j in 1..5) { + if (i * j> 20) { + println("Product too large, breaking inner loop only") + break + } + println("i=$i, j=$j, product=${i * j}") + } + } + } +} +``` + +### **5. Real-World Examples** + +#### **Game Board Processing** +```kotlin +fun gameBoardExample() { + val board = Array(8) { Array(8) { 0 } } + + // Place some pieces + board[0][0] = 1 // White piece + board[7][7] = 2 // Black piece + + // Find all pieces of a specific type + val targetPiece = 1 + val positions = mutableListOf>() + + search@ for (row in board.indices) { + for (col in board[row].indices) { + if (board[row][col] == targetPiece) { + positions.add(Pair(row, col)) + + // If we found enough pieces, stop searching + if (positions.size>= 3) { + break@search + } + } + } + } + + println("Found ${positions.size} pieces of type $targetPiece") + positions.forEach { (row, col) -> + println("Position: [$row][$col]") + } +} +``` + +#### **Data Validation** +```kotlin +fun dataValidationExample() { + val users = listOf( + mapOf("name" to "Alice", "age" to 25, "email" to "alice@example.com"), + mapOf("name" to "Bob", "age" to -5, "email" to "invalid-email"), + mapOf("name" to "Charlie", "age" to 30, "email" to "charlie@example.com") + ) + + val validUsers = mutableListOf
    >() + + user@ for (user in users) { + // Validate name + if (user["name"] !is String || (user["name"] as String).isEmpty()) { + println("Invalid name for user: $user") + continue@user + } + + // Validate age + if (user["age"] !is Int || (user["age"] as Int) <= 0) { + println("Invalid age for user: $user") + continue@user + } + + // Validate email + if (user["email"] !is String || !(user["email"] as String).contains("@")) { + println("Invalid email for user: $user") + continue@user + } + + validUsers.add(user) + println("Valid user added: ${user["name"]}") + } + + println("Total valid users: ${validUsers.size}") +} +``` + +## ๐ŸŽฏ Key Concepts Explained + +### **1. Label Syntax** + +**Basic Label:** +```kotlin +labelName@ for (item in collection) { + // Loop body +} +``` + +**Using Label with Break:** +```kotlin +break@labelName +``` + +**Using Label with Continue:** +```kotlin +continue@labelName +``` + +### **2. When to Use Labels** + +**Use Labels when:** +- You have deeply nested loops +- You need to break out of or continue specific loops +- You want to make your code more readable +- You're dealing with complex loop logic + +**Don't Use Labels when:** +- You have simple, single loops +- The logic is straightforward +- You can refactor to avoid deep nesting + +### **3. Label Naming Conventions** + +- Use descriptive names: `outer@`, `inner@`, `search@` +- Keep names short but meaningful +- Use lowercase with descriptive words +- Avoid generic names like `loop@` or `break@` + +## ๐Ÿš€ Advanced Patterns + +### **1. Functional Alternative** +```kotlin +// Instead of labelled loops, consider functional approaches +fun functionalAlternative() { + val matrix = arrayOf( + intArrayOf(1, 2, 3), + intArrayOf(4, 5, 6), + intArrayOf(7, 8, 9) + ) + + // Find first occurrence of target + val target = 5 + val position = matrix.asSequence() + .flatMapIndexed { rowIndex, row -> + row.asSequence().mapIndexed { colIndex, value -> + Triple(rowIndex, colIndex, value) + } + } + .find { it.third == target } + + position?.let { (row, col, value) -> + println("Found $value at position [$row][$col]") + } +} +``` + +### **2. Early Return Pattern** +```kotlin +fun earlyReturnExample() { + val data = listOf( + listOf(1, 2, 3), + listOf(4, 5, 6), + listOf(7, 8, 9) + ) + + fun processData(): Boolean { + for (list in data) { + for (item in list) { + if (item < 0) { + return false // Early return instead of labelled break + } + if (item == 0) { + return true // Early return for success + } + } + } + return false + } + + val result = processData() + println("Processing result: $result") +} +``` + +## ๐Ÿ’ก Best Practices + +### **1. Keep Labels Simple** +- Use clear, descriptive names +- Don't nest too deeply (max 3-4 levels) +- Consider refactoring complex nested loops + +### **2. Use Labels Sparingly** +- Only when they significantly improve readability +- Consider functional alternatives first +- Document complex labelled loop logic + +### **3. Consistent Naming** +- Use consistent label naming across your codebase +- Follow team conventions +- Make labels self-documenting + +## ๐Ÿ”ง Common Pitfalls + +### **1. Infinite Loops with Labels** +```kotlin +// โŒ Wrong - Infinite loop +outer@ while (true) { + for (i in 1..5) { + if (i == 3) { + continue@outer // This continues the infinite while loop! + } + println(i) + } +} + +// โœ… Correct - Limited loop +outer@ for (iteration in 1..10) { + for (i in 1..5) { + if (i == 3) { + continue@outer + } + println("Iteration $iteration, value $i") + } +} +``` + +### **2. Unreachable Code** +```kotlin +// โŒ Wrong - Unreachable code after break +for (i in 1..5) { + if (i == 3) { + break + println("This will never execute") // Unreachable + } +} + +// โœ… Correct - No unreachable code +for (i in 1..5) { + if (i == 3) { + break + } + println("Processing $i") +} +``` + +### **3. Label Scope Issues** +```kotlin +// โŒ Wrong - Label not in scope +fun badExample() { + for (i in 1..5) { + if (i == 3) { + break@outer // Error: label 'outer' is not defined + } + } +} + +// โœ… Correct - Label in proper scope +fun goodExample() { + outer@ for (i in 1..5) { + if (i == 3) { + break@outer // Works correctly + } + } +} +``` + +## ๐Ÿ“š Summary + +Labelled loops are powerful tools for controlling nested loop execution. They're essential for: +- **Complex nested loop logic** +- **Precise loop control** +- **Improved code readability** +- **Efficient algorithm implementation** + +## ๐ŸŽฏ Practice Exercises + +1. **Matrix Search**: Create a function that searches a 2D matrix and breaks out of both loops when a target is found +2. **Data Processing**: Implement a data validation loop that continues to the next item when invalid data is found +3. **Game Logic**: Build a simple game loop with multiple nested loops and appropriate labels +4. **Performance Optimization**: Use labels to optimize a nested loop that processes large datasets + +## ๐Ÿ”— Related Topics + +- [For Loops](03-for-loops.md) - Basic loop structures +- [While Loops](04-while-loops.md) - Conditional loops +- [Break and Continue](05-break-continue.md) - Loop control statements +- [Collections](../collections/01-arrays.md) - Data structures for iteration + +## ๐Ÿ“– Additional Resources + +- [Official Kotlin Control Flow Documentation](https://kotlinlang.org/docs/control-flow.html) +- [Returns and Jumps](https://kotlinlang.org/docs/returns.html) +- [Loop Control Best Practices](https://kotlinlang.org/docs/returns.html#break-and-continue-labels) + +--- + +**Next Lesson**: [Functions Basics](../functions/01-functions-basics.md) - Learn about function fundamentals + +**Happy coding with Labelled Loops! ๐Ÿท๏ธ** diff --git a/docs/functional-programming/05-higher-order-functions.md b/docs/functional-programming/05-higher-order-functions.md new file mode 100644 index 0000000..9f7f7ed --- /dev/null +++ b/docs/functional-programming/05-higher-order-functions.md @@ -0,0 +1,599 @@ +# ๐Ÿš€ Higher-Order Functions in Kotlin + +Higher-order functions are a fundamental concept in functional programming that allows functions to take other functions as parameters or return functions as results. They enable powerful abstractions and make your code more flexible and reusable. + +## ๐Ÿ“‹ Learning Objectives + +By the end of this lesson, you will be able to: +- โœ… Understand what higher-order functions are and their benefits +- โœ… Create functions that take other functions as parameters +- โœ… Build functions that return functions +- โœ… Use higher-order functions for common programming patterns +- โœ… Apply functional programming principles effectively + +## ๐Ÿ” What You'll Learn + +- **Higher-order function concepts** - Functions as first-class citizens +- **Function parameters** - Passing functions to other functions +- **Function returns** - Returning functions from functions +- **Common patterns** - Map, filter, reduce, and more +- **Real-world applications** - Practical examples and use cases + +## ๐Ÿ“ Prerequisites + +- Understanding of [Functions](../functions/01-functions-basics.md) +- Knowledge of [Lambdas](01-lambdas.md) +- Familiarity with [Collections](../collections/01-arrays.md) + +## ๐Ÿ’ป The Code + +Let's examine higher-order functions in action: + +**๐Ÿ“ File:** [35_lambdas_higher_order_functions.kt](https://github.com/gpl-gowthamchand/KotlinTutorial/blob/feature/documentation-improvements/src/35_lambdas_higher_order_functions.kt) + +## ๐Ÿ” Code Breakdown + +### **1. Basic Higher-Order Functions** + +#### **What are Higher-Order Functions?** +A higher-order function is a function that either: +- Takes one or more functions as parameters, OR +- Returns a function as its result + +This makes functions "first-class citizens" in your programming language. + +#### **Function as Parameter** +```kotlin +fun main() { + // Function that takes another function as a parameter + fun executeOperation(operation: (Int, Int) -> Int, a: Int, b: Int): Int { + return operation(a, b) + } + + // Pass lambda functions as parameters + val result1 = executeOperation({ a, b -> a + b }, 5, 3) // 8 + val result2 = executeOperation({ a, b -> a * b }, 4, 6) // 24 + val result3 = executeOperation({ a, b -> a - b }, 10, 4) // 6 + + println("Addition: $result1") + println("Multiplication: $result2") + println("Subtraction: $result3") +} +``` + +#### **Function with No Parameters** +```kotlin +fun repeatAction(times: Int, action: () -> Unit) { + repeat(times) { + action() + } +} + +fun main() { + // Pass a lambda that prints "Hello!" + repeatAction(3) { println("Hello!") } + + // Pass a lambda that prints a random number + repeatAction(2) { println("Random: ${(1..100).random()}") } +} +``` + +### **2. Function as Return Value** + +#### **Returning Functions** +```kotlin +fun createMultiplier(factor: Int): (Int) -> Int { + return { number -> number * factor } +} + +fun createGreeter(greeting: String): (String) -> String { + return { name -> "$greeting, $name!" } +} + +fun main() { + // Create specific multiplier functions + val double = createMultiplier(2) + val triple = createMultiplier(3) + val quadruple = createMultiplier(4) + + println("Double of 5: ${double(5)}") // 10 + println("Triple of 5: ${triple(5)}") // 15 + println("Quadruple of 5: ${quadruple(5)}") // 20 + + // Create specific greeter functions + val helloGreeter = createGreeter("Hello") + val goodMorningGreeter = createGreeter("Good morning") + + println(helloGreeter("Alice")) // Hello, Alice! + println(goodMorningGreeter("Bob")) // Good morning, Bob! +} +``` + +#### **Complex Function Factory** +```kotlin +fun createCalculator(operation: String): (Double, Double) -> Double { + return when (operation) { + "add" -> { a, b -> a + b } + "subtract" -> { a, b -> a - b } + "multiply" -> { a, b -> a * b } + "divide" -> { a, b -> + if (b != 0.0) a / b else throw IllegalArgumentException("Division by zero") + } + else -> throw IllegalArgumentException("Unknown operation: $operation") + } +} + +fun main() { + val add = createCalculator("add") + val multiply = createCalculator("multiply") + + println("5 + 3 = ${add(5.0, 3.0)}") // 8.0 + println("5 * 3 = ${multiply(5.0, 3.0)}") // 15.0 +} +``` + +### **3. Common Higher-Order Function Patterns** + +#### **Map Pattern** +```kotlin +fun customMap(list: List, transform: (T) -> R): List { + val result = mutableListOf() + for (item in list) { + result.add(transform(item)) + } + return result +} + +fun main() { + val numbers = listOf(1, 2, 3, 4, 5) + + // Double each number + val doubled = customMap(numbers) { it * 2 } + println("Doubled: $doubled") // [2, 4, 6, 8, 10] + + // Convert to string + val strings = customMap(numbers) { "Number $it" } + println("Strings: $strings") // [Number 1, Number 2, Number 3, Number 4, Number 5] +} +``` + +#### **Filter Pattern** +```kotlin +fun customFilter(list: List, predicate: (T) -> Boolean): List { + val result = mutableListOf() + for (item in list) { + if (predicate(item)) { + result.add(item) + } + } + return result +} + +fun main() { + val numbers = listOf(1, 2, 3, 4, 5, 6, 7, 8, 9, 10) + + // Filter even numbers + val evens = customFilter(numbers) { it % 2 == 0 } + println("Even numbers: $evens") // [2, 4, 6, 8, 10] + + // Filter numbers greater than 5 + val greaterThan5 = customFilter(numbers) { it> 5 } + println("Greater than 5: $greaterThan5") // [6, 7, 8, 9, 10] +} +``` + +#### **Reduce Pattern** +```kotlin +fun customReduce(list: List, initial: T, operation: (T, T) -> T): T { + var result = initial + for (item in list) { + result = operation(result, item) + } + return result +} + +fun main() { + val numbers = listOf(1, 2, 3, 4, 5) + + // Sum all numbers + val sum = customReduce(numbers, 0) { acc, num -> acc + num } + println("Sum: $sum") // 15 + + // Find maximum + val max = customReduce(numbers, numbers.first()) { acc, num -> + if (num> acc) num else acc + } + println("Maximum: $max") // 5 +} +``` + +### **4. Advanced Higher-Order Functions** + +#### **Function Composition** +```kotlin +fun compose(f: (B) -> C, g: (A) -> B): (A) -> C { + return { a -> f(g(a)) } +} + +fun main() { + val addOne = { x: Int -> x + 1 } + val multiplyByTwo = { x: Int -> x * 2 } + val square = { x: Int -> x * x } + + // Compose functions: f(g(x)) = (x + 1) * 2 + val addOneThenMultiply = compose(multiplyByTwo, addOne) + println("addOneThenMultiply(3): ${addOneThenMultiply(3)}") // 8 + + // Compose multiple functions: square(addOne(multiplyByTwo(x))) + val complexFunction = compose(square, compose(addOne, multiplyByTwo)) + println("complexFunction(2): ${complexFunction(2)}") // 25 +} +``` + +#### **Partial Application** +```kotlin +fun partialApply(f: (A, B) -> C, a: A): (B) -> C { + return { b -> f(a, b) } +} + +fun main() { + val add = { a: Int, b: Int -> a + b } + + // Partially apply the first parameter + val addFive = partialApply(add, 5) + println("addFive(3): ${addFive(3)}") // 8 + println("addFive(7): ${addFive(7)}") // 12 + + // Create a function that always adds 10 + val addTen = partialApply(add, 10) + println("addTen(5): ${addTen(5)}") // 15 +} +``` + +#### **Currying** +```kotlin +fun curry(f: (A, B) -> C): (A) -> (B) -> C { + return { a -> { b -> f(a, b) } } +} + +fun main() { + val add = { a: Int, b: Int -> a + b } + val curriedAdd = curry(add) + + // Apply first parameter + val addFive = curriedAdd(5) + // Apply second parameter + val result = addFive(3) + + println("Result: $result") // 8 + + // Chain the calls + val result2 = curry(add)(5)(3) + println("Result 2: $result2") // 8 +} +``` + +### **5. Real-World Examples** + +#### **Event Handler System** +```kotlin +typealias EventHandler = (T) -> Unit + +class EventSystem { + private val handlers = mutableListOf>() + + fun addHandler(handler: EventHandler) { + handlers.add(handler) + } + + fun removeHandler(handler: EventHandler) { + handlers.remove(handler) + } + + fun fireEvent(event: T) { + handlers.forEach { it(event) } + } +} + +fun main() { + val eventSystem = EventSystem() + + // Add event handlers + eventSystem.addHandler { event -> println("Handler 1: $event") } + eventSystem.addHandler { event -> println("Handler 2: Processing $event") } + eventSystem.addHandler { event -> println("Handler 3: Logging $event") } + + // Fire an event + eventSystem.fireEvent("User login") +} +``` + +#### **Configuration Builder** +```kotlin +class ConfigurationBuilder { + private val config = mutableMapOf() + + fun set(key: String, value: T): ConfigurationBuilder { + config[key] = value as Any + return this + } + + fun get(key: String, defaultValue: T): T { + return config[key] as? T ?: defaultValue + } + + fun build(): Map = config.toMap() +} + +fun withConfig(block: ConfigurationBuilder.() -> Unit): Map { + val builder = ConfigurationBuilder() + builder.block() + return builder.build() +} + +fun main() { + val config = withConfig { + set("database.url", "localhost:5432") + set("database.name", "myapp") + set("server.port", 8080) + set("debug.enabled", true) + } + + println("Configuration: $config") +} +``` + +#### **Retry Mechanism** +```kotlin +fun retry( + maxAttempts: Int = 3, + delayMs: Long = 1000, + operation: () -> T +): T { + var lastException: Exception? = null + + repeat(maxAttempts) { attempt -> + try { + return operation() + } catch (e: Exception) { + lastException = e + if (attempt < maxAttempts - 1) { + println("Attempt ${attempt + 1} failed: ${e.message}") + println("Retrying in ${delayMs}ms...") + Thread.sleep(delayMs) + } + } + } + + throw lastException ?: RuntimeException("Operation failed after $maxAttempts attempts") +} + +fun main() { + try { + val result = retry(maxAttempts = 3, delayMs = 500) { + // Simulate an operation that might fail + if (Math.random() < 0.7) { + throw RuntimeException("Random failure") + } + "Success!" + } + println("Result: $result") + } catch (e: Exception) { + println("All attempts failed: ${e.message}") + } +} +``` + +## ๐ŸŽฏ Key Concepts Explained + +### **1. Function Types** + +**Basic Function Types:** +```kotlin +() -> Unit // No parameters, no return value +(Int) -> String // One parameter, returns String +(Int, String) -> Boolean // Two parameters, returns Boolean +() -> Int // No parameters, returns Int +``` + +**Generic Function Types:** +```kotlin + (T) -> T // Generic parameter and return + (T) -> R // Generic parameter and return + (List) -> T // Generic list parameter +``` + +### **2. Higher-Order Function Benefits** + +- **Reusability**: Write once, use many times +- **Flexibility**: Change behavior without changing code +- **Composability**: Combine functions in new ways +- **Testability**: Easier to test individual components +- **Readability**: More expressive and declarative code + +### **3. When to Use Higher-Order Functions** + +**Use Higher-Order Functions when:** +- You have common patterns that differ only in behavior +- You want to make your code more flexible +- You're implementing callback mechanisms +- You need to compose or combine functions +- You want to implement functional programming patterns + +**Don't Use Higher-Order Functions when:** +- The logic is simple and straightforward +- Performance is critical (function calls have overhead) +- The code becomes harder to understand +- You're over-engineering a simple solution + +## ๐Ÿš€ Advanced Patterns + +### **1. Monadic Operations** +```kotlin +sealed class Result { + data class Success(val value: T) : Result() + data class Error(val message: String) : Result() +} + +fun Result.map(transform: (T) -> R): Result { + return when (this) { + is Result.Success -> Result.Success(transform(value)) + is Result.Error -> Result.Error(message) + } +} + +fun Result.flatMap(transform: (T) -> Result): Result { + return when (this) { + is Result.Success -> transform(value) + is Result.Error -> Result.Error(message) + } +} +``` + +### **2. Builder Pattern with Functions** +```kotlin +class HTMLBuilder { + private val content = StringBuilder() + + fun tag(name: String, block: HTMLBuilder.() -> Unit) { + content.append("<$name>") + block() + content.append("") + } + + fun text(text: String) { + content.append(text) + } + + fun build(): String = content.toString() +} + +fun html(block: HTMLBuilder.() -> Unit): String { + val builder = HTMLBuilder() + builder.block() + return builder.build() +} + +fun main() { + val htmlContent = html { + tag("html") { + tag("body") { + tag("h1") { + text("Hello, World!") + } + tag("p") { + text("This is a paragraph.") + } + } + } + } + + println(htmlContent) +} +``` + +## ๐Ÿ’ก Best Practices + +### **1. Function Naming** +- Use descriptive names for function parameters +- Name functions that return functions clearly +- Use type aliases for complex function types + +### **2. Performance Considerations** +- Be aware of function call overhead +- Use inline functions when appropriate +- Consider caching frequently used functions + +### **3. Error Handling** +- Handle exceptions in higher-order functions +- Provide meaningful error messages +- Use Result types for functional error handling + +## ๐Ÿ”ง Common Pitfalls + +### **1. Function Call Overhead** +```kotlin +// โŒ Wrong - Unnecessary function calls +fun processList(list: List): List { + return list.map { it * 2 } + .filter { it> 5 } + .map { it + 1 } +} + +// โœ… Better - Single pass when possible +fun processListOptimized(list: List): List { + return list.mapNotNull { + val doubled = it * 2 + if (doubled> 5) doubled + 1 else null + } +} +``` + +### **2. Capturing Variables** +```kotlin +// โŒ Wrong - Capturing mutable variables +fun createCounter(): () -> Int { + var count = 0 + return { count++ } // Captures mutable variable +} + +// โœ… Better - Use immutable state +fun createCounter(): () -> Int { + var count = 0 + return { + count += 1 + count + } +} +``` + +### **3. Complex Function Types** +```kotlin +// โŒ Wrong - Hard to read complex types +fun process(f: ((Int) -> String) -> (String) -> Boolean): Boolean { + return f({ it.toString() })("test") +} + +// โœ… Better - Use type aliases +typealias IntToString = (Int) -> String +typealias StringToBoolean = (String) -> Boolean +typealias FunctionProcessor = (IntToString) -> StringToBoolean + +fun process(f: FunctionProcessor): Boolean { + return f({ it.toString() })("test") +} +``` + +## ๐Ÿ“š Summary + +Higher-order functions are powerful tools that enable: +- **Function composition and combination** +- **Flexible and reusable code patterns** +- **Functional programming paradigms** +- **Cleaner, more expressive code** + +## ๐ŸŽฏ Practice Exercises + +1. **Custom Collection Operations**: Implement your own map, filter, and reduce functions +2. **Function Composition**: Create a system for composing multiple functions +3. **Event System**: Build an event handling system using higher-order functions +4. **Configuration Builder**: Create a fluent configuration builder with function parameters + +## ๐Ÿ”— Related Topics + +- [Lambdas](01-lambdas.md) - Anonymous functions +- [Functions Basics](../functions/01-functions-basics.md) - Function fundamentals +- [Collections](../collections/01-arrays.md) - Data structures +- [Functional Programming](01-lambdas.md) - Functional concepts + +## ๐Ÿ“– Additional Resources + +- [Official Kotlin Functions Documentation](https://kotlinlang.org/docs/functions.html) +- [Higher-Order Functions](https://kotlinlang.org/docs/lambdas.html#higher-order-functions) +- [Function Types](https://kotlinlang.org/docs/lambdas.html#function-types) + +--- + +**Next Lesson**: [Closures](06-closures.md) - Learn about closure concepts in Kotlin + +**Happy coding with Higher-Order Functions! ๐Ÿš€** diff --git a/docs/functional-programming/06-closures.md b/docs/functional-programming/06-closures.md new file mode 100644 index 0000000..5231a2b --- /dev/null +++ b/docs/functional-programming/06-closures.md @@ -0,0 +1,578 @@ +# ๐Ÿ”’ Closures in Kotlin + +Closures are a powerful concept in functional programming where a function captures and remembers the environment in which it was created. In Kotlin, closures allow lambda expressions and anonymous functions to access variables from their enclosing scope, even after the original scope has finished executing. + +## ๐Ÿ“‹ Learning Objectives + +By the end of this lesson, you will be able to: +- โœ… Understand what closures are and how they work +- โœ… Create closures that capture variables from outer scopes +- โœ… Use closures for state management and data encapsulation +- โœ… Understand closure memory management and performance implications +- โœ… Apply closures in real-world scenarios effectively + +## ๐Ÿ” What You'll Learn + +- **Closure fundamentals** - What closures are and how they work +- **Variable capture** - How closures capture variables from outer scopes +- **State management** - Using closures to maintain state +- **Memory considerations** - Understanding closure memory management +- **Practical applications** - Real-world use cases and examples + +## ๐Ÿ“ Prerequisites + +- Understanding of [Lambdas](01-lambdas.md) +- Knowledge of [Functions](../functions/01-functions-basics.md) +- Familiarity with [Variables](../basics/02-variables-data-types.md) + +## ๐Ÿ’ป The Code + +Let's examine closures in action: + +**๐Ÿ“ File:** [37_lambdas_closures.kt](https://github.com/gpl-gowthamchand/KotlinTutorial/blob/feature/documentation-improvements/src/37_lambdas_closures.kt) + +## ๐Ÿ” Code Breakdown + +### **1. Basic Closures** + +#### **What are Closures?** +A closure is a function that captures variables from its enclosing scope. The function "remembers" the environment in which it was created, allowing it to access and modify those variables even after the original scope has finished executing. + +#### **Simple Closure Example** +```kotlin +fun main() { + val multiplier = 5 + + // This lambda captures the 'multiplier' variable from outer scope + val multiply = { x: Int -> x * multiplier } + + println("5 * 3 = ${multiply(3)}") // 15 + + // The closure still works even if we change the original variable + // (though in Kotlin, captured variables are immutable by default) +} +``` + +#### **Variable Capture in Loops** +```kotlin +fun closureInLoop() { + val functions = mutableListOf<() -> Int>() + + for (i in 1..3) { + // Each lambda captures the current value of 'i' + functions.add { i } + } + + // Execute all functions + functions.forEach { function -> + println("Function result: ${function()}") + } +} +``` + +**Output:** +``` +Function result: 1 +Function result: 2 +Function result: 3 +``` + +### **2. Advanced Closure Patterns** + +#### **Stateful Closures** +```kotlin +fun createCounter(): () -> Int { + var count = 0 // This variable is captured by the closure + + return { + count++ // The closure can modify the captured variable + count + } +} + +fun main() { + val counter1 = createCounter() + val counter2 = createCounter() + + println("Counter 1: ${counter1()}") // 1 + println("Counter 1: ${counter1()}") // 2 + println("Counter 1: ${counter1()}") // 3 + + println("Counter 2: ${counter2()}") // 1 (separate state) + println("Counter 2: ${counter2()}") // 2 +} +``` + +#### **Closure with Parameters** +```kotlin +fun createMultiplier(factor: Int): (Int) -> Int { + // The 'factor' parameter is captured by the closure + return { value -> value * factor } +} + +fun main() { + val double = createMultiplier(2) + val triple = createMultiplier(3) + + println("Double of 5: ${double(5)}") // 10 + println("Triple of 5: ${triple(5)}") // 15 + + // Each closure maintains its own captured 'factor' value +} +``` + +### **3. Closure with Complex Data** + +#### **Object Capture** +```kotlin +data class User(val name: String, var age: Int) + +fun createAgeUpdater(): (User) -> Unit { + var updateCount = 0 // Captured variable + + return { user -> + user.age += 1 + updateCount++ + println("Updated ${user.name}'s age to ${user.age} (update #$updateCount)") + } +} + +fun main() { + val alice = User("Alice", 25) + val bob = User("Bob", 30) + + val ageUpdater = createAgeUpdater() + + ageUpdater(alice) // Updated Alice's age to 26 (update #1) + ageUpdater(bob) // Updated Bob's age to 31 (update #2) + ageUpdater(alice) // Updated Alice's age to 27 (update #3) +} +``` + +#### **Collection Processing with Closures** +```kotlin +fun createFilter(predicate: (T) -> Boolean): (List) -> List { + var filterCount = 0 // Captured variable + + return { list -> + filterCount++ + val result = list.filter(predicate) + println("Filter #$filterCount: ${list.size} items -> ${result.size} items") + result + } +} + +fun main() { + val numbers = listOf(1, 2, 3, 4, 5, 6, 7, 8, 9, 10) + + val evenFilter = createFilter { it % 2 == 0 } + val oddFilter = createFilter { it % 2 != 0 } + + evenFilter(numbers) // Filter #1: 10 items -> 5 items + oddFilter(numbers) // Filter #2: 10 items -> 5 items + evenFilter(numbers) // Filter #3: 10 items -> 5 items +} +``` + +### **4. Closure Memory Management** + +#### **Understanding Closure Lifecycle** +```kotlin +fun demonstrateClosureLifecycle() { + var heavyData = "Large data string" * 1000 // Large data + + val closure = { + println("Accessing: ${heavyData.length}") // Captures heavyData + } + + // The closure keeps a reference to heavyData + println("Closure created") + + // Even after heavyData goes out of scope in the function, + // the closure still holds a reference to it + return closure +} + +fun main() { + val closure = demonstrateClosureLifecycle() + + // heavyData is still accessible through the closure + closure() // Accessing: 13000 + + // The closure and its captured data will be garbage collected + // when the closure is no longer referenced +} +``` + +#### **Avoiding Memory Leaks** +```kotlin +class DataProcessor { + private var data: String? = null + private var processor: (() -> Unit)? = null + + fun setData(newData: String) { + data = newData + } + + fun createProcessor(): () -> Unit { + // Capture only what's needed, not the entire class + val currentData = data ?: return { println("No data set") } + + return { + println("Processing: $currentData") + } + } + + fun cleanup() { + // Clear references to avoid memory leaks + data = null + processor = null + } +} + +fun main() { + val processor = DataProcessor() + processor.setData("Sample data") + + val processFunction = processor.createProcessor() + processFunction() // Processing: Sample data + + // Clean up when done + processor.cleanup() +} +``` + +### **5. Real-World Closure Examples** + +#### **Configuration Manager** +```kotlin +class ConfigurationManager { + private val config = mutableMapOf() + private val listeners = mutableListOf<(string, Any?) -> Unit>() + + fun set(key: String, value: Any) { + val oldValue = config[key] + config[key] = value + + // Notify all listeners + listeners.forEach { listener -> + listener(key, oldValue) + } + } + + fun addListener(listener: (String, Any?) -> Unit) { + listeners.add(listener) + } + + fun get(key: String): Any? = config[key] +} + +fun main() { + val configManager = ConfigurationManager() + + // Add a listener that captures the manager's state + configManager.addListener { key, oldValue -> + val newValue = configManager.get(key) + println("Config changed: $key = $oldValue -> $newValue") + } + + configManager.set("database.url", "localhost:5432") + configManager.set("server.port", 8080) +} +``` + +#### **Event Handler with State** +```kotlin +class EventHandler { + private val handlers = mutableMapOf Unit>>() + private var eventCount = 0 // State maintained by closures + + fun on(event: String, handler: () -> Unit) { + handlers.getOrPut(event) { mutableListOf() }.add(handler) + } + + fun fire(event: String) { + eventCount++ + println("Firing event '$event' (total events: $eventCount)") + + handlers[event]?.forEach { handler -> + try { + handler() + } catch (e: Exception) { + println("Handler error: ${e.message}") + } + } + } +} + +fun main() { + val eventHandler = EventHandler() + + // Each handler is a closure that can access the eventHandler's state + eventHandler.on("user.login") { + println("User logged in") + } + + eventHandler.on("user.logout") { + println("User logged out") + } + + eventHandler.fire("user.login") // Firing event 'user.login' (total events: 1) + eventHandler.fire("user.logout") // Firing event 'user.logout' (total events: 2) +} +``` + +#### **Retry Mechanism with Closure** +```kotlin +fun createRetryMechanism( + maxAttempts: Int, + delayMs: Long = 1000 +): (() -> T) -> T { + var attemptCount = 0 // Captured state + + return { operation -> + while (attemptCount < maxAttempts) { + try { + attemptCount++ + return operation() + } catch (e: Exception) { + if (attemptCount>= maxAttempts) { + throw e + } + println("Attempt $attemptCount failed: ${e.message}") + println("Retrying in ${delayMs}ms...") + Thread.sleep(delayMs) + } + } + throw RuntimeException("All attempts failed") + } +} + +fun main() { + val retry = createRetryMechanism(maxAttempts = 3, delayMs = 500) + + try { + val result = retry { + // Simulate an operation that might fail + if (Math.random() < 0.7) { + throw RuntimeException("Random failure") + } + "Success!" + } + println("Result: $result") + } catch (e: Exception) { + println("All attempts failed: ${e.message}") + } +} +``` + +## ๐ŸŽฏ Key Concepts Explained + +### **1. Closure Definition** + +**A closure is:** +- A function that captures variables from its enclosing scope +- A function that "remembers" its creation environment +- A way to maintain state between function calls +- A powerful tool for functional programming + +### **2. Variable Capture Rules** + +**In Kotlin:** +- **Local variables** are captured by value (immutable) +- **Mutable variables** can be captured and modified +- **Objects** are captured by reference +- **Primitive types** are captured by value + +### **3. Closure Benefits** + +- **State encapsulation** - Maintain state without global variables +- **Data privacy** - Control access to captured variables +- **Function composition** - Combine functions with shared state +- **Event handling** - Maintain context across event calls + +## ๐Ÿš€ Advanced Closure Patterns + +### **1. Closure Factories** +```kotlin +fun createClosureFactory(initialValue: T): () -> T { + var value = initialValue + + return { value } +} + +fun createMutableClosureFactory(initialValue: T): () -> T { + var value = initialValue + + return { + value = value // This creates a mutable closure + value + } +} +``` + +### **2. Closure Composition** +```kotlin +fun composeClosures(vararg closures: (T) -> T): (T) -> T { + return { value -> + var result = value + closures.forEach { closure -> + result = closure(result) + } + result + } +} + +fun main() { + val addOne = { x: Int -> x + 1 } + val double = { x: Int -> x * 2 } + val square = { x: Int -> x * x } + + val composed = composeClosures(addOne, double, square) + println("Result: ${composed(2)}") // ((2 + 1) * 2)2 = 36 +} +``` + +### **3. Closure with Generics** +```kotlin +class ClosureContainer(private val initialValue: T) { + private var value = initialValue + + fun createGetter(): () -> T = { value } + + fun createSetter(): (T) -> Unit = { newValue -> value = newValue } + + fun createUpdater(update: (T) -> T): () -> T = { + value = update(value) + value + } +} + +fun main() { + val container = ClosureContainer("Hello") + + val getter = container.createGetter() + val setter = container.createSetter() + val updater = container.createUpdater { it + " World" } + + println(getter()) // Hello + setter("Hi") + println(getter()) // Hi + println(updater()) // Hi World +} +``` + +## ๐Ÿ’ก Best Practices + +### **1. Minimize Captured Variables** +- Only capture what's necessary +- Avoid capturing large objects unnecessarily +- Use local variables when possible + +### **2. Memory Management** +- Be aware of what your closures capture +- Clear references when closures are no longer needed +- Use weak references for long-lived closures + +### **3. Closure Design** +- Keep closures focused and single-purpose +- Document what variables are captured +- Consider the lifecycle of captured variables + +## ๐Ÿ”ง Common Pitfalls + +### **1. Capturing Mutable State** +```kotlin +// โŒ Wrong - Capturing mutable state can lead to unexpected behavior +fun createCounter(): () -> Int { + var count = 0 + return { count++ } +} + +// โœ… Better - Be explicit about state management +fun createCounter(): () -> Int { + var count = 0 + return { + count += 1 + count + } +} +``` + +### **2. Memory Leaks** +```kotlin +// โŒ Wrong - Capturing entire objects can cause memory leaks +class HeavyObject { + val data = "Large data" * 1000 + + fun createClosure(): () -> Unit { + return { println(data) } // Captures entire object + } +} + +// โœ… Better - Capture only what's needed +class HeavyObject { + val data = "Large data" * 1000 + + fun createClosure(): () -> Unit { + val dataLength = data.length // Capture only the length + return { println("Data length: $dataLength") } + } +} +``` + +### **3. Closure in Loops** +```kotlin +// โŒ Wrong - Common mistake with closures in loops +fun wrongWay() { + val functions = mutableListOf<() -> Int>() + for (i in 1..3) { + functions.add { i } // All closures capture the same 'i' + } + // All functions return 3! +} + +// โœ… Correct - Use local variables +fun correctWay() { + val functions = mutableListOf<() -> Int>() + for (i in 1..3) { + val localI = i // Create local copy + functions.add { localI } + } + // Functions return 1, 2, 3 as expected +} +``` + +## ๐Ÿ“š Summary + +Closures are powerful tools that enable: +- **State management** without global variables +- **Function composition** with shared context +- **Event handling** with persistent state +- **Functional programming** patterns + +## ๐ŸŽฏ Practice Exercises + +1. **Stateful Counter**: Create a closure that maintains a counter state +2. **Configuration Manager**: Build a system that uses closures to manage configuration changes +3. **Event Handler**: Implement an event system using closures for state management +4. **Closure Factory**: Create a factory that generates closures with different behaviors + +## ๐Ÿ”— Related Topics + +- [Lambdas](01-lambdas.md) - Anonymous functions +- [Higher-Order Functions](05-higher-order-functions.md) - Functions that take/return functions +- [Functions Basics](../functions/01-functions-basics.md) - Function fundamentals +- [Variables](../basics/02-variables-data-types.md) - Variable concepts + +## ๐Ÿ“– Additional Resources + +- [Official Kotlin Lambdas Documentation](https://kotlinlang.org/docs/lambdas.html) +- [Closure Concepts](https://kotlinlang.org/docs/lambdas.html#closures) +- [Function References](https://kotlinlang.org/docs/reflection.html#function-references) + +--- + +**Next Lesson**: ['it' keyword](07-it-keyword.md) - Learn about the 'it' keyword in lambdas + +**Happy coding with Closures! ๐Ÿ”’** diff --git a/docs/functional-programming/07-it-keyword.md b/docs/functional-programming/07-it-keyword.md new file mode 100644 index 0000000..350e204 --- /dev/null +++ b/docs/functional-programming/07-it-keyword.md @@ -0,0 +1,597 @@ +# ๐Ÿ”‘ The 'it' Keyword in Kotlin + +The `it` keyword is a special implicit parameter name in Kotlin that makes lambda expressions more concise and readable. It's automatically available when a lambda has only one parameter, eliminating the need to explicitly name the parameter. + +## ๐Ÿ“‹ Learning Objectives + +By the end of this lesson, you will be able to: +- โœ… Understand what the 'it' keyword is and when to use it +- โœ… Use 'it' effectively in single-parameter lambdas +- โœ… Know when to use 'it' vs explicit parameter names +- โœ… Apply 'it' in collection operations and other contexts +- โœ… Write cleaner, more readable lambda expressions + +## ๐Ÿ” What You'll Learn + +- **'it' keyword fundamentals** - What 'it' is and how it works +- **Single parameter lambdas** - Using 'it' for concise syntax +- **Collection operations** - 'it' in map, filter, forEach, etc. +- **Best practices** - When to use 'it' vs explicit names +- **Common patterns** - Real-world examples and use cases + +## ๐Ÿ“ Prerequisites + +- Understanding of [Lambdas](01-lambdas.md) +- Knowledge of [Collections](../collections/01-arrays.md) +- Familiarity with [Functions](../functions/01-functions-basics.md) + +## ๐Ÿ’ป The Code + +Let's examine the 'it' keyword in action: + +**๐Ÿ“ File:** [38_it_keyword_lambdas.kt](https://github.com/gpl-gowthamchand/KotlinTutorial/blob/feature/documentation-improvements/src/38_it_keyword_lambdas.kt) + +## ๐Ÿ” Code Breakdown + +### **1. Basic 'it' Usage** + +#### **What is 'it'?** +The `it` keyword is an implicit parameter name that Kotlin automatically provides when a lambda has exactly one parameter. It makes your code more concise by eliminating the need to explicitly name the parameter. + +#### **Simple 'it' Examples** +```kotlin +fun main() { + val numbers = listOf(1, 2, 3, 4, 5) + + // Without 'it' - explicit parameter naming + val doubled1 = numbers.map { number -> number * 2 } + + // With 'it' - implicit parameter naming + val doubled2 = numbers.map { it * 2 } + + // Both produce the same result + println("Doubled 1: $doubled1") // [2, 4, 6, 8, 10] + println("Doubled 2: $doubled2") // [2, 4, 6, 8, 10] +} +``` + +#### **'it' in Different Contexts** +```kotlin +fun demonstrateIt() { + val names = listOf("Alice", "Bob", "Charlie") + + // forEach with 'it' + names.forEach { println("Hello, $it!") } + + // filter with 'it' + val longNames = names.filter { it.length> 4 } + println("Long names: $longNames") // [Alice, Charlie] + + // map with 'it' + val nameLengths = names.map { it.length } + println("Name lengths: $nameLengths") // [5, 3, 7] + + // any with 'it' + val hasShortName = names.any { it.length <= 3 } + println("Has short name: $hasShortName") // true +} +``` + +### **2. 'it' in Collection Operations** + +#### **List Operations** +```kotlin +fun listOperations() { + val numbers = listOf(1, 2, 3, 4, 5, 6, 7, 8, 9, 10) + + // Filter even numbers + val evens = numbers.filter { it % 2 == 0 } + println("Even numbers: $evens") // [2, 4, 6, 8, 10] + + // Map to squares + val squares = numbers.map { it * it } + println("Squares: $squares") // [1, 4, 9, 16, 25, 36, 49, 64, 81, 100] + + // Find first number greater than 5 + val firstLarge = numbers.find { it> 5 } + println("First number> 5: $firstLarge") // 6 + + // Count numbers divisible by 3 + val divisibleBy3 = numbers.count { it % 3 == 0 } + println("Divisible by 3: $divisibleBy3") // 3 +} +``` + +#### **String Operations** +```kotlin +fun stringOperations() { + val words = listOf("hello", "world", "kotlin", "programming", "language") + + // Filter words starting with specific letters + val hWords = words.filter { it.startsWith("h") } + println("Words starting with 'h': $hWords") // [hello] + + // Map to uppercase + val upperWords = words.map { it.uppercase() } + println("Uppercase words: $upperWords") // [HELLO, WORLD, KOTLIN, PROGRAMMING, LANGUAGE] + + // Find longest word + val longest = words.maxByOrNull { it.length } + println("Longest word: $longest") // programming + + // Filter words with length> 5 + val longWords = words.filter { it.length> 5 } + println("Long words: $longWords") // [programming, language] +} +``` + +#### **Object Operations** +```kotlin +data class Person(val name: String, val age: Int, val city: String) + +fun objectOperations() { + val people = listOf( + Person("Alice", 25, "New York"), + Person("Bob", 30, "Los Angeles"), + Person("Charlie", 35, "Chicago"), + Person("Diana", 28, "New York") + ) + + // Filter people by age + val youngPeople = people.filter { it.age < 30 } + println("Young people: ${youngPeople.map { it.name }}") // [Alice, Diana] + + // Map to names only + val names = people.map { it.name } + println("Names: $names") // [Alice, Bob, Charlie, Diana] + + // Find people from specific city + val newYorkers = people.filter { it.city == "New York" } + println("New Yorkers: ${newYorkers.map { it.name }}") // [Alice, Diana] + + // Group by city + val byCity = people.groupBy { it.city } + println("People by city: $byCity") +} +``` + +### **3. Advanced 'it' Usage** + +#### **Chaining Operations** +```kotlin +fun chainingOperations() { + val numbers = listOf(1, 2, 3, 4, 5, 6, 7, 8, 9, 10) + + // Chain multiple operations with 'it' + val result = numbers + .filter { it % 2 == 0 } // Keep even numbers + .map { it * it } // Square them + .filter { it> 20 } // Keep squares> 20 + .sum() // Sum the results + + println("Result: $result") // 36 +ใŸใ™ 64 +ใŸใ™ 100 =ใ‚ 200 + + // More complex chaining + val processed = numbers + .filter { it> 5 } + .map { it * 2 } + .filter { it % 3 == 0 } + .map { "Number $it" } + + println("Processed: $processed") // [Number 12, Number 18, Number 24, Number 30] +} +``` + +#### **'it' with Custom Functions** +```kotlin +fun customOperations() { + val numbers = listOf(1, 2, 3, 4, 5) + + // Custom function that takes a lambda + fun processNumbers( + list: List, + processor: (Int) -> Int + ): List { + return list.map { processor(it) } + } + + // Use 'it' in the lambda passed to custom function + val doubled = processNumbers(numbers) { it * 2 } + val incremented = processNumbers(numbers) { it + 1 } + + println("Doubled: $doubled") // [2, 4, 6, 8, 10] + println("Incremented: $incremented") // [2, 3, 4, 5, 6] +} +``` + +#### **'it' in Higher-Order Functions** +```kotlin +fun higherOrderWithIt() { + // Function that returns a lambda using 'it' + fun createMultiplier(factor: Int): (Int) -> Int { + return { it * factor } // 'it' refers to the Int parameter + } + + // Function that takes a lambda and uses 'it' + fun applyOperation( + value: Int, + operation: (Int) -> Int + ): Int { + return operation(value) + } + + val double = createMultiplier(2) + val triple = createMultiplier(3) + + println("Double of 5: ${applyOperation(5) { it * 2 }}") // 10 + println("Triple of 5: ${applyOperation(5) { it * 3 }}") // 15 + println("Using double function: ${double(5)}") // 10 +} +``` + +### **4. When NOT to Use 'it'** + +#### **Multiple Parameters** +```kotlin +fun multipleParameters() { + val pairs = listOf( + Pair("Alice", 25), + Pair("Bob", 30), + Pair("Charlie", 35) + ) + + // โŒ Wrong - 'it' only works for single parameters + // val result = pairs.map { it.first + " is " + it.second } + + // โœ… Correct - Use explicit parameter names + val result = pairs.map { (name, age) -> "$name is $age" } + println("Result: $result") // [Alice is 25, Bob is 30, Charlie is 35] + + // Alternative with explicit parameter names + val result2 = pairs.map { pair -> "${pair.first} is ${pair.second}" } + println("Result 2: $result2") +} +``` + +#### **Complex Logic** +```kotlin +fun complexLogic() { + val numbers = listOf(1, 2, 3, 4, 5) + + // โŒ Wrong - 'it' makes complex logic hard to read + // val result = numbers.map { if (it % 2 == 0) it * 2 else it * 3 } + + // โœ… Better - Use explicit parameter name for clarity + val result = numbers.map { number -> + if (number % 2 == 0) { + number * 2 + } else { + number * 3 + } + } + println("Result: $result") // [3, 4, 9, 8, 15] +} +``` + +#### **Nested Lambdas** +```kotlin +fun nestedLambdas() { + val matrix = listOf( + listOf(1, 2, 3), + listOf(4, 5, 6), + listOf(7, 8, 9) + ) + + // โŒ Wrong - Multiple 'it' references can be confusing + // val flattened = matrix.flatMap { it.map { it * 2 } } + + // โœ… Better - Use explicit names for clarity + val flattened = matrix.flatMap { row -> + row.map { element -> element * 2 } + } + println("Flattened: $flattened") // [2, 4, 6, 8, 10, 12, 14, 16, 18] +} +``` + +### **5. Real-World Examples** + +#### **Data Processing Pipeline** +```kotlin +fun dataProcessingPipeline() { + data class User(val id: Int, val name: String, val age: Int, val active: Boolean) + + val users = listOf( + User(1, "Alice", 25, true), + User(2, "Bob", 30, false), + User(3, "Charlie", 35, true), + User(4, "Diana", 28, true), + User(5, "Eve", 22, false) + ) + + // Process users with 'it' for simple operations + val activeUserNames = users + .filter { it.active } // Keep active users + .filter { it.age>= 25 } // Keep users 25+ + .map { it.name } // Extract names + .sorted() // Sort alphabetically + + println("Active users 25+: $activeUserNames") // [Alice, Charlie, Diana] + + // More complex processing + val userSummary = users + .filter { it.active } + .map { "${it.name} (${it.age})" } + .joinToString(", ") + + println("Active users: $userSummary") // Alice (25), Charlie (35), Diana (28) +} +``` + +#### **Configuration Processing** +```kotlin +fun configurationProcessing() { + data class Config( + val key: String, + val value: String, + val required: Boolean = false + ) + + val configs = listOf( + Config("database.url", "localhost:5432", true), + Config("database.name", "myapp", true), + Config("server.port", "8080", false), + Config("debug.enabled", "false", false) + ) + + // Process configuration with 'it' + val requiredConfigs = configs + .filter { it.required } + .map { "${it.key}=${it.value}" } + + println("Required configs: $requiredConfigs") + + // Validate configuration + val missingRequired = configs + .filter { it.required } + .any { it.value.isBlank() } + + if (missingRequired) { + println("Warning: Some required configurations are missing!") + } +} +``` + +#### **API Response Processing** +```kotlin +fun apiResponseProcessing() { + data class ApiResponse( + val success: Boolean, + val data: List?, + val error: String? = null + ) + + val responses = listOf( + ApiResponse(true, listOf("user1", "user2")), + ApiResponse(false, null, "Network error"), + ApiResponse(true, listOf("user3")), + ApiResponse(false, null, "Authentication failed") + ) + + // Process successful responses + val successfulData = responses + .filter { it.success } + .flatMap { it.data ?: emptyList() } + + println("Successful data: $successfulData") // [user1, user2, user3] + + // Process error responses + val errors = responses + .filter { !it.success } + .map { it.error ?: "Unknown error" } + + println("Errors: $errors") // [Network error, Authentication failed] +} +``` + +## ๐ŸŽฏ Key Concepts Explained + +### **1. When 'it' is Available** + +**'it' is available when:** +- The lambda has exactly one parameter +- The parameter type can be inferred +- You want concise, readable code + +**'it' is NOT available when:** +- The lambda has multiple parameters +- The lambda has no parameters +- You need explicit parameter names for clarity + +### **2. 'it' vs Explicit Names** + +**Use 'it' when:** +- The lambda is simple and clear +- The parameter meaning is obvious from context +- You want concise code +- The operation is straightforward + +**Use explicit names when:** +- The lambda logic is complex +- The parameter meaning isn't clear +- You have nested lambdas +- You want to improve readability + +### **3. 'it' Best Practices** + +- **Keep it simple**: Use 'it' for straightforward operations +- **Avoid confusion**: Don't use 'it' when it makes code harder to read +- **Be consistent**: Use the same approach throughout your codebase +- **Consider readability**: Choose clarity over brevity when needed + +## ๐Ÿš€ Advanced Patterns + +### **1. 'it' with Extension Functions** +```kotlin +fun extensionWithIt() { + // Extension function that takes a lambda + fun T.process(operation: (T) -> T): T { + return operation(this) + } + + val result = "hello" + .process { it.uppercase() } + .process { it.reversed() } + .process { "Result: $it" } + + println(result) // Result: OLLEH +} +``` + +### **2. 'it' in DSLs** +```kotlin +fun dslWithIt() { + class HTMLBuilder { + private val content = StringBuilder() + + fun tag(name: String, block: HTMLBuilder.() -> Unit) { + content.append("<$name>") + block() + content.append("") + } + + fun text(text: String) { + content.append(text) + } + + fun build(): String = content.toString() + } + + fun html(block: HTMLBuilder.() -> Unit): String { + val builder = HTMLBuilder() + builder.block() + return builder.build() + } + + val htmlContent = html { + tag("body") { + tag("h1") { + text("Hello, World!") + } + } + } + + println(htmlContent) //

    Hello, World!

    +} +``` + +### **3. 'it' with Sequences** +```kotlin +fun sequenceWithIt() { + val numbers = (1..1000).asSequence() + + val result = numbers + .filter { it % 2 == 0 } + .map { it * it } + .filter { it> 1000 } + .take(5) + .toList() + + println("Result: $result") // [1024, 1156, 1296, 1444, 1600] +} +``` + +## ๐Ÿ’ก Best Practices + +### **1. Readability First** +- Use 'it' when it makes code more readable +- Avoid 'it' when it creates confusion +- Consider your team's coding standards + +### **2. Consistency** +- Use the same approach throughout your project +- Document your 'it' usage patterns +- Follow established conventions + +### **3. Performance Considerations** +- 'it' has no performance impact +- Use 'it' for simple operations +- Consider explicit names for complex logic + +## ๐Ÿ”ง Common Pitfalls + +### **1. Overusing 'it'** +```kotlin +// โŒ Wrong - Too many 'it' references can be confusing +val result = data + .filter { it.isValid() } + .map { it.process() } + .filter { it.isReady() } + .map { it.finalize() } + +// โœ… Better - Use explicit names for clarity +val result = data + .filter { item -> item.isValid() } + .map { item -> item.process() } + .filter { item -> item.isReady() } + .map { item -> item.finalize() } +``` + +### **2. Nested 'it' Confusion** +```kotlin +// โŒ Wrong - Nested 'it' references are confusing +val result = list.flatMap { it.map { it.toString() } } + +// โœ… Better - Use explicit names +val result = list.flatMap { sublist -> + sublist.map { element -> element.toString() } +} +``` + +### **3. Complex Logic with 'it'** +```kotlin +// โŒ Wrong - Complex logic with 'it' is hard to read +val result = items.map { if (it.condition) it.processA() else it.processB() } + +// โœ… Better - Use explicit parameter name +val result = items.map { item -> + if (item.condition) { + item.processA() + } else { + item.processB() + } +} +``` + +## ๐Ÿ“š Summary + +The `it` keyword is a powerful tool that: +- **Simplifies lambda expressions** for single parameters +- **Improves code readability** in simple cases +- **Reduces boilerplate** in collection operations +- **Enhances functional programming** style + +## ๐ŸŽฏ Practice Exercises + +1. **Collection Processing**: Use 'it' to process lists of numbers and strings +2. **Object Filtering**: Apply 'it' to filter and transform custom objects +3. **Chaining Operations**: Chain multiple operations using 'it' effectively +4. **Custom Functions**: Create functions that use 'it' in their lambdas + +## ๐Ÿ”— Related Topics + +- [Lambdas](01-lambdas.md) - Anonymous functions +- [Collections](../collections/01-arrays.md) - Data structures +- [Functions](../functions/01-functions-basics.md) - Function fundamentals +- [Functional Programming](01-lambdas.md) - Functional concepts + +## ๐Ÿ“– Additional Resources + +- [Official Kotlin Lambdas Documentation](https://kotlinlang.org/docs/lambdas.html) +- [Lambda Expressions](https://kotlinlang.org/docs/lambdas.html#lambda-expressions-and-anonymous-functions) +- [Collection Operations](https://kotlinlang.org/docs/collections-overview.html) + +--- + +**Next Lesson**: [Scope Functions](02-scope-functions.md) - Learn about scope functions in Kotlin + +**Happy coding with the 'it' keyword! ๐Ÿ”‘** diff --git a/docs/installation.md b/docs/installation.md new file mode 100644 index 0000000..2603fe0 --- /dev/null +++ b/docs/installation.md @@ -0,0 +1,142 @@ +# Kotlin Installation Guide + +This guide will help you install Kotlin on your system, regardless of your operating system. + +## Prerequisites + +Before installing Kotlin, you'll need: +- **Java Development Kit (JDK)** version 8 or higher +- **An IDE** (IntelliJ IDEA, Android Studio, or VS Code) + +## Installing Java (JDK) + +### Windows +1. Download OpenJDK from [Adoptium](https://adoptium.net/) +2. Run the installer and follow the setup wizard +3. Add Java to your PATH environment variable +4. Verify installation: `java -version` + +### macOS +1. Install Homebrew if you haven't: `/bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"` +2. Install OpenJDK: `brew install openjdk` +3. Link the JDK: `sudo ln -sfn /opt/homebrew/opt/openjdk/libexec/openjdk.jdk /Library/Java/JavaVirtualMachines/openjdk.jdk` +4. Verify installation: `java -version` + +### Linux (Ubuntu/Debian) +1. Update package list: `sudo apt update` +2. Install OpenJDK: `sudo apt install openjdk-11-jdk` +3. Verify installation: `java -version` + +## Installing Kotlin + +### Option 1: Using SDKMAN! (Recommended for Linux/macOS) +1. Install SDKMAN!: `curl -s "https://get.sdkman.io" | bash` +2. Restart your terminal or run: `source "$HOME/.sdkman/bin/sdkman-init.sh"` +3. Install Kotlin: `sdk install kotlin` +4. Verify installation: `kotlin -version` + +### Option 2: Using Homebrew (macOS) +1. Install Kotlin: `brew install kotlin` +2. Verify installation: `kotlin -version` + +### Option 3: Manual Installation +1. Download the latest Kotlin compiler from [GitHub Releases](https://github.com/JetBrains/kotlin/releases) +2. Extract the archive to a directory (e.g., `/usr/local/kotlin`) +3. Add the `bin` directory to your PATH +4. Verify installation: `kotlin -version` + +## Installing an IDE + +### IntelliJ IDEA (Recommended) +1. Download [IntelliJ IDEA Community Edition](https://www.jetbrains.com/idea/download/) +2. Install and launch the IDE +3. Install the Kotlin plugin (usually pre-installed) +4. Create a new Kotlin project + +### Android Studio +1. Download [Android Studio](https://developer.android.com/studio) +2. Install and launch the IDE +3. Kotlin support is included by default +4. Create a new Android project with Kotlin + +### VS Code +1. Download [VS Code](https://code.visualstudio.com/) +2. Install the Kotlin extension by fwcd +3. Install the Kotlin Language Server + +## Creating Your First Kotlin Project + +### Using IntelliJ IDEA +1. File โ†’ New โ†’ Project +2. Select "Kotlin" โ†’ "JVM | IDEA" +3. Choose project name and location +4. Click "Create" +5. Right-click on `src` โ†’ New โ†’ Kotlin File/Class +6. Name it `Main.kt` +7. Add this code: +```kotlin +fun main() { + println("Hello, Kotlin!") +} +``` +8. Run the program (Shift + F10) + +### Using Command Line +1. Create a new directory: `mkdir my-kotlin-project` +2. Navigate to it: `cd my-kotlin-project` +3. Create `Main.kt`: +```kotlin +fun main() { + println("Hello, Kotlin!") +} +``` +4. Compile: `kotlinc Main.kt -include-runtime -d Main.jar` +5. Run: `java -jar Main.jar` + +## Verifying Your Setup + +Run these commands to ensure everything is working: + +```bash +# Check Java version +java -version + +# Check Kotlin version +kotlin -version + +# Check if you can compile and run Kotlin +echo 'fun main() { println("Hello, Kotlin!") }'> test.kt +kotlinc test.kt -include-runtime -d test.jar +java -jar test.jar +``` + +## Troubleshooting + +### Common Issues + +**"kotlin: command not found"** +- Ensure Kotlin is in your PATH +- Restart your terminal after installation + +**"Java not found"** +- Install JDK first +- Set JAVA_HOME environment variable + +**Permission denied errors** +- Use `sudo` for system-wide installations +- Check file permissions + +### Getting Help + +- [Kotlin Official Documentation](https://kotlinlang.org/docs/home.html) +- [Kotlin GitHub Issues](https://github.com/JetBrains/kotlin/issues) +- [Kotlin Community Slack](https://kotlinlang.slack.com/) + +## Next Steps + +Once you have Kotlin installed, you can: +1. [Start with Hello World](basics/01-hello-world.md) +2. [Learn about Variables and Data Types](basics/02-variables-data-types.md) +3. [Explore Control Flow](control-flow/01-if-expressions.md) + +Happy coding with Kotlin! ๐Ÿš€ From 3c86f3013b69218726dc0cd6e70af304258b70a9 Mon Sep 17 00:00:00 2001 From: gpl-gowthamchand Date: 2025ๅนด8ๆœˆ26ๆ—ฅ 05:56:03 +0530 Subject: [PATCH 22/23] Fix incorrect link --- README.md | 2 +- docs/00-course-introduction.md | 245 +++++++++++++++++++++++++++++++++ docs/README.md | 2 +- 3 files changed, 247 insertions(+), 2 deletions(-) create mode 100644 docs/00-course-introduction.md diff --git a/README.md b/README.md index 3c831f2..24e5c34 100644 --- a/README.md +++ b/README.md @@ -3,7 +3,7 @@ Learn Kotlin Programming, its basics and Fundamentals from scratch. ## Topics to be covered 0. Overview - - [Course introduction, prerequisites and software required](docs/basics/01-hello-world.md) + - [Course introduction, prerequisites and software required](docs/00-course-introduction.md) 1. Installation - [Install required softwares for Windows, MAC and Linux ( Ubuntu )](docs/installation.md) 2. Getting Started with Kotlin Programming diff --git a/docs/00-course-introduction.md b/docs/00-course-introduction.md new file mode 100644 index 0000000..d0c88cc --- /dev/null +++ b/docs/00-course-introduction.md @@ -0,0 +1,245 @@ +# ๐Ÿš€ Kotlin Programming Tutorial - Course Introduction + +Welcome to the comprehensive Kotlin Programming Tutorial! This course is designed to take you from a complete beginner to a confident Kotlin developer. + +## ๐Ÿ“‹ Course Overview + +This tutorial covers all the essential concepts of Kotlin programming, from basic syntax to advanced features like coroutines and functional programming. Whether you're new to programming or an experienced developer looking to learn Kotlin, this course has something for you. + +## ๐ŸŽฏ Learning Objectives + +By the end of this course, you will be able to: +- โœ… Write, compile, and run Kotlin programs +- โœ… Understand and use Kotlin's basic syntax and data types +- โœ… Implement control flow structures (if, when, loops) +- โœ… Create and use functions with various parameter types +- โœ… Work with object-oriented programming concepts +- โœ… Utilize functional programming features +- โœ… Handle collections and null safety effectively +- โœ… Use coroutines for asynchronous programming +- โœ… Apply best practices and design patterns + +## ๐Ÿ“š Course Structure + +The course is organized into logical sections: + +1. **Basics** - Hello World, variables, data types, comments +2. **Control Flow** - Conditional statements and loops +3. **Functions** - Function declaration, parameters, extensions +4. **Object-Oriented Programming** - Classes, inheritance, interfaces +5. **Functional Programming** - Lambdas, higher-order functions +6. **Collections** - Arrays, lists, maps, sets +7. **Null Safety** - Safe calls, Elvis operator, lateinit +8. **Scope Functions** - with, apply, let, also, run +9. **Coroutines** - Asynchronous programming +10. **Advanced Topics** - Generics, reflection, testing + +## ๐Ÿ”ง Prerequisites + +### **Programming Experience** +- **Beginner-friendly**: No prior programming experience required +- **Basic concepts**: Understanding of variables, functions, and logic helps +- **Math skills**: Basic arithmetic and logical thinking + +### **Computer Skills** +- **File management**: Ability to create, save, and organize files +- **Text editing**: Comfortable using a text editor or IDE +- **Command line**: Basic familiarity with terminal/command prompt (helpful but not required) + +### **Learning Mindset** +- **Patience**: Programming takes time to master +- **Practice**: Willingness to experiment and practice regularly +- **Problem-solving**: Enjoyment of logical thinking and problem-solving + +## ๐Ÿ’ป Software Requirements + +### **1. Java Development Kit (JDK)** + +**Why JDK?** +Kotlin runs on the Java Virtual Machine (JVM), so you need a JDK to compile and run Kotlin programs. + +**Recommended Version:** +- **JDK 8** (minimum) +- **JDK 11** (recommended) +- **JDK 17** (latest LTS) + +**Installation:** +- **Windows**: Download from Oracle or use OpenJDK +- **macOS**: Use Homebrew or download from Oracle +- **Linux**: Use package manager or download from Oracle + +### **2. Kotlin Compiler** + +**Options:** +- **Kotlin command-line compiler** (kotlinc) +- **Gradle** (build tool with Kotlin support) +- **Maven** (build tool with Kotlin support) + +**Installation:** +- **SDKMAN!** (Linux/macOS): `sdk install kotlin` +- **Homebrew** (macOS): `brew install kotlin` +- **Manual**: Download from [kotlinlang.org](https://kotlinlang.org) + +### **3. Integrated Development Environment (IDE)** + +**Recommended IDEs:** + +#### **IntelliJ IDEA (Recommended)** +- **Community Edition**: Free, full-featured +- **Ultimate Edition**: Paid, additional features +- **Kotlin Support**: Excellent, first-class support +- **Features**: Code completion, refactoring, debugging + +#### **Android Studio** +- **Free**: Completely free +- **Kotlin Support**: Excellent +- **Android Development**: Perfect if you plan to develop Android apps +- **Features**: Full IDE with Android-specific tools + +#### **Visual Studio Code** +- **Free**: Completely free +- **Kotlin Support**: Good with extensions +- **Extensions**: Kotlin Language, Kotlin Extension Pack +- **Features**: Lightweight, fast, extensible + +#### **Eclipse** +- **Free**: Completely free +- **Kotlin Support**: Good with plugins +- **Plugin**: Kotlin Plugin for Eclipse +- **Features**: Familiar to Java developers + +### **4. Text Editor (Alternative)** + +**For minimal setup:** +- **Notepad++** (Windows) +- **Sublime Text** (Cross-platform) +- **Atom** (Cross-platform) +- **Vim/Emacs** (Advanced users) + +## ๐Ÿš€ Getting Started + +### **Step 1: Install Java (JDK)** +1. Download JDK from [Oracle](https://www.oracle.com/java/technologies/) or [OpenJDK](https://openjdk.java.net/) +2. Install following the installation wizard +3. Verify installation: `java -version` and `javac -version` + +### **Step 2: Install Kotlin** +1. Choose your preferred method (SDKMAN!, Homebrew, or manual) +2. Verify installation: `kotlinc -version` + +### **Step 3: Choose Your IDE** +1. Download and install your preferred IDE +2. Install Kotlin plugin if required +3. Create your first Kotlin project + +### **Step 4: Verify Setup** +1. Create a simple "Hello World" program +2. Compile and run it +3. Ensure everything works correctly + +## ๐Ÿ“– How to Use This Course + +### **Learning Path** +1. **Start with Basics**: Begin with Hello World and fundamental concepts +2. **Follow the Order**: Each lesson builds on previous knowledge +3. **Practice Regularly**: Complete exercises and experiment with code +4. **Review and Reinforce**: Revisit concepts you find challenging + +### **Code Examples** +- **Source Files**: Each lesson references actual Kotlin source files +- **Live Coding**: Try the examples as you read +- **Modification**: Experiment with changing values and logic +- **Extension**: Build upon examples to create your own programs + +### **Exercises and Projects** +- **Practice Exercises**: Complete the exercises at the end of each lesson +- **Mini Projects**: Build small applications to reinforce concepts +- **Real-world Examples**: Apply what you learn to practical scenarios + +## ๐ŸŽฏ Success Tips + +### **1. Code Every Day** +- Practice coding regularly, even if just for 30 minutes +- Consistency is more important than long sessions +- Build a habit of daily practice + +### **2. Understand, Don't Memorize** +- Focus on understanding concepts rather than memorizing syntax +- Learn why things work, not just how +- Ask questions and seek clarification + +### **3. Build Projects** +- Start with simple projects and gradually increase complexity +- Apply what you learn to real problems +- Share your projects and get feedback + +### **4. Use Resources** +- Official Kotlin documentation: [kotlinlang.org](https://kotlinlang.org) +- Kotlin community forums and discussions +- Online coding platforms (LeetCode, HackerRank) +- Open source projects for inspiration + +### **5. Join the Community** +- Participate in Kotlin discussions +- Attend meetups and conferences +- Contribute to open source projects +- Help other learners + +## ๐Ÿ” Troubleshooting + +### **Common Issues** + +#### **"Java not found" error** +- Ensure JDK is properly installed +- Check PATH environment variable +- Verify JAVA_HOME is set correctly + +#### **"Kotlin not found" error** +- Ensure Kotlin is properly installed +- Check PATH environment variable +- Verify installation with `kotlinc -version` + +#### **IDE not recognizing Kotlin** +- Install Kotlin plugin/extension +- Restart IDE after installation +- Check project configuration + +#### **Compilation errors** +- Check syntax carefully +- Verify all required imports +- Ensure proper file structure + +### **Getting Help** +- **Documentation**: Check official Kotlin docs +- **Community**: Ask questions on Stack Overflow or Reddit +- **Forums**: Use Kotlin community forums +- **GitHub**: Check issues and discussions on Kotlin repositories + +## ๐Ÿ“š Additional Resources + +### **Official Documentation** +- [Kotlin Language Documentation](https://kotlinlang.org/docs/home.html) +- [Kotlin API Reference](https://kotlinlang.org/api/latest/jvm/stdlib/) +- [Kotlin Playground](https://play.kotlinlang.org/) + +### **Books and Courses** +- "Kotlin in Action" by Dmitry Jemerov and Svetlana Isakova +- "Kotlin Programming: The Big Nerd Ranch Guide" +- Online courses on platforms like Udemy, Coursera, and edX + +### **Practice Platforms** +- [LeetCode](https://leetcode.com/) - Algorithm problems +- [HackerRank](https://www.hackerrank.com/) - Coding challenges +- [Exercism](https://exercism.org/) - Language track exercises + +## ๐ŸŽ‰ Ready to Start? + +You're all set to begin your Kotlin programming journey! The next lesson will guide you through creating your first Kotlin program - the classic "Hello World" application. + +**Remember**: Learning to program is a journey, not a destination. Take your time, practice regularly, and don't be afraid to make mistakes. Every error is an opportunity to learn! + +--- + +**Next Lesson**: [Hello World](basics/01-hello-world.md) - Create your first Kotlin program + +**Happy coding! ๐Ÿš€** diff --git a/docs/README.md b/docs/README.md index 0ed0d71..09773e1 100644 --- a/docs/README.md +++ b/docs/README.md @@ -3,7 +3,7 @@ Learn Kotlin Programming, its basics and Fundamentals from scratch. ## Topics to be covered 0. Overview - - [Course introduction, prerequisites and software required](basics/01-hello-world.md) + - [Course introduction, prerequisites and software required](00-course-introduction.md) 1. Installation - [Install required softwares for Windows, MAC and Linux ( Ubuntu )](installation.md) 2. Getting Started with Kotlin Programming From 5ed49acd5de462aeb5ae607e32efacc79be0a8ad Mon Sep 17 00:00:00 2001 From: gpl-gowthamchand Date: 2025ๅนด8ๆœˆ26ๆ—ฅ 06:04:22 +0530 Subject: [PATCH 23/23] Remove duplicates --- README.md | 7 ++----- docs/README.md | 7 ++----- 2 files changed, 4 insertions(+), 10 deletions(-) diff --git a/README.md b/README.md index 24e5c34..e55641a 100644 --- a/README.md +++ b/README.md @@ -14,10 +14,8 @@ Learn Kotlin Programming, its basics and Fundamentals from scratch. - [Comments](docs/basics/03-comments.md) 4. Constants, Variables and Data Types - [Data Types Deep Dive](docs/basics/04-data-types.md) - - [String Interpolation](docs/basics/05-string-interpolation.md) 5. Control Flow Statements - - [IF ELSE](docs/control-flow/01-if-expressions.md) - - [IF Expressions](docs/control-flow/01-if-expressions.md) + - [IF ELSE and IF Expressions](docs/control-flow/01-if-expressions.md) - [WHEN Expressions](docs/control-flow/02-when-expressions.md) 6. Loop Control Statements - [What are Iterators?](docs/control-flow/06-iterators.md) @@ -37,9 +35,8 @@ Learn Kotlin Programming, its basics and Fundamentals from scratch. - [Named Parameters](docs/functions/03-named-parameters.md) - [Tailrec Functions](docs/functions/07-tailrec-functions.md) 8. Object Oriented Programming in Kotlin - - [Defining Class and creating Objects](docs/oop/01-classes-constructors.md) + - [Defining Class, creating Objects, and Constructors](docs/oop/01-classes-constructors.md) - [INIT block](docs/oop/05-init-block.md) - - [Primary and Secondary Constructors](docs/oop/01-classes-constructors.md) - [Properties ( Field variables )](docs/oop/06-properties.md) - [Inheritance](docs/oop/02-inheritance.md) - [Method and Property Overriding](docs/oop/03-method-overriding.md) diff --git a/docs/README.md b/docs/README.md index 09773e1..7112be6 100644 --- a/docs/README.md +++ b/docs/README.md @@ -14,10 +14,8 @@ Learn Kotlin Programming, its basics and Fundamentals from scratch. - [Comments](basics/03-comments.md) 4. Constants, Variables and Data Types - [Data Types Deep Dive](basics/04-data-types.md) - - [Ranges and Sequences](basics/05-string-interpolation.md) 5. Control Flow Statements - - [IF ELSE](control-flow/01-if-expressions.md) - - [IF Expressions](control-flow/01-if-expressions.md) + - [IF ELSE and IF Expressions](control-flow/01-if-expressions.md) - [WHEN Expressions](control-flow/02-when-expressions.md) 6. Loop Control Statements - [What are Iterators?](control-flow/06-iterators.md) @@ -37,9 +35,8 @@ Learn Kotlin Programming, its basics and Fundamentals from scratch. - [Named Parameters](functions/03-named-parameters.md) - [Tailrec Functions](functions/07-tailrec-functions.md) 8. Object Oriented Programming in Kotlin - - [Defining Class and creating Objects](oop/01-classes-constructors.md) + - [Defining Class, creating Objects, and Constructors](oop/01-classes-constructors.md) - [INIT block](oop/05-init-block.md) - - [Primary and Secondary Constructors](oop/01-classes-constructors.md) - [Properties ( Field variables )](oop/06-properties.md) - [Inheritance](oop/02-inheritance.md) - [Method and Property Overriding](oop/03-method-overriding.md)