Master Cotlin from the ground up in this comprehensive beginners course. This tutorial covers core fundamentals and objectoriented design guiding you all the way through advanced asynchronous programming techniques like co- routines and reactive flows. Alexandrew developed this course. Now it's time to download the nostalian ID. And throughout this course I'm going to use an ID called intellig ID which is an ID made by a company called Jet Brains. Now you may be wondering what is an ID? And ID is stands for an integrated development environment. And it's basically the tool that you're going to use to write your code in. And IntelligJ, it's like a text editor, but a very sophisticated one because it has a lot of features like highlighting our code, showing realtime errors and suggestions. You can run and compile your code just by a few clicks. In other words, it's making the process of writing code easier and better. Therefore, let's download Intellig ID. So, I'm going to open my browser and in the search bar, type IntelligJ ID. Press enter. And we click on this download link. And on the downloading page, you're going to see two options. Ultimate, which is paid. So you need to pay in order to download it. And community, which is free. And uh the difference is that the ultimate has more features and community has less features. But community is not for our purposes. Also, pay attention on the left hand side. When I'm currently recording this video, the latest version is 2021.1.2, which was released on the 1st June of 2022. And if you look at the system requirements here, if you click on that, you will see that this IntelligJ ID only works for a 64-bit version of Windows. So if you have a 32-bit version of Windows, this will not work for you. Now, if you have Mac O X or Linux, uh you should download the particular version for your system. And if you don't know how, just uh tell me in the comments and I will make two separate videos and just show you how to do. Now I'm going to download the community version for Windows. So click to download the community. Now our downloading is starting on the right hand side on the top. And if you have a different browser, you will see this in a different place. So once the downloading is finished, just open this file. I'm going to close the browser now because I don't need the browser. And the installation will start immediately. Now it's asking us if you allow this app to make changes to our device. Click yes. Here we need to click on next. This is the for the location where intellig ID is going to be installed. You can keep the default location or you can change it if you want. I'm going to keep the default location. So I'm going to click on next. And here I'm going to check this box to have a desktop shortcut. I'm going to click on next. Now I'm going to click on install. And IntellJ ID is going to be installed on my computer. As you can see on the left hand side, IntelJ already appeared. Now let's wait until the installation finishes. When the installation finishes, just check this run Intel J Community Edition and click on finish. Now, Intellig IntellJ is going to open. Now, it's asking us if you want to import IntellJ settings. So, if you had the previous version of IntelligJ, you can import the settings here, but I'm going to click not import settings and click on okay. Now, IntellJ is going to open and uh this is the welcome window of Intelligj IDM and here on the left hand side, click on customize. Go here where it says color team. So, you can change this team because the default team is this dark team. I'm going to change it to light because the IntelligJ light team is better for tutorials, but you can keep the dark team if you want. And uh in a separate video, I'm going to show you how to add more teams, not just the ones provided by IntelligJ. Now, click back on projects. Click on this plus button where it says new project because we need to configure the JDK. Now, now here we have JDK and we have in red in angle brackets we have no JD, no SDK. And now here we need to add the JDK and JDK stands for Java Development Kit. SDK stands for software development kit. So software development kit is the Java development kit. No matter how you call it, this thing contains the tools that IntelligJ needs in order to be able to compile and run our programs because without them intell it's like an empty text editor. So it needs those tools in order to be able to work with our particular programming language which is Cotlin. Now to download the JDK and we use Java development kit because Kotlin and Java are interoperable and that means that we can use the existing tools and library which Java already has in our cotlin project. So we click here on this no SDK and click here where it says download JDK. Now on this window we select version 17 not version 18 because this is the latest version 18. We choose version 17 because this is the version which has the long-term support and that means that it will receive bug fixes and updates for several list to come without you needing to switch to new new version when that new version is released. For the vendor we choose Amazon corator and uh I'm going to let the default location. So I'm going to click download to download the JDK. Now the JDK is going to be downloaded. Now the JDK has been installed and it's uh installed on our Intel J ID and now we can create our projects but I'm not going to create a project. So I'm not going to click on create. I'm going to click on cancel and in the next video we're going to create our first project and we're also going to talk about a little bit the JDK and the JVM. So see you in the next video. Everyone welcome back. Today we're going to talk about something that sits underneath all of your cotling code. something you never see directly but that makes cotlin possible. We're talking about JVM the Java virtual machine and why it matters for you as a cotling developer. By the end of this video you'll understand what JVM is, how cotling code gets converted into something the computer can actually run and why you can freely mix Kotlin and Java code in the same project without any awkward glue code in between. So let's start with the problem. Computers don't understand Katlin. They don't understand Java either. Computers only understand machine code, row binary instructions that are specific to the processor they're running on. Now, here's the challenge. An Intel processor and an RMA processor speak different machine languages. Code compile for one will not run for the other. In the early days of computing, if you wanted your software to run on multiple platforms, you had to write or recompile it separately for each one. That was expensive and painful. Sun Micro Systemystems had an idea in the mid 1990s. What if instead of compiling directly to machine code, you compile to a universal intermediate format? A format that any platform could understand as long as the platform had a small piece of software called virtual machine installed. They called it universal intermediate format by code and the virtual machine that runs it the Java virtual machine or JVM. That promise was simple. Write once run everywhere. What is JVM? The JVM is a program that runs on your computer. It's not a physical machine. It's a piece of software that simulates a processor. When you run a Java or a Cotlin program, you're not running it directly on your hardware. You're running it inside JVM which then translates the bite code into machine code that your specific hardware can execute. Think of it like a translator. Your code speaks bite code. Your computer speaks machine code. The JVM sits in the middle and translates in real time. This is why Java and Kling can run Windows, Mac OS, Linux, Android, and anywhere else a JVM implementation exists. The code doesn't change. Only the JVM implementation is different per platform. The JVM also handles a lot of more than just execution. It manages memory, runs garbage collection, so you don't have to manually free memory, optimizes your code at runtime through a process called JAT compilation, just in time compilation and handle security. You'll get all of that for free just by targeting JVM. How does your cotling code actually get to the JVM? It goes through two steps. Step one, the cotling compiler cotlin C reads your KT files and compiles them into that class files. Aclass file contains by code, not machine code for any specific processor, just by code, the universal intermediate format. The JVM understands. Step two, you hand those classifiers to JVM. The JVM reads the bite code and executes it. During execution, the JVMJ compiler will further optimize the hottest parts into native machine code for your specific hardware. Your KT source files never touches the hardware directly goes cotlin source by code JVM machine code hardware. And here is the critical detail. The B code that cotlin C produces is identical instruction to the bicon that Java C the Java compiler produces. They both output standard JVM B code. The JVM cannot tell the difference. It just is by code. This is the heart of everything. Let's make it concrete. You write a a cotlin class. Let's say you have class greeter and you have a function grid. The cotlin compilers turn this into that class file full of bite code. Now compare that with the equivalent Java class public class greeter public string grid. The Java compiler also turns this into that class file full of byte code. The by code from both files is not identical line for line. The two compilers may produce slightly different by code but they conform the exactly the same JVM specification. They play by exactly the same rules. The JVM treats both files the same way. This is not a coincidence or hack. Jet brains designed Cotlin from day one to target the JVM specifically so that cotlin by code would be fully compatible with Java by code. It was a foundational design decision. Kotlin and Java in interoperability because both languages compile to the same B by code format they can talk to each other directly. You can call Java code from Kotlin if you have a an existing Java library say retrofit okv or any kind any third party library written in Java. You can use it in your cotlin without any wrappers adapter or conversion lo layers. You just import it and use it. Cotlin sees as if they were written in cotlin. You can call cotling code from Java. If you write a utility class in cotlin, your Java colleagues can import and use it like any Java class. It appears in Java as a normal class with normal methods. You can even have Cotlin files the same module in the same package calling calling each other back and forth. The build system compiles everything to the same class files. The JVM runs them all together and none of it cares which language each file was written in. This is why Android migrated to Cotlin so smoothly. Google didn't have to rewrite any of the Android framework in Cotlin. All the existing Java APIs, activities, fragments, views are available available directly from Kotlin. Companies with large Java code bases can add Cotlin files one by one without touching the existing Java code. Let's do a quick recap. We have uh JVM, Java virtual machine, a software program that runs on your hardware and executes JVM by code. By code, the universal intermediate format, not machine code, not source code. The middle layer that JVM understands regardless of the original language. Cotling compiler cotlin C read your KT files outputs.class files containing by code. Java compile Java C read your Java files output.class Classifiers containing by code same specification. Both both compilers target the same JVM by code specification. The JVM threes them identically. Interoperability. Cotling can use Java libraries directly. Java can use Cotlin classes directly. No wrappers needed. Right ones run everywhere because your bike runs inside the JVM. Your Cotlin app runs on Windows, Mac OS, Linux, Android, anywhere the JVM exist. Guys, welcome back. Today we're starting a brand new series on Katlin and we're starting right at the beginning with variables. Now I know that some of you might thinking variables really that's the most basic thing in programming and you're right it is basic but Cotlin has a really interesting take on variables that's actually that's actually quite different from what you might be used to if you're coming from Java or other languages. So even if you've been programming for a while, I'd encourage you to stick around because the way cut handles this this is clean, intentional, and worth understanding properly from day one. So what is a variable? Before we look at any code, let's just take a moment to think about why variables exist as all. When your program runs, it needs to remember things. It needs to keep track of user's name, the current score in a game, whether a user is logged logged in, how many items are in a shopping cart. All of that information needs to leave somewhere while the program is running. That somewhere is memory, specifically RAM. A variable is just a named a named slot in memory. You give it a name, you put a value on it, and then anywhere in your code, you can refer it back to it by that name. Instead of writing the actual value everywhere, you write the name and that name always points to the current value stored in that slot. So if a user's name is Alex, you don't scatter the word Alex all over your code. You create a variable called username, put Alex in it, and then use username everywhere. If the user changes their name, you update it in one place and every part of the code that uses username automatically gets the new value. That's the fundamental purpose of a variable. give it a name to a piece of data so we can refer it. We can refer to it, use it and potentially change it. Now variables in cotlin you have val and var keywords. Now in coding there are two keywords for declaring variables val and var. This is one of the first things that surprises people coming from Java because Java just has one one one way to declare a variable. You write the type and the name and that is done. Cotlin splits variable declaration into two categories based on a very important question. Will this value will this value ever change? Val, short for value declares a variable that cannot be reassigned after it's set. Once you give it a value, that's it. You can't you can read it, but you can you can use it, but you cannot change what it points to. Var short for variable. This declares a variable that can be reassigned. You you set set it to one value and later you can set it to a different value. That's it. Two keywords. Val for things that don't change and var for things that do. So let's look at more briefly more deeply about val. So let's look at val first. And here here is how you declare one. You type val. Let's say username Alex. That's it. Username is now Alex and it will always always be Alex at least in their scope in this scope. If you try to do this later, username equals Bob. Error. V cannot be reassigned. Cotlin will refuse to compile your code. It gives you a compile time error. Val cannot be reassigned. The compiler is protecting you from accidentally changing something you said should be fixed. When you when you use val, use it for anything that represents a fixed fact for the duration of that scope. The ur your app connects to a user ID after they log in. The result of a calculation you you only needed to do once. The number of items in a list you just fetch. Anything that you set once and read multiple times. That's val territory. In practice, the cotling community and the official style guide recommends default to val. Reach for var only when you generally need to reassign the value. This makes your code easier to reason about because when you see a val, you know that its value is stable. You don't need to track it down every place it may have changed. Next, we have var type var score equals zero. And you type score starts at zero, but it can change. So you can type score equals 10 or score equals score + 5. That's all that every that all works fine. Score goes from 0 to 10 to 15. The variable holds whatever the most recent assignment was. When to use var? Use it when the value generally needs to change over time. score in a game, a counter that increments in a loop, a flag that starts as false and flips to true when something happens, a running a running total being accumulated. The key word is the genuinely, not might possibly change someday, generally change changes as part of the logic of your program. If you find yourself declaring a var but never actually reassign it, cotling compiler will even warn you. Your var could be val. It's nudging you to be more intentional. Val versus v. Let me put them side by side so you can see the contrast clearly. You have val app name my app val max retries three var current user request var retry count zero. Look at how the comments match with the keywords. The valves are things that are set once and stay the same. The VS are things with trajectory trajectory. They start at one value and are expected to move somewhere else. This distinction isn't stylistic. It's communicating intent to anyone who reads your code. When I see a val, I know that I don't need to scan the rest of the function to check if it gets modify. When I see a V, I know that I should pay attention to how it changes. What val does not mean immutable objects. Going to cover this in object oriented programming. But now there is one thing that I want to make very clear because it confuses a lot of people early on. Val means the variable cannot be reassigned. It does not does not mean the object the variable points to is immutable. Let me show you what I mean. We have val numbers and you have this thing mutable. Going to see what all of these are in the next videos, but I'm going to show you this to illustrate my point. So you have val numbers and you have a few numbers added to a list. A list is basically a variable which can hold multiple values not just one. Numbers is a val. You cannot reassign it to point to a completely different list. But you can modify the list. It's pointing to adding elements, removing elements. The all that still works. V says this variable will always point to this object. He says nothing about what happens inside that object. This is an important nuance. We'll go we'll go much deeper into mutability collections and immutable data later in the series. For now, just remember val equals no reassignment. Not val means it means frozen forever. So it doesn't mean that it's frozen forever. I want to spend a moment on the bigger picture here before I think it helps you explain why cotton was designed this way. One of the biggest sources of bugs in software is unexpected mutation. Something changes a value somewhere and something else was depending on it depending on it being the old value and suddenly your app is an incon inconsistent state. It's one of the hardest classes of bugs to track down. Cotling designer look at this and said let's make immutability the default. Let's make it easy to declare something that won't change and slightly more effortful to declare something that will not impossible just intentional. That's why val comes first in every cotling tutorial and style guide. Not because v is bad bad is fine when you need it but because if you default but because if you default it to val you're defaulting to stabil stability. you your code becomes easier to test, easier to read and harder to break break accidentally accidentally. This mindset shows up everywhere in Cra in how collections are work in how functions are designed in how data classes behave. But it all starts here with these two little keywords quick rules to remember. Let's wrap it up with a few simple rules that you can take away from this lesson. Rule one, use val by default. Every time you declare a variable, start with val. Only switch to val if you hit a point where you actually need to reassign it. Rule two, val prevents reassignment, not mutation. A val pointing to a mutable object can still have the can still have that object change internally. Rule three, if you declare a v and never reassign it, quoting with or that's a sign you should make it a val. Rule four, both val and var must be initialized before their use. Cotlin does not allow uninitialized variables. If you try to use a variable before giving giving it a value, the compiler will stop you. We'll talk about how to handle that in a specific situ in specific situations like class properties in a later video. And rule five, name naming matters. Use discretive names for variables. Username is clearer than you. Retry count is clearer than R. Kotlin type inference which we'll cover in the next video means you don't need to write the type of type out you need you don't need to type out explicitly in most cases. So the name carries even more of the commun even more of the communication weight. So that's cotling variables. We cover why variables exist their name slots in memory that you let your that let you refer to data by name. We covered the two keywords val for values you set once and for v for values you reassign. We cover val means no reassignment not deep immutability and we talked about the cotlin mindset of preferring val by default. In the next video we're going to look at how look at data types and type inference how cotling figures out what kind of data you're storing when you need to be explicit about it. All right I'm going to end this video here and I'll see you in the next video. Hey guys, welcome back. Today we're continuing our Cotlin series. In the last video, we covered why variables exist and the difference between val and var keywords. Today we're going a step further. We're going to talk about data types, specifically two of the most common ones, string and int. And we're also going to cover one of Cotlin's most convenient features, type inference. By the end of this video, you'll understand what types are, when to use string versus int, and how cotling can figure out type for you without you having to write it explicitly. So, let's go. Before we dive into data types, let me do a super quick recap for the last video because we're going to build on it. In Cotlin, you can declare variables with either val or v. Val means the variable cannot be reassigned. You set it once and it stays. The cotlin style guide says that to use val by default. Val means the variable can be reassigned. You use it when your value on the value needs to be changed over time. So you have val app name that is fixed v score that changes as the user plays the game. So this is the foundation. How now the question is what kind of data can a variable hold? That's where data types came in. What is a data type? Every variable in codlin has a data type. The data type tells codlin two things. What kind of value the variable holds and what operations are valid on it. Think about this way. You have a variable holding the number 42. You can do arithmetic on it. Add, subtract, multiply. But you can't do arithmetic on the word hello. And if you have a variable holding someone's name, you can't you can join it with other text. But you can't say add 10 to it. and expect a number back. The data type is the contract that says this variable holds this kind of thing and here are the things you're allowed to do with it. In cotton, every value every in every value has a type. Even when you write val score equals to 42, cotton knows this is an int an integer number. You didn't have to write it. Cotton figured it out. That's type inference and you'll get to it in detail shortly. For now, let's look at two two types like the let's look at the two types we're focusing focusing on today. String and int. A string is a sequence of characters. Text names messages ur labels. If it's text, it's a string. And quotes and strings values are written with double quotes. Have first name, double quotes, Alex, greeting, double quotes, hello world. Strings can contain letters, numbers, spaces, punctuation, anything. The double quotes tells codlin. So the double quotes tell everything between those quotes is text treated as a string. You can join two strings together using the plus operator. This is called concatenation. So you type val full name is going to be equal to first name plus the space last name. But in coten there are even cleaner ways which are called string templates. You put a dollar sign before before the variable followed by the variable name directly inside the string. So you type val greeting hello dollar sign name. If you need to include an expression, wrap it in curly braces. So you type val age 25 val message equals you are dollar sign curly braces age + one because you put an expression. Now maybe you maybe wondering what is an expression? An expression is something which returns a single value. So h + one is a single value. So it can be used there. String templates I don't know are one of those uh small things that make cine a joy to write. Use them. They're cleaner than concatenation. Int. An int is a whole number. No decimal point. And it's used for scores, counts, ages, quantities. If it's a number you're counting with, it's probably an int. You can do all all the standard arithmetic operation on in. So you can have sum, difference, product, co, quotient, remainder. One thing to watch for in division always produces an int. So the division of a int produces an int. 10 / 3 gives you three, not 3.333. Co tray is a decimal. If you need a decimal number, you'd use a double. But that that's but that's a topic for another video. For today, just remember int is for whole numbers and division between ins gives you a whole number result. String versus int. Here is something that trips up beginners in cotlin. 25 and in quotation marks and double quotation marks and 25 without double quotation marks are completely different things. So you have val number as text. This is a string. V actual number 25 without the quotation marks. This is an int. They look similar but they behave completely differently. You can do math with the int. You cannot do math with a string. Coding type system protects you from this. If you try to mix them, you get a compile error before your app even runs. If you have a number as a string, maybe it came from user input and you you you need to do math with it. You can we can convert it. You can type you have val user input 42 as a string and you can another variable as int and use the function. We're going to see what functions are later. User input dot to int. Now it's a int 42. And if you need to display it as text type val count values message you have dollar sign count items automatically converts count to a text inside the template string templates handle the int to string conversion for you automatically. That's another reason to use them. Type inference. Now let's talk about let's talk about type inference and this is where cotling really shines in many language languages you have to explicitly declare the type of every variable Java style explicit you need to explicitly define the times you type string name Alex int score equals z in cotlin you don't have to do this you can but you don't have to cotlin looks at the value you assign and figures out the type itself the type is still there cotling is still a statically type language. The compiler knows that name is a string and score is an int. It just didn't have to write it. Cotton read the value and infer infer the type. How does it work? It's straightforward. If the value is in double quotes, it's a string. If it's a whole number is with no codes, it's an int. Cotlin applies these rules at compile time and assigns the type accordingly. Explicit type annotation. Now sometimes you do want to write the types explicitly. Cotton lets you do this with a colon after the variable name. So you type val name colon string Alex. This is called a type annotation. The syntax is variable name colon type equals and the value. When should you use explicit types? There are a few situation where it makes sense. First, when you type when when the type isn't obvious from the value. If someone reads your code and can't immediately tell the what type of variable is annotating it helps. Second, when you want to declare a variable without immediately giving the value though in practice cotlin usually requires initialization and declaration. Third, for class level property is common style in Cotlin to annotate times on a class me on class members for clarity. And fourth when you need a cotling you need you so when fourth when you're new to cotlin or explaining code to others writing the type explicitly makes the code more self-documenting and easier to teach. The important thing is explicit annotation and type inference produce exactly the same result. Cotries them identically at compile time. It's poorly read readability choice. this syntactical sugar as is sometimes called. Okay. So let's look at both styles side by sides to to see the difference crystal clear. So we have with type inference and without type inference. Both sets of declarations are completely equivalent. The comp compile output is identical. The variables have the same types, the same values, the same behavior. In real cotling code, you'll see inference use most of the time. It's less verbose and still perfectly clear when the value makes the type obvious. But you don't be afraid to add the annotation when it helps. All right. So, this is our uh this is a string template. I'm going to end this video here and I'll see you in the next video. Hey everyone, welcome back. Today we're going deeper into Cotlin's numerical types. Specifically, we're covering the four integer types, bite, short, int, and long. Now, in a previous video, we introduce int as the go-to type for whole numbers. Today, we're going to zoom out and see the full picture. We'll understand why there are four different integer types instead of just one. What's the difference between them? What their minimum and maximum values are? And most importantly, when to actually use each one. So why there are four integer types? This is a reasonable question. Why not just one number type for all whole numbers? The answer is memory. Every variable program takes up space in memory. A number like three doesn't need much space. A number like 96ion needs a lot more. If you use the same large container for every single number, even small ones, you'd waste enormous amounts of memory. Think of it like boxes. You don't use a moving truck to carry a single coffee mug. Use the right size container for the job. Cotling gives you four box sizes for integers. Bite, the tiny box, fits very small numbers. Short, the small box fits small numbers. Int, the medium box fits most everyday numbers. and long is the large box which fits very large numbers. In practice, int covers the vast majority of use cases, but understanding them to reach for others makes you a more intentional programming. So, I'm going to repeat this. But understanding when to reach for the others makes you a more intentional programmer. Let's look at the first which is bite. A bite uses opts of memory. That's one bite literally because it has OP bits. It can represent 256 different values split between negative and positive. That gives us minimum value 100 - 128 maximum value is 127 that range - 128 to 127 when you would use bite honestly I rarely I rarely an application code the main use cases are working with row binary data files network packets image pixels when you're reading a file by bite by bite you work with bytes memory critical arrays if you need an array of 10 million numbers that all fit in the same in the minus 138 to 137 range using byte array instead of in array. We're going to see what those are later in the videos in the course. Saves you 30 megabytes of memory right there. Inter operating with Java APIs or C libraries that deal in row bytes. For most everyday programming, you won't reach for bite, but knowing knowing that it exists matters. Next we have short. A short uses 16 bits, two bytes. With 16 bits it can represent 65,536 different values. Minimum value is minus 32 32768 and max value is 32,767. Short is rarely used in modern contment. Historically they existed to save memory in environments with very limited RAM. Today, most devices have plenty of memory and the slight the slight saving from short to interifies the extra cognitive overhead. You might encounter short when working with audio sample data where samples are often 16 bit signed integers, legacy file formats that store fields as 16 bit values. interoperability with C or Java code that uses short in new coding code. If a number fits in short, it almost certainly also fits in int. And an int is simpler and more ideatic. It reserve short for specific technical requirements. Next, we have int. Int is every everywhere is the work horse. This is the type you use for the overwhelming majority of integer values in your programs. An int uses 32 bits, four bytes. With 32 bits, we have maximum values minimum value sorry minus 2 bill147 40,483,000 648 about negative to 2 billion and maximum value is 2 billion 147 million and so on. And uh intros is also cotlin's default integer type. When you write a whole number literal without the suffix codling inferies as an int. So we have val x42. This is an int. No annotation needed. The range is roughly plus or minus 2 billion. 2 billion. Covers virtually every real world integer you'll deal with. Ages, scores, counts, indices, years, pixel coordinates, screen size, view ids. Unless your number exceeds to two exceeds exceeds the exceeded the two billion use. So I'm going to repeat that. Unless your number can can exceed the two billion using if you have a number which is bigger use long. The range of roughly plus or minus 2 billion covers virtually every real world integer you'll deal with. One handy feature quoting allows you to replace underscores in numeric literal to improve relability. You can put two billion underscore 147 million 483,647 is clear than writing that at once as you can see in the diagram on the right. Okay. Long. Long is for when int isn't enough. When your number exceeds roughly 2 billion in either direction, you need the long. A long uses 64 bits, eight bytes. And the minimum value is this number that you see on the left. And the maximum value is also that number. And that number is roughly plus or minus 9 quintilion. 9 followed by 18 zeros. You mark a long lit with an uppercase L suffix. The L suffix is mandatory when writing a long lit that exceeds the int range. The ins range. Without it, Cotlin would try to interpret as an int and fail. When the value is within an ins range, but you especially want a long, add the l. Where do you actually use long? So where do you actually use long timestamps system current time in millions returns the number of milliseconds since January 1st 1970 that number is currently over 1.7 trillion far beyond ins range file sizes a large file can be gigabytes in size bytes of 5 gigabytes file 5 billion 3,68 million 79,120 that and that needs a long database primary keys in large systems if your table has billions of rows your ID needs to be long any astronomical or scientific computation where numbers get large let's do a sideby-side comparison Okay. So the memory cost per variable bite cost one bite short costs two bytes int cost four bytes long cost eight bytes. For for a single variable this difference is negligible for any array of 10 million values. The difference between byte and long is 7 70 megabytes. Integer overflow that's one important concept to understand with integer types overflow. What happens if you add one to the maximum int value? You get minus 2 bill147,400 400 88 8,3 648. The minimum value the number wraps around from the higher positive value back to the lowest negative value. This is called integer overflow and it's a silent bug. Cotlin doesn't throw an exception. It just gives you a wrong answer. This is why exactly long exists. If you're computing values that could approach or exceed ins range, time stamps, file sizes, population numbers, accumulated totals over time, you must use long. Using in when your values can overflow is one of the classic bugs in system programming. Cotlin provides convenient constants for checking. So you have int min value, int max value, long mean min value, long max value. Use this in your code instead of magic numbers when you need to check boundaries. Now when to use each design? This is a decision guide. Which in integer type should you reach for? Default choice always start with int unless you have a specific reason to use something else. Int is correct for everyday integer values. Use long when your value can exceed two billion timestamps, file sizes, large IDs, scientific values, anything that grows beyond the in range. Use bite when you're working directly with binary data. Reading files, processing network packets, working with image buffers. The data literally comes in bytes in as bytes. Use short only for specific technical requirements. Legacy formats, legacy formats, audio samples, uh, interrupt with 16 bit API. Don't use short just to save a few bytes on ordinary variables. The overall rule, be precise but not obsessive. Using it for counter is correct. Using long for time stamp is essential. Using by for users age is premature optimization. Clarity and correctness matter more than squeezing out a few bytes. All right, this is our and I'm going to see you in the next video. Hey everyone, welcome back. Today we're continuing our series on cotling data types. In the last video we covered the integer types by short int and long all whole numbers with no decimal point. Today we're moving on to the two types that can handle decimal numbers float and double. By the end of this video you understand what each type is, their size and precision. And this is the important one. Why cotlin assumes a decimal number is a double by default and how to explicitly tell cotling you want that you want a float instead. So why you need decimal types? First a quick reminder of why you need different types for decimal numbers at all. Integer types are store whole numbers. They are perfect for counts, indices, ages, things that don't have a fractional part. What about a price? A temperature. the result of dividing 10 by three a GPS coordinate. These values have digits after the decimal point and you can't represent them according accurately with an integer. That's where floating point types come in. The name floating point refers to how the decimal point can move or float to represent both very large and very small numbers within the same type. It's a way of using a fixed number of bits to cover an enormous range of values including fractions. Cotling gives you two floating point types, double and float. Double is the default is the default floating point type in coding and most modern programming languages. A double uses 64 bits of memory, eight bytes. It can represent decimal numbers with approximately 51 15 to 16 significant digits of precision. The range of a double is enormous from roughly 1.7 times 10 to the negative 308 power up to 1.7 times then the positive 308 power. For practical purposes double can represent visually any decimal number you'll encounter in everyday programming. Here is the critical point. When you write a decimal lit in cotlin it is assumed to be a double always. No suffix needed. No annotation needed. Cotly reach the decimal point and immediately inferes double. You get up to 15 to 16 digits of precision which is more than enough for the overwhelming majority of use cases. Float float is the smaller of two types. A float uses 32 bits, four bytes, half the memory of double. Because it has fewer bytes, it has less precision, approximately six to seven significant digits. Now here is the important thing I want to make sure you understand. If you just write a decimal number in cotlin it will be treated as a double not a float. To create a float you must add an uppercase f at the end of the literal or a lowerase f without the f cotling gives you a double. That's the default. The f suffix is your explicit instruction to the compiler. I want this to be a float. So this is saying specifically to the compiler, hey I want this to be a float. This is the opposite of what some languages do in Java. For example, a decimal is a float by default in and you add D for double. Kotlin flips it. Double is the default and F means float. Five default is always double. I want to make sure this point absolutely crystal clear because it's the most common source of confusion with these types. in quotes in any decimal lit you write is a double unless you explicitly say otherwise to have val ABC and you have first 3.14 and that is a double because it's inferred and is the assumed floating point number then we have a B which is a flo which is float because it has the suffix f at the end and we have again double 34 for c if you try to reassign a bare decimal to a float variable without the f suffix, you'll get a compile error to say type mismatch double assign to float. And if you and this is intentional contain wants you to be explicit about using the less precise types. If you're assigning to a float, you need to own that decision. The f suffix is your way of saying yes, I know this is a float and I'm intentional about it. Float versus double. So when to when do you actually use float versus double? Use double by default. It's more precise is the default and on modern hardware that performance difference between float and double is negligible for most applications. When in doubt, use double. Use float and memory generally matters. The place where float is most commonly used in Android development is where graph with with graphics and 3D rendering. OpenGLes, the graphics API used on Android, works with flows, not doubles. If you're passing coordinates or color values to a graphics pipeline, they need to be flows. The slight loss of precision is access acceptable for visual output and the memory savings across millions of vertices or pixels is meaningful. Also, some scientific or numerical libraries may use flood for performance in bulk computation. If an external API requires a float, use a float. But before for everyday application code, prices, measurements, calculation, coordinates you're displaying to the user, use double. Thus, it's safer, more precise, and the default for a reason. All right, I'm going to end this video here. This is the cheat sheet. If you take a look at it, what of what you covered? I'm going to end this video here and I'll see you in the next. Come back. Today we're covering the last two primitive data types in cotlin that we haven't talked about yet and those are the char and boolean. These are two of the simplest types in language but they come up constantly in real code. By the end of this video know exactly what each one does, how to declare and declare and use them and where you'll run into them in practice. Let's get into it. So what is a char? Let's start with char. The name is short for character. Char holds exactly one character, a single letter, digit, symbol or special character. Notice the emphasis on singular, one character, not a word, not a sentence, not even two characters, just one. In cotlin, declare a char using single quotes. The type letter single quotes a single quotes are the signal to cotlin that this is a char. This is important because it's different from string which uses double quotes. If you write double quotes a with double quotes, that's a string containing the letter A. If you write single quotes A with single quotes, that's a char. The character the character the A itself. The distinction is intentional. A char and the string are different types with different capabilities. A char is a single unicode character nothing more. Let's look at char in detail. Let's look at char more closely. Under the hood, Cotlin's chart represents a uni code character. Uni code is the international standard that covers virtually every character in every writing system. Latin, cerillic, Arabic, Chinese, emoji, mathematical symbols, everything. A cotlin chart can hold any uniode character that fits in 16 bits, which covers the vast majority of what you'll ever work with. You can declare charts for all sorts of for all sorts of characters. For uppercase, lowerase, digit, space, new line, tab. Notice that the last two backslash n and back slash t these are escape sequences. They are a way of representing characters that you can type directly like a new line or a tab. The box backslash tells cotlin the next character is not a literal character. It's a code for something else. One thing to be aware of you cannot put more than one character between the single quotes. If you try to write a b cotton will give a compile error. Single codes hold exactly one character always. So what you can actually do with char once you have one first you can compare chars. Characters in mini code have a defined order alphabetical order corresponds to their underlying code points. So comparing two charts works as you expect. We have char one. So C1, C2, A and B print A is less than C2 is true. A come A comes before the B. Second, you can convert char to it underlying integer value using the digit to in function or by using the code property. So type val char a print line chart. For now don't need to know how this works. So how we type this chart char code and you get 65 because uh that involves function and we didn't cover function yet. but we'll cover them in later videos. And conversely, you can go from an integer to a char using the two char function. Third, chars work naturally with strings. If you want to build a string out of individual characters, you can concatenate them. And when you iterate over a string with a for loop, each element you get back, it's a char have val. So 4 C h in name print ch have a l x and x. You'll use this of often when processing text characters by character checking if a character is a letter a digit an uppercase lower case and so on. Quotling provides a rich set of extension functions for exactly this purpose. What is a boolean? Now let's talk about boolean. A boolean is a type that holds one exactly two which holds one of exactly two values true or false. That's it. No numbers no text no other options just true or false. You have val is logged in it's loading has permission wall have boolean values true false and true. Boolean is named after George Bull the 19th century mathematician who developed the boolean algebra the mathematical system that all digital logic is based on. every conditional statement, every if block, every while loop in every programming language ultimately operates on boolean values. And if you don't know what are uh loops and all of that just I I need to bring this illustrate my example, but you don't need to understand that right now. We're going to cover that later in the course. Functions and loops and all of that. In Katlin, boolean is a first class type. You declare boolean variables with val or v and you can pass them to functions, store them in data classes and return them from functions just like any other type. V result equals boolean condition parenthesis going to call the the function same condition is going to return something if the result is true or false. Boolean operators boolean because boolean only has two possible values. You can combine and manipulate booleans using three fundamentals operators and or and not. I'm going to cover those later in the course in just a few videos from them. And written and is written as two empersons. The result is true if both sides are true. If either side is false, the whole expression is false. We have val a b true and false. If print a and b you get false because must both must be true or written as two pipes. The result is true if at least one side is true and only is false if both sides are false. So we print or ab this is going to return true. Not written as an exclamation mark. This flips the value. True becomes false and false becomes true. So if you type print line not not a not b first it's going to return not a false and not b true because we're going to flip them. These three operations are the building blocks of all conditional logic. Every if statement every while condition every filter in a collection is built on top of this. So where you'll see booleans in every day boolean is everywhere in real code. Let me walk you to the most common places you'll encounter it. If statements, the most obvious one, the condition if in an if statement, it's always a boolean expression. We're going to cover if statements later. Loops, while loops, we're going to cover those also later. The condition that controls whether the loops other loops keep running, it's a boolean. So, we type while condition, it's loading true or false. If it's true or false, show spinner. Functions that return boolean. These are called predicate functions. By convention in coding functions that return a boolean are named with is has can or should as a prefix. So we have fun is valid email and again don't need to understand how all of this works. Now data class properties again this also is going to be covered in the OP section things like selected is enabled is visible. Every UI component you'll ever work with has boolean properties controlling it state. Data class user smart casting has uses boolean checks to narrow tiles. When you check if something is not null inside the if block, cutling compiler automatically knows inside that block that the value is non null. Now let's look at char. Let's look at char versus string. Before we wrap up, I want to address a confusion that ths beginners. The difference between char and string. Single quotes a is a char. It's a single character. Double quotes a is a string. It's a string containing one character, but it's still a string, not a char. They are different types. You cannot assign one to the other. So don't don't you can put wrong char equals double a. In practice, this matters when you're working with text processing. A function that expects a char will not accept a string even a single character one. If you need to convert, you can use the first extension function on a string to get its first character. If you need to turn a chart into a string, call two string on it. Now let's some quick rules to remember. Let's put all together the few rules. Char single quotes exactly one character cannot be empty cannot be two character used for individual characters for processing text character by character or when an API specifically needs a child boolean true or false nothing else name boolean variable start with yes has can or should when they represent a state or condition use the end operator the or operator or the not operator char versus string a single single quotes equals char double quotes equals string. They are different types. Single quotes a is not the same as double quotes a as far as cotlin type system is concerned. Boolean is not an integer. In some languages zero means false and one means true. Cotlin is not does not work that way. Boolean it's its own type. You cannot use it as an integer where a boolean is expected. Type inference works for both. Val letter single quotes infer val flag true infer boolean. You don't need to write the type explicitly. Okay, I think I'm going to end this video here and I'll see you in the next video. Come back. Today we're covering operators in Cotlin, specifically the five arithmetic operators, operator precedence, the four compound assignment operators, and the increment and decrement operators, including the difference between prefix and postfix. This is a meaty topic, but a fundamental one. Every program you write, you will use this constantly. So let's go through all of them properly. But before we jump into specific operators, let me take 30 seconds to define what an operator actually is. An operator is a symbol that tells Cotlin to perform a specific operation on one or more values. The values the operator works on are called operands. You have val result 10 + 3. In this line, the plus sign is the operator. The numbers 10 and three are the operants. The operator, the operator takes the two operants and produces a result. In this case, 13. Cotton has many categories for operators. Today, we're focusing on arithmetic operators, the ones that do math and the assignment operator that combines math with assignment. These are the ones you use every single day in every single project without exception. So, let's look at first addition and subtraction. Let's start with two simple with the simplest addition and subtraction. Addition uses the plus sign. It adds two values. V a 10 val b 3. You have val sum a + b and you get total val total equals to 100 + 50. Subtraction uses the minus sign. It subtracts the right operent from the left. This works exactly as you expect from school mathematics. Nothing surprising here. One small thing worth noting the minus sign can can also be used as a unary operator meaning it can work on a single operator to negate the value. We can have val positive= 5 and val negative equals minus positive. The single minus sign in front of the variable negates it. This is different from subtraction which needs two operants. Multip multiplication and division. Multip multiplication uses the asterics the star symbol. It multip multiplies two values. We have product A * B val area 6 * 7. Division uses a forward slash. And this is where something important happens that you need to understand. When you divide two integers in cotton, you get an integer result. The decimal part is simply discarded. Not rounded, discarded. V quotient 10 / 3 is 3, not 3.333. 10 / 3 is 3.333 repeating. But because both operands are in, the result is also an int and the fractional part is thrown away. You get three. This is called integer division and it's been a source of bugs for programmers since the beginning of computing. If you want the decimal result, at least one of the operants needs to be a double. So if you have val exact and val also exact divided by 3.0 3.0 is double. The moment either operent is a double the result is a double. Keep this in mind whenever you're dividing integers and expecting a fractional result. The modulus operator uses the percent sign. It returns a remainder after an integer division. So if type val reminder 10 percent sign three and this is going to return one. Why? Because 10 uh modulus 3 equals to 1 because 3 goes into 10 three times that's 9. The remainder is 10 - 9 which is one. The modulus is that remainder. When the modulus is zero it means that the left number divides evenly by the right number. That's very common check. If you type if number percent modus 2 equals equals z that means that that is an even number. If a number modus 2 equals zero it's even if it equals one it's odd. You'll you'll use this pattern constantly checking if something is even checking if your counter has hit a a multiple of something. Checking if a value is divisible by a specific number. model is one of those operators that seems obscure at first and then turns out to be everywhere in real code. Now let's look at the topic of operators precedence. Now let's talk about operator precedence. When you have multiple operators in expression which one runs first follows the rules as the rules of mathematics. The same rule you learn in school. The acronym Bodmas or PEMD does depending on where you from brackets or orders divisions multiplication addition and subtraction. The practical rule for our operators role one multiplication division and modulus are evaluated before addition and subtraction. So val result 2 + 3 * 4 14 not 20. The multiplication happen first. 3 * 4 is 12. Then addition 2 + 12 is 14. If you expect 20, you're thinking left to right. But that's not how it works. Rule two. When operators have the same precedence, they they are evaluated from left to right. So val result 10 - 3 - 2 is equal to 5, not 9. Left to right. 10 - 3. So left to right 10 - 3 is 7. Then 7 minus 2 is 5. Rule three parenthesis can override everything. You can type val result 2 + 3 * 4 and that is 20. Now the addition happens first because it's in parenthesis. 3 + 2 3 + uh sorry 2 + 3 is 5. Then 5 * 4 is 20. This is one of the most important things to internalize as a programmer. Subtle pre precedence bugs are real. When when in doubt, add parenthesis. Explicit is always better than relying on precedence rules that next the next person reading your code might not be might not remember. So use parenthesis whenever you want to enforce that compound assignment operators. Now let's move to the compound assignment operators. These are shortcuts that combine an arithmetic operation with an assignment in one step. You'll use this all the time. Here is the full set. You have plus, equals, add and assigns, minus equals, subtract and assigns, asterics, multiplication equals, multiply and and assign, and slash and equal, divide and assign. Let me show you what each one does. Starting with a variable called score. So you have the variable var score equals to 10. So type score plus= 5. Same as score equals score + 5. Score is now 15. Score minus= 3. Score as score equals score minus 3. Score now is 12. Score * 2. Score time equals 2. Same as score equals score * 2. Score is now 24. Score divided equals to 4. Same as score equals to square / 4. Score is now six. Each one takes the current value of score. Applies the operation with the right hand number and store the result back into score. The two character operator is just a shorter way of writing the full expression. So why those exist? Readability and brevity. Then we have a counter running. So when we have a counter a running total or any value that accumulates over time writing writing score plus= 5 is cleaner and less error prone than writing score equals score + 5 over and over. Notice that these operators require v not val. They are you are reassigning the variable. So it must be mutable. Trying to use plus equals one of val will give you a compile error. So let's look next at the increment operator. Now let's talk about the increment operator. Two plus two plus signs written together plus+. The increment operator adds one to a variable. That's it entire it's that's its entire purpose. V count equals zero. Count ++ count now is one. This is equivalent to writing count equals to count + one or count plus equals 1. It's just more concise. You'll see plus+ everywhere is the standard way to advance uh to advance a counter loop variable step step counts retry attempts. Anything that increments by one uses plus+. Now here is where it gets interesting and where many beginners get tripped up. There are two forms of the increment operator. Posfix and prefix. Posfix count plus the plus comes after the variable name. prefix is plus+ count. The plus+ comes before the variable name. Both both add one to the variable but they differ in when the increment happens relative to when the value is read. So let me show you postfix B versus prefix incrementation. This is the most subtle part uh the most subtle part of today lessons. Pay close attention to this. Postfix means the plus+ comes after the variable name. So we type v s x x = to 5 and val a is equal to x++ a gets the current value of x 5. Then x is incremented after the line a= to 5 x x is equal to 6. The postfix operator returns the value before the increment. Think of it as give me the value then add one. Prefix comes before the variable name. So we type v x= 5 and val b ++ x is incremented first then b gets to the gets the new value after this line b = 6 and x x = 6. The prefix operator increments first and then returns the value. Think of it as add one first then give me the value. Okay. So prefix the plus+ come comes before the variable name. So type var x = to 5 and val actually I already I already covered this. Let's look at both side by side. So we have vx = to 5 print x++ prints five. Current value given to print line then x becomes six and v y = 5. Print line plus y. This prints six. I is decremented first and the new value is given to print line. when the increment is is uh the increment is standalone on its own line prefix and postix produce the same result. So if I type count plus+ effect as plus count when not used inside an expression the difference only matters when you use it inside a larger expression an assignment or a function call. In those cases, postfix uses the old value and prefix uses a new value. The decrement operator two minus signs works exactly like increment but subtracts one instead of adding one. So we have v count equals 10 count minus minus count is now nine. And just like increment, decrement has both postfix and prefix forms with the exact same rules. Decrement is used for counts reducing a counter on each iteration tracking remaining capacity or any situation where a value is decreased by one at a time. So putting it all together have here an example of of a realistic example of a game where you have the score and the way of calculating the score. I'm going to leave it for you. We inspect this and I'm going to end this video here. Welcome back. Today we're covering the if statement in Katlin. One of the most fundamental control flow tools in any programming language. Every program you ever write makes decisions and if is how you express them. So what is an if statement? An if statement lets your program to make a decision. It evaluates a condition, something that is either true or false, and runs a block of code only if that condition is true. The basic structure is simple. You have if parenthesis condition curly braces the code that runs only when the condition is true. The condition inside the parenthesis must be a boolean expression. That means it must evolate to either true or false. Nothing else. If it's true, the code inside the curly braces runs. If it's false, the block is skipped entirely and the execution continues after it. So if you have val age equals 20 if age is greater than 18 you are an adult. Here's the condition here the condition age is greater than equal to 18 since age is 20 the condition is true. So the message is print is printed. If age were 15 the condition would be false and nothing would be printed. The if else block. What if you want to if you want something to happen when the condition is false as well? That's what else is for. So you have let's say age equals to 15. If age greater than equal to 18 print line you you are an adult. Else print you are not an adult yet. The else block runs only the when the the if condition is false. Exactly one of the two blocks will always run. Never both never either. Think of it as a fork in the road. The program reach reaches the if checks the condition and takes on two parts. It always picks one. If else if and else sometimes two options are aren't enough. You need to check multiple condition in sequence. This that's where else if comes in. So else if comes in and as you can see in this diagram checks each condition from top to bottom. The moment it finds one that is true, it it runs that block and skips all the remaining branches. If one of the condition are true, the final else block runs. If I'm going to repeat that, if none of the conditions were true or are true, the final else block runs. So if all the condition are false, the u the else block is going to be executed. This is important. Only one branch ever runs. Regardless of how many else if clauses you have, the conditions are checked in order. And the first true always wins. The first true one always wins. If as an expression and if you wonder what an expression is, an expression is a something which which is uh returns a single value. So here is something that makes cotlin different from any other programming languages. In quotes the if is an expression that means it can return a value. So if you have val age 20 and val status if age greater than equal to 18 adult else minor the entire if else evaluates the one of the two values and assign it to status. If age is 20 status became became adult. If age is 15 status became minor. This is much cleaner than declaring a variable then reassign it inside an if else block. You can declare the variable as a value immutable because you're setting it in one step. So you have so let me go here. When you use it as an if expression that assigns a value you must have an else branch. Without it cotlin doesn't know what value to return when the condition is false. And this is the cheat sheet. So we have basic if runs a block when the condition is true skips it when it's false. If else two branches exactly one always runs. If else multiple condition check top to bottom first true one wins else catches everything else. If as an expression val result if condition value A else value B returns a value it must have else when used this way. Condition must be boolean. The expression in parenthesis must evolate to true or false, not a number, not a string. Welcome back. In the last video, we covered the if statement. We checked a single condition and decided what to do based on whether the condition is true or false. But in real programs, decisions are rarely that simple. You often need to check two things at the same time or check whatever at least one of several things is true. That's where the end and or operators come in. Today we're covering the logical operators 2% sign which is end and two pipes which is or. This let you combine multiple boolean conditions into a single expression. So let's go. The end operator. The end operator is written as two emp% signs. So we have two emp% signs. It combines two conditions and returns true only if both conditions are true. If either one is false or both are false, the whole expression is false. So if I type val age 25 and val has ticket true. If age is greater than 18 and has ticket is true going to print you can enter. For this to print you can enter both condition must be true. Age needs to be greater than or equal to 18. And uh has t must also to be true. The moment the moment either one is false the whole condition is false and the block is skipped. Think of it as the strictest bouncer at the door. You need to meet every requirement, not just some of them, all of them. Let's look at the true table at all the possible combinations. So you have true and true returns true. Both satisfied. True and false returns false. False and true returns false. False and false returns false. The only way to get true or true out of an end operator is when both sides are true. The or operator. The or operator is written as two vertical pipes. It combines two conditions and returns true if at least one of them is true. It only returns false when both are false. So if at the time val is admin and val is moderator true and is admin equal to false. If is admin and so if is admin or is moderator print line access is granted here. Even though admin is false, if moderator is true. So the or expression is true and the block runs. Think of it as more lenient check. You qualify if you met at least one of the criteria. Even one is enough. The two the truth table for or true or true. True. True or false? True. False or true. True. False and false. We're going to return false. The only way to get false out of the or operator is when both sides are false. The end versus or sideby-side comparison. Let me put them side by side to contrast to see crystal clear what we have. So we have end which all condition must be true and war which at least one condition must be true. the key mental model and raises the bar. Adding more and more condition makes the overall condition harder to satisfy. Each new condition is another requirement that must be met. Low the or lowers the bar. Adding more or conditions makes the overall condition easier to satisfy. Each new condition is another way to is another way the expression can be true. Choose and when all requirements must be met simultaneously. Choose or when any of requ any any of so choose or when any one requirement is sufficient. Combining with if. Now let's look to see operators in context with real if statement. This is where you'll get use them the most. check you multiple requirements all almost pass. So say you have val age val has license val has car and you type if age greater than 18 and has license and has car are true then you can execute a block of code which is going to say you're going to print in the console or you can drive. You can chain more than two condition with end. All of them must be true. Checking for any qualification condition. So checking for any qualifying condition. If type again val is student val is senior val is employee and we have for the is student false for is senior true for is employee false. So if you type if is student or is senior or is employee print discount applied. Same with or. Any one being true is enough for the block to run. You can also mix and or in the same expression. But be careful about precedence. And the end operator is evaluated before the or just like multiplication before addition. When in doubt, use parenthesis to make your intent explicit. Without parenthesis, the Q the end would be would bind first, giving a different result. Parenthesis remove all ambiguity. There's one important behavior to understand. Short circuit evaluation. Cotlin doesn't always evaluate both sides of a logical expression. It stops as soon as the result is determined with end. If the left side is false, cotlin already knows the whole expression is false. It skips the right side entirely. It doesn't even evaluate it. So if you type val user string and we have here the lvis operator I'm going to cover later in the code lvis operator I'm going to put this is for making the variable null so we have equals to null and if user is not null and userlength is greater than zero print valid user if user is null the left side user not null is false stops there it never evaluates userlength greater than zero which would crash if you did because You can't call that length on a null. Short circuit evaluation makes this safe with or if the left side is true. Cotton already knows the whole expression is true. If it skips the right side. So if is admin and sorry not end. So if is admin or check databases. If is admin is already true. Check database is never called. This matters for performance. You don't want to run an expensive function when the result is already known. Short circuit evaluation is not just a technical detail. It's a practical tool. Put the put the cheaper, more likely condition first. All right, I'm going to end this video here and I'll see you in the next video. I'm back. Today we're covering one of Kotlin's most powerful and elegant features, the when statement. If you use other languages, you might recognize this as similar to a switch statement. But uh when in cutting in is significantly more capable. By the end of this video, you'll know how to use when as a statement, how to use it an as an expression that returns a value and how many different ways you can match condition inside it. So what is when? The when construct replaces the long chain of if else blocks you'd otherwise write when you have multiple conditions to check against the same variable. Instead of writing this, you write what you see. You write what you see on the right hand side. The when keyword is followed by the value you want to match in parenthesis. Then each branch is written as the value to match, an arrow, and the code to run. The else branch is the fallback. It runs when none of the other branches match. Immediately cleaner, more readable, and less repetitive than a chain of LCF blocks. when as a statement. Let's look at when used as a statement. Meaning that it runs cut but it runs code but doesn't produce a value to assign to variable. So we have val direction north and and okay so you have val direction north and you have when direction north, south, east, west and have else. Each branch matches a specific value. When Kotlin finds a match, it runs that branch and stops. It doesn't fall. It doesn't fall to the next branch the way some other languages do. If none none of them match, else runs. You can also run multiple lines of code in a branch by wrapping them in curly braces. When as an expression, just like if when in cotlin can also be an expression. It can return a value. This is where it becomes especially powerful. So you have val day have day name when day is equal to 1 2 3 4 5. The entire when expression evaluates to one of the string values and assign is to day name because it covers all possibility with else knows it always knows it always produces a value. So if you can if you can use val and assign it directly when you use when as an expression the else branch is required without it cotling can guarantee that every possible input has a matching branch so it can it can't know what value to return multiple values per branch one of the one one of when's most useful feature is that a single branch can match multiple values you separate them with commas so we have val season and we have equals when month and have 1212. So we have this matching multiple values. Month uh month four matches the branch 3 4 5. So season becomes spring. You don't need to write a separate branch for each value. Any value in the comma separated list to trigger that branch. This is one of the things that makes when so much cleaner than else if or if else. Grouping related values together in a single branch is both concise and readable. You can also match ranges using the in keyword. This is something traditional statement simply cannot do. So we have val score 75 val grade when score and we have yin 19 until 100 and we have a the in keyword checks whether the value falls within the given range 90 to 100. This this is a range from 90 to 100 inclusive score is 75 which falls into 70 17 and uh 17 and 79. So grade becomes C. You can also use the not in to check whether a value does not fall in a range. Okay. when without an argument. There's one more form of when worth knowing. Even without an argument, when you leave out the parenthesis and the variable, each branch becomes a full boolean expression. Just like an LC chain, but cleaner. This form is most useful for when each branch condition is different. Not all checking checking the same variable. Each arrow condition is evaluated in order and the first true through true one runs. This is functionally equivalent to a chain of if else statements. But many custom developers find this form cleaner and more readable especially when conditions are complex. Okay, let's wrap it up. When when as a statement runs code for matching branch no value return. Else is optional but recommended. When as an expression returns a value, assign it to a variable. Else is required to ensure a value is always produced. Arrow syntax value arrow action clean. No fall through between branches. Multiple values 1 2 3 arrow action comma separated values one branch. Range matching in one till 10 in dot 10. Check if a value falls within false in a range not in reverse. when without argument. Each branch is a full boolean expression like else if else was but cleaner. Else branch the fallback runs when no other branch matches required when using when as an expression. All right, I'm going to end the video here and I'll see you in the next video. Come back. Today we're covering the for loop. Loops are one of the most fundamental concepts in programming. They let you repeat a block of code multiple times without writing it out manually. The for loop is the most common loop you'll write and cotlin's version of it. It's clean, flexible, and expressive. Let's go through all of the forms it takes. So what is a for loop? A for loop repeats a block of code for each element in a sequence. Instead of writing the same logic five times, 10 times or 100 times, you write it once and let the for loop handle the repetition. The basic structure in cotlin is for item in collection and say print line or do something. The keyword for starts the loop. Inside the parentheses, you declare a variable variable item that represents the current element on each iteration. The in keyword connects that variable to whatever you're iterating over. The curly braces contain the body, the code that runs once per element. Cotlin's for loop works with anything that is iterable iterable iterable ranges lists arrays strings maps and more let's start with the most common cases ranges looping over a range a range is a sequence of numbers from start to end value you create a range with the dot dot operator so it I four I in one dot dot five print line I the iterators from 1 to 5 are inclusive. The variable I takes the value one on the first iteration, two on the second and so on up to five. If you want to exclude the end value, use until instead of dot. So for y in one until five and five is not going to be printed. One until five means from one to odd up two but not including five. You can also count down using the down two. So you type four I. So four I in five down to one. So this prints four five 4 3 2 1. And we can change the step how much to increment or decrement each iteration using step. So type four I and one to do 10. Step two. Now, looping over a list. We didn't cover list yet, but we're going to I said to you in a previous video that a list is a single variable name which can hold multiple values. So, ranges are great for numeric sequence, but you'll also loop over collections. Going to see what collections are later in the course. List, arrays, and so on. So, if have a list of val fruits, list of apple, banana, cherry. If you type for fruit in fruits print fruit you're going to going to output in the console apple banana and cherry the loop variable fruit takes on the value of each element in turn on the first iteration is apple on the second is banana on the third is cherry clean and readable if you need the index alongside the value use with index so we type four parentheses parenthesis again index fruit in fruit with index and that's going to print zero Apple one banana two cherry with index gives you a pair of index and value on each iteration. You do the structure it with parentheses. This is the ideomatic cotton to loop with indices cleaner than manually tracking a counter. Looping over a string. A string in cotlin is iterable. You can loop over it character by character. So you can type val war codlin for forch char or yes forch char in word print line char that's going to print k o t l i n. Each iteration gives you a char a single character from the string. This is useful when you're processing text checking characters building output characters by character or counting occurrences and so on. Controlling the loop with break and continue. Sometimes you need more control over loop execution. Cotling gives you two key also for this break and continue. Break exits the loop entirely when a condition is met. So if you type 4 I in 1 10 if I equals equals 5 break as soon as it equals five break fires and the loop is done. Nothing after the if runs and the loop doesn't continue. Now now let's look at continue. The continue skips the current iteration and moves to the next one. So if you type four I and one to and one 10. So one until 10 including if I percent 2 equals zero continue print I and that's going to print 1 2 sorry 1 3 5 7 and 9 only the inver even numbers are skipped when I is even continue fires and that iteration prints line is skipped. The loop moves to the next value of I. Break and continue gives you the ability to exit early or skip specific iterations based on runtime conditions. So let's wrap it up. Basic range four I in 1.5 one through five inclusive until for I in one until five one through four and excluded down to for I in five down to one countdown from five to one step four and so on. I'm going to end this video here. You're going to see the next ones here in this slide. All right, I'm going to end this video and I'll see you in the next video. I'm back. Today we're covering the remaining loop types in Cotlin. While, dual, and labels. In the last video, we covered the for loop, which is great when you know what you're iterating over. But sometimes you don't know how many times a loops needs to run. You just know it should keep going until a condition changes. That's where while and do come in. and labels give you precise control when you're working with nested loops. So let's get into all of three. The Y loop. The Y loop is the simplest conditional loop. It checks a condition before each iteration and keeps running as long as that condition is true. So we have V count while count is less than equal to 5. Before each iteration, Cotlin checks if count is less than equal to 5. If yes, the body runs. If no, the loop ends on the first iteration. Count is one. True. and it runs on the fifth iteration count is five is still true and runs. After that count becomes after that count becomes six the condition is false the loop ends. The key thing to understand is that the condition is checked before the body runs. If the condition is false from the very beginning the body never runs at all. So if you have var x while x is less than equal to five and you have print this never prints x x is five which is not less than five. So the loop body is completely skipped the while loop in practice. While loops are ideal when you don't know how many in iterations you'll need when the loop should continue until something happens rather than for a fixed number of time. A classic use case is reading input until a sentinel value a certain value is received. So type v input while input is not equal to quit. Print enter command and you get the input read line. This loop keeps asking for input forever until the user types quit. You can't write this with a for loop because you don't know in advance how many times the user will enter commands. Another common pattern loop while the condition of your data is true. So if you type v number 256 while number is greater than one number divided equals to two the number of iterations depends entirely on the starting value that's the power of while the loop duration is driven by data not a predetermined count the dow loop the dile loop is a variation of y loop with one c crucial difference it checks the condition after the body runs. This guarantees the body executes at least once no matter what. So you have v count equals one and you have do print line count count++ while count is less than equal to five and it prints 1 2 3 4 5. The structure is do first check condition after the body runs then count increments the condition is checked. If it's still true, repeat. If false, stop. The critical difference from while becomes clear when the position starts as false. So if you have vx= 10, do and you have do curly braces while x is less than equal to 5. This runs once and it prints 10. Why? With do while the body run once even though the condition was false. With while the body never runs. This is the definition characteristic of the do while at least one execution is guaranteed. When to use each? Let me give you a clear decision guide for choosing bet between four while and while. Use four when you know what you're iterating over a range, a list, an array, a string, something concrete and bounded. The for loop is clean and expressive for the these cases. Use while when you're waiting for a condition to become false. We don't know how many iterations it will take and the body should run if the if the condition starts false. User input loops retry logic pooling while your while is your tool. Use do while when the body must execute at least once before the condition is even checked. Man display initial setup validation that needs to prompt at least once. Do while ensures the first run always happens. A help a helpful way to remember four for each item in this sequence while keep going while this is true. Do I do this and keep going going while this is true? Labels. Now let's talk about labels. To understand why labels exist, you need to see the problem they solve. Consider the nested loop a loop inside the loop. The break inside the inner loop only breaks the inner loop. The outer loop keeps running. So you get I = 1, I = 2, I = 3 and J = 1, J = 1, J = 1. The inner loop breaks when J J reaches two. But the outer loop continues to I = 2, I = 3. What if you wanted to break out both loops entirely when J reaches 2 with plain break? You can't. You need the flag variable an extra if check and it's messy. That's why exactly that's exactly what labels solve using labels with break. A label is a name followed by the at symbol placed before a loop. When you use break at label name or continue at label name to target that specific loop. So we type out at four. So we label our outer four and have four J in 1 2 3. If J equals equals 2 break outer print I and J. Break at outer tells Scotland break out of the loop labeled outer not just the nearest loop. When J reaches two on the very first I iteration the break fires and jumps completely past past both loops only one line prints the label name is up to you outer loop main loop anything followed by add the label goes on the loop you want to control you can use the same approach with continue. So you have outer at four and you have for loop and you have another four and you type if equals to continue at outer continue at out outer skips to the next iteration of the outer loop instead of the inner one. So when J reaches two instead of continuing the inner loop it jumps to the next value of I. This give you fine grain control over exactly which loop responds to your control statement. So labels work with while and do while loops too not just for not just for for loops. So type search add while true val line read line and I'm going to read something from the input for warning line that's split if word equals stop break search. Here the outer while loop is labeled search. Inside there is a for loop. When word equals stop, you type you type break search and break search fires and exist the exist exit sorry the while loop entirely not just for the for loop. Labels work the same way regardless whether the loop is for while or do while break and continue and labels summary. Let me put the control flow tools together clearly. Break ex exits the nearest closing and closing loop. Break at label exits the loop with that label no matter how many levels up it is. Continue skips the next iteration of the nearest enclosing loop. Continue at label skips to the next iteration of the label loop. The label must be placed directly before the loop is named. So you have my label at four. Labels are most useful when you have two or three labels of nesting and need to escape or continue an outer loop in response to something that happens in the inner one. If you find yourself reaching for labels frequently, it might also be worth considering whether the nested loops could be refactored into separate functions. But for cases where they generally needed, labels are clean and expressive. All right, I'm going to end this video here and I'll see you in the next. Welcome back. Today we're covering one of the most important concepts in Cotlin. Null safety. This is one of the features that makes Cotlin generally better than Java for Android development and understanding it properly will save you from an entire class of bugs that Java application have. We're going to cover what null is, why it's dangerous, how cotling protects you from it, and every tool codling gives you to work with nullable values safely. So let's get into it. So what is null? Before we talk about safety, let's be clear on what null actually is. Null means the absence of a value, not zero, not an empty string, not false, the complete absence of a value. Nothing is there. As you can see in this uh example, in many languages, any variable can hold null. You declare a string and instead of holding text, it holds null. Meaning the reference points to nothing. The problem is that the moment you try to use that variable co method on it access property pass it somewhere your program crashes with a null pointer exception. Tony the computer scientist who invented null reference references back in 1965 called it billion dollar mistake. Null related crashes are among the most common bugs in software history and for decades they were considered just a fact of life in languages like Java. Cotlin was designed to eliminate this class of bugs entirely. So let's look at non-nullable versus nullable types. Cotlin's approach is elegant. The type system itself distinguishes between values that can be null and values that cannot. By default, every type in cotlin is non-nullable. This means null is not allowed. So you have val string equals to Alex and val name string equals to null. The last line will not be will not compile. The compiler the compiler flat out refuses to let you assign null to a non-nalable string. This guarantees is that it's in force at compile time before your app even runs. If you generally need a variable that might hold null because say a user hasn't entered their name yet or you haven't loaded data from the network yet, you opt it in by adding a question mark to the type. So you type val string question mark null. This question mark makes all the difference. String and string question mark are different types in cotlin. String is guaranteed null. string question mark might might be null and the compiler knows it. This is the foundation of everything. The question mark is the key to understand cotlin's null safety. The safe call the null safety problem. Now here is what happens when we try to use a nullable type without checking it first. So we type val string question mark equals null and type print line name.length the length and that's going to return the size of the string. Cot won't let you call that length on a on a nullable string directly because name might be null and calling it that length on null would crash. The compiler stops you before you can write you can write this bug. This is fundamentally different from Java. In Java, you could call methods on null references and the compiler said nothing. You'll get an alpointer excession at runtime when a real user was running was running already your app in cotlin the compiler catches it at build time. So how do you actually use a nullable value cotling gives you several tools. Let's go through each one. The safe call operator. The first and most common tools is the safe call operator. A question mark followed by a dot instead of crashing name dot sorry name question mark.length returns null if name is equal to null. If name has a value it returns the length normally. The safe call short circuit if the left side is null. The whole expression returns null without evaluating the right side. You can change safe calls like user question mark address question mark city. This is like checking if user equals null or if it's not equal to null. So again the question mark acts like a like a check. It checks to see if that is null and only if it's not null is going to return the value. So if the user is null the whole expression returns null. If the user is not null but address is null still returns null. Only if both are not null do you get the city each question mark dot. So each safe call operator in the chain protects against null at the at that step. The safe call is the everyday tool for working with nullable values is what you'll reach for most often. Next let's look at the Elvis software operator. Often you don't want null check. If you want a default value and something is null, that's what the Elvis operator does. The Elvis operator is written as a question mark followed by a colon. So you have val string question mark equals null val display name. You have question mark colon quest in the left side. If the left side is not null, you get the left side. If it's null, you get the right side. The default the name comes from the fact that the Elvis operator looks a bit like Elvis Presley hair when you tilt your head. You can combine Elvis with safe call. So you can type V val length equals name question mark.length question mark again colon zero. If name is null name question mark.length returns null and then and then the Elvis operator kicks in and gives you zero instead. This pattern safe call followed by Elvis is one of the most common patterns in cotling code. You can also use Elvis to return early from a function or to throw an exception. The double bang operator. Now let's talk about the non-null assertion operator. two exclamation marks. This operator tells Kotine, I know this value is not null. Trust me, skip the null check. If name it's actually null when this line run, this uh line runs, it throws null pointer exertion. You are explicitly opting back in the null crash behavior that cotin protects you from by default when you should use the double bang operator. Almost never. It's essentially saying I know better than the type system and most of the time it's that's a bad bad idea. The only legitimate use cases are when you have a external proof the value cannot be null like when you already checked in a way the compiler can't see or when working with certain framework framework callbacks in tests where crash is actually the correct failure mode. In practice, if you find yourself reaching for the double bang operator, stop and ask what whether you can use a safe call Elvis or a proper null check instead instead of the double bang operator. It's a code smell. It makes it means you're giving up null safety. Null checks with if. The most straightforward way to handle nullable values is a plain if check and cotlin has powerful features called smart cast that make this really clean. So if you have a variable name and uh it's a string analable one and you assign Alex to if you type if name is not equal to null inside the if block cotling knows that that is not null the check guarantees it. So the type of name is automatically null from string nullable string to string. You can use it as a regular non-nullible string without any uh any nullable operators or safe call operators. This is called a smart cast. The smart cast only was inside the block where the check is guaranteed. If name were a var that could be changed by another thread, the smart cast wouldn't apply. But for val properties and local variables it works beautifully. You can use the same approach with else to handle both cases. Now let there is one more null safety tool worth knowing. The left function combined with the safe call operator. The let sorry not left. The let function combined with the safe call operator. The safe call operator let pattern runs the block only if the name is not null. Inside the the block, it refers to the non null value. If name is null, the entire block is skipped. This is particularly useful when you want to run multiple lines of code only when a value is present without writing an explicit if check. It's ideatic cotlin. You'll see this a lot in real code bases. All All three lines only run if user is not null, clean, safe and concise. I have an example here. This is why all three lines user question marks do let and type update it.name it.p profofile it first name. So this is what I have in my transcript example. We don't have this here actually. Let me check actually. No, we don't have it here. So null safety tools summary. Let's wrap everything up. Non-nullable types string int boo boolean cannot hold null. The default in cotlin is this. Compiler guarantees it. Nullable types string question mark int question mark boolean question mark can hold null or a real value. Requires explicit handling. The safe call name question mark.length length returns null if the name is null otherwise the length otherwise the length no crash chain them free freely okay and these are the next examples that we looked at I'm going to end this video here and I'll see you in the next video back today we're covering functions functions are one of the most fundamental building blocks of any program once you understand them properly the way you structure your code changes completely so let's get into it. So first, why do you need functions? Before writing any code, let's talk about why functions exist at all. Imagine you're writing a program and you need to greet a user. You write print line quotation marks, hello Alex. Then somewhere else in your program, you need to greet someone again. So you type print line again, hello Maria, and again, hello Sam. Now imagine that greeting logic gets more complex. Maybe you add a title, format the name differently, add punctuation. Every time you want to change the greeting, you have to find every single place it appears and update it each one. That's fragile, repetitive, and hard to maintain. Function solve this. A function is a named reusable block of code. You write the logic once, give it a name, and then call that whenever you need it. If the logic changes, you change it in one place and every call benefits auto automatically. Functions give you three things. Reusability, write once, use many times. Readability. A well-n named function makes code code read like plain English. And organization breaks a large problem into small focus pieces. Let me show you how you declare a function in Kotlin. You type the fun keyword tells Cotlin that this is a function. Then comes the function name grid followed by parentheses and the block of code in curly braces. That's the function body. The code that runs every time the function is called. To use this function you call it by writing its name followed by parenthesis. So it greet print hello greet print hello again and again. One definition called as so one definition called as many times as needed. Anytime the greeting logic changes you update it in one place. The parenthesis are always required both when defining the function and when calling the function. They're not opt optional. there what tells cotlin this is a function call not just a variable reference function parameters right now our grid function always prints the same thing that's fine but limited to make function function flexible you give it par you give it parameters a parameter is a variable declared inside the parenthesis of a function it let you pass information when you call the function you have fun grid name colon string now The function has a parameter called name of type string. When you call the function, you provide the value. So like greet Alex, greet Maria, greet Sam. Each call passes a different value for name and the function uses inside the body. The value you pass when calling the function is called an argument. So parameters are defined in the function. Arguments are the actual values you pass in. Multiple parameters. Functions can have more than one parameter. You separate them with commas. So you type grid name string comma greeting string and you type can when you call it you type greet Alex and also value for the second parameter. When you have multiple parameters the arguments you pass must match the word they they they were declared Alex goes to name hello goes to greeting because that's the word they appear in the function definition. You can have as many parameters as you need through though in practice if a function needs more than three or four parameter that's often a sign that the function is doing too much and could be broken down further but there is no technical limit to how many parameters you can define return values. So far our functions have printed things but haven't produced any value you can use that changes with return values. A function can compute something and hand the result back to whoever call called it. You specify you specify the return type after a colon following the closing parenthesis of the parameters. So you type fun add the parameters a and b outside the parenthesis you put colon and int. The colon int after the parenthesis tells code this function returns an int. The return key sends the value back to the caller. the caller receives it and can store it, use it as an expression or pass it to another function. The return type is part of the function contract. If you declare a function a function that returns int, it must always return an int. The compiler enforces this. If a function doesn't return anything useful, it returns that it its return type its unit. But you don't need to write that explicitly. Functions without a return type declaration implicitly return unit. Single expression functions when a function body is just a single expression code lets you write it in a more concise way using the equals sign instead of curly braces. So you can type fun add a b parameters return int and put equals instead of curly braces a plus b. The equal sign directly assigns the expression as the functions return value. No return keyword needed. No curly braces just the function signature equals an expression. Kotlin can even infer the return type in many cases. So you can write fun add a and b. And now we don't put colon int with type equals a plus b and the int is inferred. This is called a single expression function. You'll see it everywhere in coding code. It's not different a different kind of function. is the same thing. It's just shorter syntax for simple cases. Use it when body when the body is simple enough to fit it cleanly on one line. All right, I'm going to end this video here and I'll see you in the next video. Back. In the last video, we introduced functions, how to declare them, how to call them, how to pass parameters. Today, we're going deeper into two specific topics, return and single expression functions. These are things you'll use in almost every function you write. So let's make sure you understand them completely. What does return mean? So far the functions we've written have done things don't done things printed output modify state. But many functions need to do something more than do than doing this. You need to compute a result and hand it back to to whoever called them. That's what return means. When a function returns a value, the caller receives it receives it and can use it. Store it in a variable, pass it somewhere else, use it and is use it in as an expression. Think of it like ordering from a restaurant. You send the request to the chicken. The chicken does the work. The chicken sends something back. You receive what comes back and do something with it. We have fun add a return a + b and type val result 35. and it's going to result. The result is going to be eight. The function does the work. The return cure sends the result back. The caller receives eight and stores it in result. Declaring return type the to return a value from a function. You declare what type it will return. The return type goes after the closing parentheses of the parameters separated by colon. So we type fun multiply a b and it's going to return a * b f full name fun it's adult the pattern is always the same after the closing parenthesis colon then the type the return type is part of the functions construct it tells every caller exactly what they'll get back the compiler enforces this if you declare a return type of int but your function in in in but in your function you try to return a string, you get a compile error. If your function has a branch that might not return anything, like an if without an else, the compiler will also tell you every path through uh every path through the function must produce a value of the declared type using the return value. Once a function returns a value, you have complete freedom in how you use it. You can store it in a variable. You can use it directly in a in an expression. You can pass it in as an argument to another function. You can use it in a condition. A return value is just a value. The moment the function returns it, it behaves exactly like any other value of that type. You can do anything with it that you could do with literal or with a literal or a variable of the same type unit. What about functions that don't return anything useful? Functions that just print something, update a variable or trigger an action. Encoding those functions have a return type called unit. Unit means no meaningful value is returned. In practice, you never write unit explicit. When you leave it out, the return type entire entirely. Cotton assumes unit. The two declarations below are completely equivalent with a van grid name colon unit fun grid name and we have nothing. If you use Java unit is cotton equivalent of void. It signals this function is called for its side effects not for a value of it of it produces single expression functions. Now let's talk about single expression functions. One of my favorite cotlin features. When a function b is just an expression one thing it computes and returns. You don't need to to put curly braces or the return keyword. You use it an equal sign instead. So type add a b of type in return a plus b. And the single expression function you type add a and b equals a + b. The equal sign replaces the return. The the curly braces and the return. The expression to the right of the equal is what the function evaluates and returns. That's it. Both versions compile to exactly the same by code. There is no performance difference, no behavioral difference. It's purely a syntactical shorthead or syntactical sugar for for when the body is a single expression. Type inference with single expression functions. There is something extra that single expression function give you. When you write a block body function, you must always declare the return type explicitly. The compiler requires it because it needs to know what type the function produces as part of its public contract. But it's a single expression function. The expression on the right of the equal sign. It's right there. The compiler can read it and figure out figure out the type it produces. So you can leave the return type annotations off. Or you can do it without the colon and the type. Cotlin reads the expression, determines its type and uses as the return type. No annotation in it. This makes single expression functions extremely concise. The entire function name parameters and return logic fix fits on one line. You'll see this style everywhere in professional codelink code. All right, this is our cheat sheet. I'm going to end this video here and I'll see you in the next video. Back. Today we're covering function overloading. This is a concept that lets you define multiple functions with the same name as long as their parameters are different. It's a simple idea but a powerful one and you'll see it constantly in Cotlin in Android and Android development. So let's get into it. So what is function overloading? Function overloading means defining multiple functions that share the same name but have different parameter lists. Consider a grid function. Sometimes you want to greet someone with just their name. Some sometimes you want to include a custom greeting. Instead of inventing two different names like grid name and grid uh grid with greeting, you define two you define two functions both call grid but with different parameters. So I find grid name string fun name string and greeting. Now both of these exist and Kotlin knows which one to call based on arguments you on arguments that you provide. We have greet Alex and greet Alex. Hi. Cotlin looks at what you pass in pass in and picks the matching function. This is called overload resolution. How cotlin tells them apart. You might be wondering how does cotling know which function to call? The answer is the function signature. A fun functions signature is the combination of its name and its parameters. So a function signature is the combination of its name and its parameter list. The number of parameters, their types, their their order. When you call a function, Cotlin matches the arguments you provide against all the available signatures and picks the one that fits. So you have three functions, same name, different parameter types. Cotlin is the type of what you pass in and routes the call to the correct version. This all happens at compile time. The compiler resolves it before the app runs. Overloading. So overloading by count. One of the most common forms of of overloading is having different numbers of parameters. Each version handles a different level of details. You have create username string, create username string agent, create username, age and city. So we have each version builds on the previous one. You call the version that matches the information you have available. The function name stays consistent. Create user or means create a user. The parameters just carry more of less detail. You can also overload based on the type of parameters even when the count stays the same. So we have square value int square value double. The function is called square in both cases. Quotly looks at the type of the argument int or double and picks up the matching version. The caller doesn't need to think about it. They just call the square with whatever value they have. This is particularly useful for mathematical or or utility functions where the logic is identical but the types differ. You write the logic once per type, give it the same name and callers use whichever fits their data naturally. So what makes a valid overload? Let's be precise about what we cannot make a valid overload. What works? What works? The parameter list must differ in at least one of these ways. Different number of parameters, two parameters versus three parameters, different parameter types, int versus double versus string, different parameters order if the types differ. What does not work? The return type alone cannot distinguish overload. So we have fun get value int return one. Fun get value return the string one. You have a compile error here. These two functions have identical parameter list. Both take no arguments. The fact that they return different types is not enough. The compiler can't know which one you want to you want by looking at the call side get value. There are no arguments to differentiate them. This is a compile error. The rule is simple. Overloads must be overloads must be distinguish distinguishable by their parameters alone. Why use overloading? So why bother with overloading? Why not just use different function names? Because naming is one of the hardest problems in programming and overloading reduces the naming burden significantly. Without overloading, you would end up with functions like grid with name, greet with name, and greeting grid greet user from name uh sorry create user from name, create user from name and age, create user name, age and city. This that's messy and hard to remember with overall aence. You just type greet and create user. The name communication comm the name communicates intent. The parameters communicate detail. The caller picks the version that fits their situation and all versions live under the same logical name. Overloading also makes API easier to use. When you're using an Android API or a cotton standard library function and you see the same function available in several flavors, that's overloading at work. Okay, let's wrap it up. Overloading multiple functions with the same name but different parameter list. Overloading resolution cotton matches the call to the correct version at compile time on the based on the arguments. Differentiate by count a int and fan a b a and b are valid overloads. Differentiate by type fun fa and fun f a string. fun a 8 of type in and f of i sorry you so I'm going to say it again differentiate by type so you have fun parenthesis a of type in fan fun a of type string are valid overloads returns return types type alone not enough to overload fun and fun f string is a compile error y one name multiple parameter option options cleaner API, less naming of overhead, more intuitive to use. So, I'm going to end this video here and I'll see you in the next video. Come back. Today, we're covering the v ar keyword. It's a small feature, but a useful one. It lets you pass any number of arguments to a function without changing the function signature. So, let's go through it. So, what is var? Var stands for variable arguments. When you mark a parameter with var, the caller can pass zero, one or as many arguments of type as they want. Without var, if you want to sum numbers, you have to decide up front how many parameters to accept. The only and have sum a and b you have a plus b equals that only works for exactly two numbers. If you need three, you'll have to overload. With var, you accept any number at once. have some one two one two three one two three four five with var you can achieve this one function handles all of them the caller passes as many as they need and the function receives them all how var works inside the function inside the function the v parameter behaves like an array you can loop over it check it size index into it anything you do with a regular array the parameter items is of type array of string inside the function the caller just pass this individual arguments separated by commas. They don't build an array themselves. You can also call var function with zero arguments. Zero is practically valid. Valid the function gets an empty array. VR parameter can be combined with other parameters in the same function. The var parameter should generally come last. We have greet all and we have we have greet all and we have four name in names print line greeting and name. The first argument fields greeting. Everything after feels name. The var parameter absorbs all the remaining arguments. One rule worth knowing. You can only have one var parameter per function. Having two var parameters in the same function is a compile error. Cotlin wouldn't know where one ends and the other begins. You have the spread operator but and the spread operator it's an aster. What if you already have an array and you want to pass it to a v ar function? You use the spread operator which is an asteric in front of the array. So type val numbers. For whatever reason I don't have a slide with a spread operator. I thought I added one but there is. There isn't one. Actually there is one here. As you can see in you have val nums you have the spread operator here. I'm going to repeat the spread operator. What if you already have an array and you want to pass it to a var function you use the spread operator an asterisk in front of the array as you can see on the right hand side in this diagram. Without the asterisk the spread operator cotlin would expect individual int arguments but you're passing an array. That's a type mismatch. The aster compacts the array. So each element is passed as a separate argument. We have val nums in array 1 2 3 4 sum the asteric operator nums. The first operator bridges the gap between a have an array and this function expects individual arguments. You use it whenever you have collected values in an array and need to forward them to a var function. All right I'm going to recap. So what we covered var marks a parameter to accept any number of arguments of the same type inside the function. The var parameter is an array use its size index it loop over it. Zero is valid. Calling calling a v r function with no arguments is allowed. You get an empty array. One var per function. You can only have one var parameter is allowed. So it should be and it should be the last parameter. Usually the spread operator the asterisk use the spread operator the asterisk array to unpack an array of individual var. All right I'm going to end this video here and I'll see you in the next video. I'm back. Today we're covering default parameter values in cotlin. This is a feature that makes function calls much more flexible and clean and it's one of the reasons cutling code tends to be shorter and more readable than equivalent Java code. So let's get into it. The problem. In the last video we saw functions overloading defining multiple functions with the same name but different parameter list that works but it has a cost. Look at this uh set of overloads. You have create user name string create username string age create username string age and city. So you have three separate function declarations, three separate bodies all doing roughly the same thing with slightly different data. If the core logic changes, you have to update all three. It's repetitive. Default values solve this. Instead of three overloads, you write one function where some parameters have default values. Values they fall back to when the caller doesn't provide them. So we have fun create username string age and city and we have for the age equals to zero city equals to one no. So I have one function one body same three calling options. Declaring a default value is simple. After the parameter type, you write an equal sign and then the default. So we have found greet name string greeting string equals to hello. The parameter greeting now has a default value of hello. If the caller provides a greeting the value is used. If not hello is used automatically. The we have greet Alex, greet Alex hi and greet Alex hey. So you have first hello Alice, second hi Alice and the third hey Alice. The first call doesn't pass a greeting at all. Cotling uses the default one. The default the second and third calls provide their own greeting which overrides the default. The parameter with the default value must still have its type declared. The default is just a fallback. The type annotation is always required. Multiple default values. You can have multiple parameters with default values in the same function. Any or all of them can be given defaults. You have name string age int equals z city string unknown. It's active boolean equals to true. Now if you call the function with just a name or any combination of the other parameters you have create user Alex create user Alex 25 and uh true create Alex create sorry create user Alex 25 London create user Alex 25 London and false each call provides as many arguments as it needs the rest fall back to defaults one function body handles all four cases is there is an important rule about where default parameters can go. Require parameters those without defaults should come first. Default parameters should come after correct required parameters first. Fan connect host port a Z A Z time out in equals to 30 problematic default before require. So we have fun connect port and type e and equals a a z a z host string. Why does order matter? Because when you call a function with a positional arguments, cotlin fills them left to right. If you have a default parameter before a required one, you skip the default. Skip the default. Cotton has no way to know which argument goes where. You have connect local host 30. Connect local host 909030. When required parameters came first, every call is unambiguous. The first argument always fills the first parameter. No confusion. Default values versus overloading. Let's compare default values with overloading directory because they're solving the same problem in different ways. With overloading, you write three separate functions. Greet name string and uh grid name and greeting. Default values you write one fun greet name string greeting equals hello and I print line greeting name same calling flexibility greet Alex and greet Alex hi both work in either approach but with default values you have one function one body one place to make changes when should you use each each default values when the variation so use default values when the variations are about option detail. Some colors have all the information while others others have less and there is a sensible default when information is missing. Use overloading when each vers version has meaningfully different logic that can be expressed with a single body and a default value. In Kotlin, default values are preferred in most cases. They keep the code dry. Don't repeat yourself. All right, I'm going to end this video here and I'll see you in the next video. Welcome back. Today we're covering arrays in Catlin. Arrays are one of the most fundamental data structure in programming. A fixed size order container for multiple values of the same type. We're focusing specifically on simple arrays created with the array of function. We're not covering collection types like list or array list. Let's go through everything you need to know. What is an array? An array is a container that holds a fixed number of elements all of the same type in a specific order. Think of it as a row of numbered boxes. Each box holds one value. Each box has an index number starting from zero. And the total number of boxes is fixed at creation time. As you can see here, this val fruits array of apple, banana, cherry. This array has three elements. Apple at the index zero, banana at the index one, cherry at the index two. Three things to remember about arrays right from the start. First fix fixed size. Once you create an array with three elements, it always has three elements. You cannot add a fourth or remove one. The size is locked in. Second in index from zero. The first element it's always at the index zero, not index one. This is a convention almost every programming language and it trips out beginners content constantly. Third, same type. An array of strings holds strings. An array of integers holds integers. You cannot mix types in the same array. Cotlin type system enforces this. Creating array with arrays with array of the primary way to create an array in Kotlin is with the array of function. You pass the elements you want in Kotlin infers the type. So you have vals, numbers, and mix. We have Alex, Maria, Sam, 10, 21, 1, hello. Cotlin is the elements you provide and infers the element type. If all elements are strings, you get an array of string. If all are ins, you get an array of int. In the third example, mixing one, hello, and true infers array of any, the common superype of all three. While this compile is generally not what you want, keep your array type typed to a single specific type for clarity and safety. You can also create an array with the explicit type annotation. This is so you have scores with the explicit type annotation. Array of type array have curly braces int. This is equivalent to what you have previously. But the notation just makes the type explicit rather than inferred. Cotin also provides specializ specialized array of types for primitive values like int array, double array, float array, long array, boolean, boolean array, char array and bite array. We have val int, doubles and bulls. Why do do this exist? Because array less than sign int greater than sign in cutling compiles to Java integer types box integers which have memory and performance overhead in array compiles to Java int Java int in integer primitive integers which are more efficient for most application level code the difference doesn't matter much but for performance sensitive code processing large data sets graphics scientific computation ation using in array instead of array int can make meaningful difference. The rule of thumb if you're working with numeric data in bulk use the type array varants for general application code with strings and objects array of is fine accessing and modifying to access an element you use the square brackets with the index. So if you type val fruit of you type if you want to get an element you type fruits curly curly brace square brackets zero and it's going to get the apple index zero gives the first element index one the second and so on. To modify an element use the same bracket syntax on the left side of an assignment. So we type fruits one blueberry. The array now has a at zero, blueberry at one, and cherry at two. One critical thing, accessing an index that doesn't exist throws an array out of bounds exception at run time. So if you try to get fruits at the index five, the array the array only has indexes 0, one, and two. Accessing index five is out of ones. Always make sure your index is valid before accessing it or use the size property to check. Iterating over an array. The most common way to loop over an array with the four in loop. So you type val numbers array of you type for number in numbers curly braces print number. This is clean and ideatic. Each iteration the number holds the current element. If you need a index alongside the value use with index. So therefore parenthesis parenthesis index number in numbers with index. You can also use loop. You can also loop using indices. So which gives you the valid index range. So you type for i in numbers indexes. Number indexes returns the range from zero to size minus one. This is useful when you need to both when you need both the index and the element. And I want to emphasize that you're working with index positions. Useful array properties and functions. Array come up come up with a useful set of built-in properties and function functions. So let's go through the most important ones. Size which is the number which gives us the number of elements. So we type val array array of 1 2 3 print line array size. And that's going to return three. first and last. The first and last element with the print line array first array.ast and that's going to return one and three contains. You can check to see if an element is in the array. So we type array that contains two three true sorry print line two in array true in the in operator checks to see if two is in the array. Sum you can sum of all num numeric elements. So nums if you type one nums array of 10 20 and 30 print line nums do sum it's going to return 60 min and max the smallest and largest value. So you type nums mean parenthesis nums.mmax and you have 10 and 30 sorted returns a new list with elements in ascending order. So you type val sorted array of 3 1 41 5 sorted. Note that sorted returns a list not an array. If you need a sorted array, call to type to type array on the result. Reverse reverses the array in place. So you type val array 2 in array 1 2 3 4 array reverse. It's going to return 4 3 2 1 arrays. Now let's look at array size is fixed. Let me emphasize something that confuses many beginners. Array size is fixed at cor creation time. You cannot add elements to an existing array or remove them. Val names array of index Alex. So val names array of Alex and Maria. Names that add sum. Arrays have no add method. If you try to add an element, you'll find there is no add function. Arrays simply don't have one. The size is two permanently. If you need to if you need a container whose size can change, use a multiple list or array list instead. Going to cover those later. But for arrays, the size you create is the size you keep. What you can do is replace an existing element at a valid index. So you can type names zero Bob. Names five Bob. The names zero Bob is going to replace Bob at that position. And names five. So names square brackets five Bob is is going to throw an exception out of bounds because you don't have index file. This distinction is worth internalizing. Arrays are fixed size but multiple. You can change what's in each slot but you cannot change how many slots there are. Converting arrays. Sometimes you want to create an array of a certain size field is filled with a default value or computed values before you have the actual data. You can create an array filled with nulls using array of nulls. So var null array array of nulls. This creates an array of strings a nullable string array with five null elements. You can then fill the slots individually. For numeric types, you can use the constructor with a lambda to fill position based on their index. We're going to cover lambdas later in the course. The lambda receives an index i and returns the value for that slot. This is the most expressive way to initialize an array with computed values. You don't need a separate field loop. So we have val zeros in array 10. We have lambda zero. So we have 10 zeros vals in array five. You have y greater arrow i * 2. This is a lambda. I'm going to cover lambdas in uh later in the course but for now just need just need to know that you can do this with lambdas can create this list in this way. Okay. So let's look at converting between arrays and lists. Let me go back. In real coding code you often need to move between arrays and list. The conversions are straightforward straightforward arrays to list. You simply type val array array of ABC val list. You type array dot to list and that is going to create a list of strings. Val mutable list equals to array dot to mutable list and that's going to create a mutable list. You can change list to array. For primitive types you can type val int array list of 1 2 3 in array val back to list. You can type in array to list and that is going to create a list of integers. The conversions create new objects. They don't share memory. Changes to the array after conversion doesn't affect the list and vice versa. Knowing when to use each arrays of arrays are for fixed size. Performance sensitive data. Lists are for variable size general purpose of collections. When in doubt in modern coding code, use list. But when you're working with lower level operations operations interer interoperating with Java APIs or processing large numerical data arrays are the right tool. All right. And this is the what we covered in this video. Take a closer look or take some notes and I'm going to end this video here and I'll see you in the next back. Today we're moving into one of the most important topics in Cotlin and in programming as a whole. Classes. Everything we covered so far, variables, data types, functions, loops, those are tools. Classes are how you organize those tools into meaningful structure that model the real world. This is the foundation object of object- oriented programming. And once you understand it, the way you think about code changes fundamentally. So let's get into it. So why do you need classes? Let's start with a simple problem that classes solve. Imagine you're building a program that manages users. Each user has a name, an age, and an email. Without classes, you'd represent one user like this. That's three variables, three separate variables for one user. Now, imagine you have five users, 15 variables all floating around with no connection to each other. How do you know which age belongs to which name? How do you pass a user to a function? you would have to pass three separate arguments every time. This doesn't scale and is fragile and confusing. A class solves this by bundling related data and behavior together into a single name unit instead of three loose variables. You have one user object that carries all the three pieces of data as a coh cohesive hole. I have problem saying the word cohesive. So what is a class? A class is a blueprint. It defines what properties and objects will have and what actions it can perform. The class itself is not the data, is the template. From that template, you create instances, also called objects. Each instance is a real concrete thing with its own values. Think of it like a house blueprint. The blueprint describes how many rooms, where the windows go, the dimensions. But the blueprint isn't the house you can live in. You build instances from it. Actual houses. Each house follows the same blueprint, but has its own color, its own furniture, and its own address and have class username string empty int equals to zero. This is a class called user. It defines two properties, a name and age. And this is the blueprint val user. So val uh user one equals to user. In my in my example we have car one and car 2. So because in my my transcript I have user. This why I said user but in the example that you see now is is cars. They are cars objects. These are two instances two separate car objects. Each has its own name and age. Changing car one name doesn't affect car 2. Declaring a class. Let's look at the syntax for declaring a class properly. The class cure starts a declaration. Then comes the class name car capitalized by convention. Inside the curly braces you declare the properties. Properties are just like variables declared with val or v with a type and a default value. To create an instance of a car, you'll call you will call the class name like a function. So I val my car equals car. This creates one car object stored in my car. The object has its own make, model, year, speed, all starting at their default values. You can then set and read properties using the dot notation. The dot connects the object to its property. My car do makeake means the make property of the car of the my car object. So let's see how we can add functions to a class. Properties hold data but classes can also contain functions. And when a function belongs to a class is called a method. Methods defines what an object can do. We have class car var make speed and have two functions accelerate and break. Now car car has two methods accelerate and break. Those functions live inside the class and operate on the object's own data. Inside the method, you refer to the object's properties directly by name. No object prefix needed because the method already knows which object it belongs to. So you have val car, you have car. Accelerate call. Accelerate. Again, each method calls the calls. So each method call operates on my car own data. If you had another car object calling it accelerate would not affect my car's speed. The this keyword inside the class the keyword this refers to the current instance the specific object on which the method or property access is happening. Most of the time you don't need to write this explicitly because cotton resolves property references automatically within a class but it becomes necessary when a parameter has the same name as a property. So we have class user val name string fun set name name name equals to string and we have this name equals name without this both name reference would refer to the parameter name unamiguously. This name unamiguously refers to the object's property. This name equals name means set the property name to this object to the value of the parameter name. You'll see this used explicitly in constructors and setters like functions where parameters names match property property names. Else you can omit it multiple instances. One of the most important things to understand about classes is that each instance is completely independent. So we have val V val car one equals car. V car 2 equals car. Car one and car 2 are two different objects. They both come from the car class, the same blueprint, but they are independent. Changing cars one speed has absolutely no effect on car two. Each instance owns its own copy of every property. This is fundamental. The class defines the structure. Each instance has its own data that lives independently of all other instances. Class versus object. Let me make sure the distinction between class and object is clear because this terminology is used constantly. Class, the blueprint, the template, the definition. A class exists in your source code. You write it once. It has no data of its own. It defines what data it instances will have. Object an instance created from the class. A real concrete thing that exists in memory when your program runs. Each object has its own copy of every property defined in the class. So if you have class user v name age and create two objects val user one and user two. Now we have two objects created with the class blueprint. You can create as many objects as you need from your one class. Every time you call user parenthesis you get a new independent object. The relationship is one class potentially many objects. Naming conventions. Before we wrap up, let me cover the naming conventions because they matter in class name uses Pascal case. Every word starts with a capital letter. User car, bank account, shopping cart, user profile. Property and method names use camel case. First word lower case subsequented words capitalize name speed user accelerate print details active. These aren't enforced by the compiler. Your code will compile either way, but they are universally followed in the cotling community and in Android development. Following them makes your code readable to anyone who knows Cotlin. One more convention, class files usually share the same this the share the name of the class they contain. A file name user.kt contains the user class. Not required but strongly recommended. All right. Now we can wrap it up. You can see here what we covered. I'm going to end this video here and I'll see you in the next video. In the last video, we covered classes, what they are, how to declare properties, and how to add methods. But there is something missing. Every time you create an instance, we got an object with empty strings and zeros. And we had to set each property manually afterwards. That's occurred. Today, we fix that with primary constructor. By the end of this video, you'll be able to create fully initialized objects in a single line. So let's go. Let me show you exactly what the problem looks like. So without a constructor creating a user looks like this. Class user v name string v agent an empty string for the name and zero for the age. So we have three separate lines just to create one user. First you create it empty. Then you manually set each property. If you have five properties, you need five extra lines. And there is another problem. Nothing stops you from using an object before you've set its values. User.name is still an empty string if you forget to assign it. This is where constructors come in. A constructor lets you pass values directly at the moment of creation, guarantee guaranteeing the object is fully initialized from the very first line. So what is a primary constructor? A constructor is a special block of code that runs automatically when an object is created. It receives the initial values for the object's properties. In cotlin, the primary constructor is declared right after the class name in the class header. So you have class user parenthesis val age. That's the entire class. The parameters in parenthesis after the class name are the primary constructor. When you create a user now, you must provide both values. So have val user user parenthesis Alex and 25 name becomes Alex and age becomes 25. The object is created and immediately fully initialized. No extra lines, no partially built objects. The primary constructor is called primary because every class can have at most one of them and it has a privileged position directly in the class header. We'll cover secondary constructors in a future video. val and var in the constructor. When you declare parameters in the primary constructor with val or var, cotlin automatically creates them as properties of the class. This is one of cotlin's most concise feature features class user val name string var agent file name means create a read only property called name. Its value is set at construction and cannot change. V age means create a mutable property called age. It value set at construction and can be changed later. You have all user user Alex 25. Print line user name prints Alex print line user age prints 25. And you can change user age goes to 2 to 26. And and for the next one, user.name equals Bob. We can't because it's val. So it's immutable. We cannot change it value. We get a compile error. So one line in the class header declares the constructor and the properties simultaneously. This is the ideomatic quoting way of writing classes. Notice what you did. You notice what you did not have to write no property declaration in the body. No assignment statement. No this.name name equals name lines. Cotlin generates all of that for you from the constructor declaration constructor without val or var. What if you use the primary constructor but don't put val or var in front of the parameter? In that case, the parameter is just a constructor parameter. It's available during initialization but it does not become a property of the class. So if you have class user name string agent, notice that it didn't use val or var. And if you type val full name equals quotation m user door sign name you you can use the name here during initialization. So you create a val user user alex25 and without val or var name and age exist only during the construction process. You can use them to initialize other property but they are not accessible on the object afterwards. When should you use this form? When you want to transform or combine the constructor parameters into different properties rather than storing them directly, the rule is simple. If you want a constructor parameter to become a property, you can access you so you can access later via the dot notation. Add val or v. If you only need during construction, leave it out. Default values in constructor. Just like regular function parameters, constructor parameters can have default values. You have class user val name string val agent val agent zero val city string and you pass unknown. Now you can create user objects with just a name or with all three values or anything in between. So we have val user one Alex val user two Maria 28 val user three user s 32 London. This is a clean replacement for overloading construction. Instead of writing three separate constructors, one declaration handles all the cases. The rules for default values in constructors are the same as for functions. Required parameters should come before parameters with defaults. The initialization block. Sometimes you need to run some logic when an object is first created. Validation, login, derive, property, setup. That logic goes in the initializer block. The initializer block runs automatically when the object is created right after the primary constructor parameters are received. So you have class user val int. We have the init keyword curly braces and you have require name is not blank. Require means if it's like an if statement which checks to see if a condition is true or false. So we have require name that is not blank. Require a greater than zero. We have print user created name and age. The initializer block has full access to the constructor parameters and can use them to validate or set up the object. Require is a castlin standard library function that throws an legal argument exception if the condition is false. Is the ideomatic way of validate to to validate constructor inputs. Derive properties with init. The init block can also be used together with derive properties. properties whose values are computed from the constructor parameter. So if a class user first name, last name, age, you have a full name string val is adult boolean initializer block. So you need curly braces full name first name last name is adult adult age is greater than 18. The properties full name and is adult are declared in the class body without initial values. The init block says them based on the constructor parameters. This works because the initializer block runs before any other code in the class body. A complete example. Let me put everything together in one realistic class. We have class bank account. We have owner account number balance is active. We have initializer block require where owner is not blank. So we're going to it's going to print owner name required require account number that length is equals equals to 8. Account number must have eight digits. We have and you also have it active equals to balance greater than equal to zero. And have two functions deposit and get info. This class uses all the pieces we covered. Primary constructor with value and v default value for balance initializer block for validation and derive property and regular methods. This is ideomatic production quality code. All right, this is the cheat sheet. This is what we covered in this video. I'm going to end this video here and I'll see you in the next. Welcome back. Today we're going deeper into class initialization in Katlin. We touched on the initializer blocks briefly in the primary constructor video. Now we're going to cover it properly. What it is, why it exists, how interacts with properties and constructors, and what happens when you have multiple isnizer blocks. This is one of those topics that seems simple on the surface, but has some important details that will save you from subtle bugs. So, let's get into it. What is an initializer block? An initializer block written as in it followed by curly braces. It's a block of code that runs automatically every time an instance of a class is created. So we have user valame string val agent. Then you have init curly braces print line. The init block is not a function you call. You never write u.init that's not a thing. It runs on its own as part of the construction process. The moment you call user Alex 25, think of it as the on creation hook for your class. Any logic that must run at the moment of the object comes into existence comes into existence goes here. So you have you can have validation login derive property setup anything you have full access to the constructor parameters and to any properties declared in the class inside the init block. The entire class context is available to you. init versus property initialization. One of the most important things to understand about init blocks is how they fit into the order of execution when an object is created. Consider this class order val ID int and you have val label string and you have init print line you have init runs id and label. Now the execution order follows the top to bottom order of the class body. Cotlin interleaves property initialization init blocks in the order they appear. So in this case first label is initialized to order 42. Then the init block runs at and at that point label it's already set. Then status is initialized to pending. This top to bottom order is a guarantee in Katlin. You can rely on it. A property initialized before the init block will be accessible inside the init block. A property declared after the after it after the initializer block won't be fully set until after the initializer block completes. This matters in practice. If you try to read a property inside in it that is declared further down in the class body and hasn't been initialized yet, the compiler will warn you or in some cases you'll read the default null or zero value instead of what you expect. validation with init. The most common use of init blocks is input validation. You want to guarantee that the object is created with the sensible values and if it isn't, you want to stop the creation immediately with a clear error message. So we have class bank account, we have val owner, val balance, we have initializer block require. This require works like an if statement. So we have if owner is not blank. Owner name cannot be blank. Require balance greater than zero. Balance cannot be negative zero. Again this is those require are like if statements and we have two objects created and the require function from the cotlin standard library is the ideomatic tool here. It takes a condition and the lazy message lambda. If the condition is false, it throws an illegal argument exception. We're going to cover accessions later in the the course with your message. The beauty of doing validation here is that it's impossible to create a bank account in an invalid state. Every bank account that exists in your program is guaranteed to have a non blank owner of a non- negative balance. That's a powerful invariant. Your class enforces it on itself. Compare this to validation after construction. So we have val by bank account Alex and 500. What if someone calls this at some other point in the code? What with init validation? It's impossible computed properties in it. The init block is also the place to set up properties whose values can be expressed as simple as a simple one linear. They need some computation logic or multiple steps. So you have class product you have val string val price in sense int and you have display price is expensive and category and you have need val price in euros and you have price in cents divide by 100 and you have all those variables here. Then down we create an object. The properties display price is expensive and category are declared in the body without initial values. The init blocks compos them using intermediate values and logic that wouldn't fit cleanly on a single line. This is more expressive than cramping everything into one complex expression per property. You can use local variables, intermediate steps, and conditional logic. All the normal programming tools and then assign to the properties at the end multiple initializer blocks. Quatlin allows you to have more than one initializer block in a class. They all run in the order they appear top to bottom and together they form a single logical initialization sequence. So we have config and we have one in it and the second init all init blocks. So all initializer blocks and property declaration run in the exact order they appear in the class body. Here is production is set to the first initializer block. Then log level is initialized using is production. Then the second initializer block runs and can read log level. Having multiple initializer blocks can be useful when you want to separate concerns. Validation logic in one block login in another derive setup in a third. It keeps each init block focused on one responsibility rather than one giant block doing everything. Initializer blocks with secondary contractors. We haven't fully covered secondary constructors yet. That's a future topic. But it's worth mentioning how initializer blocks interact with them. Every constructor, primary or secondary, always runs on the initializer blocks. Always runs the initializer blocks. The init block are part of the class core construction process and cannot be bypassed. We have user and we have init. And we have a secondary constructor which has two parameters name and age. When the secondary constructor is called, it delegates to the primary construction first which is this parenthesis name which runs the initializer block. Then the secondary constructors body runs. The order is always primary initializer blocks secondary constructor body. This guarantees this guarantee means that you can put validation in in it and be confident it runs regardless of which constructors constructor was used to create the object. In it versus property default property defaults when to use which you've now seen two ways to initialize properties directly with a default value in the declaration and via an initializer block. So when to use each use property defaults when the initialization is simple direct and uh direct direct expression. So we have val label string order id active boolean true val count in equal to zero using it when you need more logic multiple steps conditions try catch or when you when one property depends on another property that itself was computed. We have unique val row. We have display price is expensing category and have if is in space expensive premium else standard. So use in need for validation require calls always belongs in the initializer block not in property declarations. Use initializer blocks for side effects at construction time printing login registering the object somewhere. The general principle if a single clean expression can initialize a property use it directly. If the initialization requires logic steps or depends on other properties being set first using it. Common patterns and pitfalls. Let me walk you through a few common patterns and pitfalls you'll encounter. Pattern one, validate and use. Always put your required calls at the top of the initializer block before you try to use the values. Pattern two, init reads properties declared above it. A property declared before the init block is fully initialized when the init runs. A property declared after it is not. Pitful in it referencing a property not yet initialized. If you declare a property after an init block and that init block tries to read it, the value won't be set yet. The compiler will warn you about this and in many cases in in many cases pay attention to those warnings. Okay, this is our discussion. This is our cheat sheet. I'm going to end this video here and I'll see you in the next. Welcome back. We covered the primary constructor, the one declared in the class header that initializes properties directly. Today, we're going one level deeper. Secondary constructors. This give you extra ways to create objects from the same class with different sets of arguments. They're not used as often as primary constructors in Catlin, but there are situations where they're actually the right tool. So let's go through everything. So first what is a secondary constructor? A secondary constructor is an additional constructor defined inside the class body using the constructor keyword. While a class can have only one primary constructor. It can have multiple secondary constructors. The simplest case looks like this. Class user and have v name colon string equals empty. And then you have the secondary constructor. We have the keyword constructor name colon string and here it should be in this slide showing this name equals name. So you you assign the name argument that is defined here to the property define above. This why I say that you type this name equals this name. Now you can create user objects with this constructor. Let's now look at primary versus secondary constructors. Before I go further, it's important to understand the relationship between primary and secondary constructors because in most Catalin code, the primary constructors handles everything and secondary constructors are rarely needed. The primary constructor is the preferred way to create obser. It's concise declared in the header and automatically creates properties when you use val or var. Secondary constructors are more verbose. You have to manually assign values in the body. They don't automatically create properties. you have to declare those separately. So when you actually use a secondary constructor when you need to access fundamentally different types of input that can easily be expressed with default values alone. For example, constructing a point from either two coordinates or from a single string like three four that you need to parse when you're extending a Java class or framework that requires specific constructor signatures. when when uh you're extending a Java class or a framework that requires specific constructor signatures when each constructor path requires meaningfully different setup logic that can't share a single body in poor cotling code. Default parameters values handle the majority of cases that other languages need multiple constructors for. But secondary contractors fill the gap when defaults aren't enough. secondary constructor with a primary constructor. When a class when a class has both a primary constructor and a secondary constructor, the secondary constructor must delegate to the primary constructor using this parenthesis keyword. It cannot bypass the primary constructor. So you have class username val v agent v email string. We have constructor name string age int and have colon and we have the keyword this and we type n or name. So notice the colon after the parameter list followed by this parentheses name. This is the delegation. It means before running my body first run the primary constructor with this argument. The delegation chain works like this for the for the three parameter constructor. It calls this name age which calls the two parameter secondary which calls this name which calls the primary. Every init block runs during this chain. The primary constructor always runs always runs first. Init block init blocks always run before the secondary constructor body and the second secondary constructor body runs last. The delegation chain. The delegation chain is one of the most important concepts to understand about secondary constructor. So let me lay it out clearly. When you call user parenthesis alex28 alixacample.com we have step one the three parameter secondary constructor begins. Step two it delegates to this name age the two parameter secondary. Step three that delegates to this name the primary constructor. Step four the primary constructor runs name property is set. Step five the any init blocks run. Step six the two parameter secondary body runs. Age is set. Step seven the three parameter secondary body runs. Email email is set. The execution is always execution always bubbles up the primary constructor first then flows back down through each secondary constructor's body. This is not optional. This is the this is enforced by the language. Why does this matter? Because it guarantees that the primary constructor's logic including any init block validation always runs regardless of which constructor the caller used. Now no secondary constructor can create partially initialized or invalid object by bypassing the primary constructor. So this is the execution order. We have primary constructor runs the initializer block runs secondary constructor runs the object is return to is returned to the caller. So this is the the steps in which this code is executed. This is the order in which the code is executed. Now let's look at secondary constructors without a primary. What if a class has no primary constructor at all? In that case, secondary constructors stand on their own and don't need to delegate. So we have class connection val. We have constructor host string port end. We have this dot post equals post. This port equals port. Here the class has no primary construction in the header. Both secondary constructors are standalone. Each one initializes host and part in a different way and port in a different way. No part. One takes them as separate parameters. The other parsees them from a single urls style string. When there is no primary constructor, secondary constructors don't use the column delegation syntax. Each one independently initializes the class. Initializer blocks and secondary constructors. An important rule, initializer blocks always run before any secondary constructor body regardless of which secondary constructor is called. You have class user one name string and have initializer block and constructor secondary constructor down the initializer block run first then the secondary constructor body. This ordering is guaranteed and cannot be changed. It means you can safely put all your validation logic in in it and know that it runs on every possible construction part. The order is always primary constructor initializer blocks secondary constructor body. When to use secondary constructors? Let me give you a practical guide on when secondary constructors are the right choice. Use a secondary constructor when you need to accept genuinely different types of input from for the same class. The connection example from earlier is a good example. You might receive either a host and the port separately or a combined uls string. These are different shapes of input that lead to the same kind of object. Use a secondary constructor when you're subclassing a Java class that has multiple constructors with different signatures. Cotly needs to provide matching constructors to satisfy the Java superclass requirement. Don't use a secondary constructor just to provide optional parameters. In Cotlin, default values do this better. The default value approach is cleaner, shorter, and just as flexible. Reserve secondary constructors for cases where default values can't express what you need. A practical example. Let me show you a realistic use of where a secondary constructor generally earns its place. Class point and have val x double val y double. We have a need have print line point created and we have a constructor a x and y and it delegates to this x to double and y to double and have a secondary constructor which has some logic. All three create the same kind of point. The primary constructor is a canonical form that takes two doubles. The secondary constructors are convenience entries for common other input shapes, integer coordinates or a string representation. Every creation path goes through the primary construction and the init block. This is a clean practical use of secondary constructors. Each one provides a generally different input format and each one delegates properly to the primary. So this is our cheat sheet. I'm going to end this video here and I'll see you in the next video. I'm back. Today we're covering default values for constructor parameters. This is a short but very practical topic. You've already seen default values in regular functions. Constructors work exactly the same way and the result is a cleaner object creation with a lot of less code. So let's go. The problem when you create a class, you often want some parameters to be required and others to be optional. Without default values, the only way to handle this is a secondary constructors. One for each combination of parameters you want to support. This gets repetitive fast. As you can see here, three constructors, three bodies, and if the logic changes, you update in multiple places. There is a much better way. Default values in the constructor. Default values let you define fallback values for parameters directly in the constructor. If the caller provides an argument, the value is used. If they don't, the default kicks in and have class username string vality string and have unknown. That's the entire class. One constructor, three parameters. Name is required. Age and city are optional with defaults. Creating instances is now completely flexible. As you can see here, we have user one, user two. All three are valid. Or in our case in this diagram, all two. So all three, all two in our case, same object, same constructor, same class, but the caller decides how much detail to provide. Require parameters first. There is one rule you must follow. Required parameters those without defaults must come before the parameters with defaults. Correct. Require first class user val name string val age in equal to z. V city unknown the wrong way. So type class user val age int zero val string empty. This will generate a compile error. Why? Because when you call user Alex, it pass the value for the parameter Al. It pass the parameter Alex. Cotlin fills arguments left to right. If the first parameter has a default and second doesn't, Cotling has no way to know whether Alex is meant to fill the default first parameter or the require second one. Require first keeps everything unamiguous. Let's wrap it up. Syntax parameter type default value direct in the constructor declaration. require first parameter with tow defaults come be come before those with defaults caller's choice provide only the required arguments or include any of the optional ones replace secondary constructor for common cases of optional parameters with sensible defaults this approach is shorter and cleaner val or var default values work with both val and var constructor parameters all right I'm going to end this video here and I'll see you in the next video welcome back today we're covering the latent need keyword in cotlin. This is a practical feature that you'll encounter regularly especially in Android development. It solves a specific problem. What do you do when you have a non-null property that you generally cannot initialize at construction time? So let's get through it properly. The problem lately solves so system is is strict about null safety. When you declare a var property, you must either give it an initial value and initial value or make it inable with a question mark. But sometimes neither option feels right. Consider this situation. Andre, you have my activity. I have two buttons. One is non nullable and one is nullable. When an activity is created, the layout hasn't been inflated yet. The button view doesn't exist at construction time. You can't pass it to the constructor and you'll set it up in on create. But the compiler doesn't know that. making it nullable with button question mark means every single time you use the button you have to write that uh question uh exclamation point exclamation point or button uh question mark text even though you know that it will always be said before any code touches it both verbose and misleading it implies the value might be absent when it really is just deferred latit is the solution it tells the compiler trust me this will be said before it's So what is latit it's a modifier you place on a v property. It tells cotlin to skip the requirement for an initial value with the promise that you'll assign it before first use. So you type class user profile latit var name latit v email and in file setup you type this name equals name data email equals email. The properties name and email are declared with the latit. No initial value is given. No null. The compiler access is because you've explicitly taken responsibility for initializing them before use. When setup is called, the real values are assigned. From that point on, name and email behave exactly like any other non null string property. You access them without any null checks, without the two double bank, without the question mark that just clean the direct access rules and restrictions. Leonit has specific rules. It's not a magic escape hatch. It's a precise tool with a clear scope. First, latit only works with var, not val. A val is is assigned once and never changes. If you can't initialize it at declaration, use latit var val is already final. Latit will be contradictory. Second, laten it only works with non-primitive types. You cannot use laten it on int, double, boolean, long, float, or any other primitive. Those types have default values zero, false, and don't need latit. It's only needed for reference types, strings, custom classes, and views and so on. Third latitate properties cannot have a custom getter or setter. The latenate mechanism manages the backing field internally. Custom accessors would interfere with that mechanism. Fourth latit is not thread safe. If multiple threads might initialize or access a latent property concurrently, you need additional synchronization. What happens if you access before initialization? This is an important safety detail. If you access a lat property before it's been assigned, cotlin throws an initialized property access exception or not a null pointer exception. So it's going to throw an initialized property access exception, not a null pointer exception. So I have class my class lat name and down val object my class print object.name. This going to throw an uninitialized property access exception. The error message is specific and helpful. It tells you exactly which property was accessed too early. Compare this to a n pointer exception which often gives you a line number and nothing else. This is one of the reason latity is safer than using nullable types with double bang. A nullable with double bang throws a plain null pointer extension with no information about what property caused the problem. So while laten it is not as safe as an immediately initialized property is safer and more informative than a nullable with double bang alternative. Checking initialization with is initialized. Cotly provides a way to check whether whether a latent property has been initialized before accessing it. The is initialized property when the property reference. So you type my class later name val object my class and then you type an if statement colon colon name that is initialized the double colon before name creates a property reference a reflection object that represents the property itself on that reference is initialized returns true if the property has been assigned and returns false if it hasn't use is initialize when you generally need to check for example in a tier down function that might run before full setup or in an optional initialization flows. Don't use it as a routine guard around every access which would defeat the purpose. If you're checking is initialize everywhere, you probably want a nullable type instead. Latit versus nable when to use which. This is the practical decision you'll face. Here is a clear guide. Use laten when the property will definitely be set before use just not at construction time. When you don't want nullable syntax cluttering every access point when the class has a known setup phase on creating it methods dependency injection after which the property is always set. Use it when the value is conceptually non null. It's never supposed to be absent just deferred. Use it as a nullable type when the value might generally be absent at some point during the object's lifetime. The property can be set to null intentionally for clean up, optional features, or recitating state. You're unsure whether it will be set before you use and want the compiler to force you to handle both cases. Android dependency injection with libraries like hilt or coin uses latit extensively. You annotate a field with the inject latitate v repository. So the user repository and the injection framework says it before your code runs. Tests also use latit frequently. You declare it. You declare your system under test as latit and initialize it in a be at before setup function. All right, this our discussion about latit. I'm going to end this video here and I'll see you in the next video. Back. Today we're covering companion objects in Cotlin. If you're coming from Java, you're used to static methods and static fields, things that belong to the class itself rather to any instance. Cotlin doesn't have a static keyword. Instead, it uses companion objects which are more flexible and more consistent with Cotlin's object orientated model. So let's go through everything. Kotlin has no static. In Java, you mark a method or a field as starting and it belongs to the class, not to instances. Cot has no static keyword. If you try to call the function directly on a class, name it without creating an instance, you'll get a compile error. But you often need this kind of functionality. Factory methods that create instances, constants that belong to a class conceptually, utility functions that don't need an object that don't need any object state. These are classic uses of use cases for class level members. The solution in cotton is the companion object. So what is a companion object? A comp object is a special object declared inside the class using the companion object keywords. It's a singleton one instance shared across all uses and it's tied up to the class itself not to any instance of it. So we have class circle have val radius double component object cons val pi and have the number pi. You access the component objects members using the class name just like static members in Java. So type circle.py and circle.create five and you pass five and zero. No instance of circle is needed. The companion object objects members are access to the class name directly. The companion object is initialized lazily when the class is first loaded and there is exactly one of it for the lifetime of the program. The factory methods the most common uses of component objects is factory methods functions that create and return instances of class. This is the quoting replacement for multiple constructors or name constructors in other languages. We have class user private constructor and I have a component object with the function admin and guest. Notice that the constructor is private. The only way to create a user is through the component objects factory methods. This is a very clean pattern. The factory methods have meaningful names like admin and guest rather than forcing the C caller to know which arguments make an admin or a guest. This is the ideomatic cotling replacement for Java static factory metal pattern constants. Companies are also the right place for class level constants values that belong conceptually to the class but don't need an instance. We have a HTTP client class and a comp audio constant val time out. The con val modifier makes the compile time constants. The compile the compiler inlines their values directly as at use size. They behave in identically to Java's public static final fields. Without a companion object, you would have to create an HTTP client instance just to access timeout or base URL which makes no sense. These values belong to the class concept, not to any particular client instance. Naming a companion object. By default, a component object is accessed using the class name and has no name of its own. But you can give it a name. You can type class server component object config. Naming a compiler object is useful when you want to pass it around as an argument. Implement an interface on it or make it the code more self-documenting. The name config makes it more immediately clear that this component holds configuration values. In most cases, you won't need to name your companion order, but the option is there when it's helpful. Companion object versus top level functions. Another natural question, should we use component object or just put the function at the top level of the file? Top level functions in causal are functions declared outside any class directly in a file. Use a component object when the function or constant is strongly associated with a specific class, especially factory methods, class level constants, and anything that logically belongs inside the class name space. Use top level functions when the function is a utility that doesn't belong to any particular class or when it's used across many different classes. Cosin encourages top level functions for general purpose utilities. It's not considered bad practice like it would be in Java. In short, if it belongs to the class conceptually, use a comp object. If it's a free utility, use a top level function. All right, this our discussion about component object. I'm going to end this video here and I'll see you in the next video. Today we're covering the singleton pattern in Cotlin. A singleton is a class that can only have one instance, one object in memory for the entire lifetime of the program. We're going to look at this in two ways. First, how you'd build a singleton manually using a class with a private constructor so you understand exactly what's happening under the hood. then how Cotlin makes the whole thing trivally simple with a single keyword object. So let's get into it. What is a singleton? A singleton is an object that exists exactly once. There is never more than one instance of it and every part of the program that access it gets the same object. When is this useful? Anytime you have something that should be shared globally and have a single point of control. Examples, a database connection. You don't want 10 different database connection objects competing with each other. An application configuration. The config values should be the same everywhere. A logger. All parts of the program should log through the same channel. A counter or registry. You need one central store, not multiple independent copies. The core idea is one instance globally accessible created only once. Now let's see how you build that manually because understanding the manual version makes the cotton keyword much more meaningful. So manual singleton so manual singleton the class approach. So here is a classic singleton pattern the the way you write it in cot using regular class. So we have a class app config private constructor var team var language. We have a component object. We have a private var instance which is going to be analable and function inside the component object which is going to be called get instance. It's going to return the app config instance. If the instance is null you're going to create the instance. If the instance is not null you're going to return the existing instance. So let's walk through the what each part does. So what each part does? The constructor is marked private. This means that no external code can call app config to create instances. The only way to get app config is through the companion object. Inside the companion object, there is a private variable called instance initialized to null. This will hold the one and only instance once it's been created. The get instance function checks whether the instance is null. If it is, this is the first time it's been called. So it creates the the one and only app config and stores it. If the instance already exists, it returns the existing one. The result, no matter how many times you call appconig.get instance from anywhere in your program, you always get the same exact object. So config one and config 2 are the same object. Changing config one team changes config to config 2 team because they're not two objects. There the there are two references to the one object to the same object. Problems with manual approach. The manual approach works but it has problem. First it's verbose. You you wrote a private constructor analable backing variable a companion object and the get instance function all all just to get one instance. That's a lot of boiler plate for a simple concept. Second and more importantly it's not stress safe. Look at this scenario. Thread A creates instance, checks for if instance equals null, then it's going to create the instance. Thread B also checks for instance if it's null and it's going to create the instance. Now both threads going to check the instance at the same time. Both signal both create app config. Now there are two instances. The singleton pattern is broken. You can fix this with synchronization wrapping the check in a synchronized block. But now you're added more complexity and synchronization has performance cost. Third, the nullable type for instance requires a double bang operator when returning it which is ugly and slightly fragile. Cotlin has a better way. One keyword no boiler plate thread save by design. The object keyword in cotlin you can create a singleton with a single keyword object. So you type object app config team and language and that's it. This is complete fully functional singleton. No private constructor, no companion object, no get instance function, no nullable backing field. The object keyword tells cotlin create exactly one instance of this, initialize it lazily the first time it's accessed access and make it available under this name. You can access it directly using the object name just like a class name. So app config app config is not a class you instantiate. It is the instance. There is no app config call. There is no getting an instance of anything. App config is the object. What cotling generates under the hood. When you write an object declaration, cotling generates exactly what you would write manually but correctly and safely. Here is what the compile code is equivalent to. The construction is private. You can't create additional instances. There is a static final instance field that holds this the one object. Initialization happens when the object is first accessed. Lazy initialization. The initialization is thread safe. The JVM guarantees that class initialization including object declaration is thread safe by default. Cotlin's object gets this for free. So everything the manual approach did and fix the thread safe problem all in one word. So everything the manual approach did and fix the thread safety problem all in just one word. The object declaration is essentially cotling saying I know the singleton pattern I'll handle it correctly not just describe what describe what's in it. Object limitation and best practices. A few things worth knowing about object declaration. You cannot pass a constructor arguments to an object since there is only one instance and it's creating automatically. There is no constructure call where you could pass values. If you need configuration, use properties, defaults or separate init pattern. Object can extend classes and implement interfaces. So we can have an interface logger and uh we can have an object console logger and you can uh inherit from the logger interface. This is a common and powerful pattern a singleton that implements a shared interface. You can pass console logger wherever a logger is expected. Objects are lazy. The instance is created the first time you access the object not when the program starts. This is the same behavior as the manual get instance approach. For thread safety causing object decoration are thread save by default. The JVM initializes them exactly once and when multiple threads access them simultaneously. This is one of the key advantages advantages over the manual pattern. And this is our cheat sheet. I'm going to end this video here and I'll see you in the next video. Welcome back. Today we're covering lazy initialization in Cotlin. This is a short but generally useful topic. The idea is simple. Don't create something until you actually need it. Cotlin makes this easy with the lazy delegate. So let's get through it. What is lazy initialization? Lazy initialization means deferring the creation or computation for of a value until the first time it's accessed. Instead of setting something up when the object is created, you set it up on demand the first time someone actually reads it. Why? Why would you want this? Two main reasons. First, performance. If a property is expensive to compute, it needs it reads from disk, makes a network call, runs a heavy calculation, and it might never even be needed in a given run for your program. computing it eagerly at startup wastes time and resources. With lazy initialization, you only pay the cost when you actually need the result. Second, availability. Sometimes the data or dependencies needed to compute a value aren't ready at construction time, but will definitely be ready by the time the value is first read. So, lazy let you express this naturally. The lazy delegate cotlin implements lazy initialization through property delegate using the lazy function. Here is the syntax. You have class user profile. We have val full support string by lazy and we have print line computing report. The by lazy curly braces block is the delegate. The code inside the block. The lambda runs only once. The first time full report is success. After that the result is stored and every subsequent axis just returns the cage the cached value immediately. The property is computed once stored and reused. That's the whole mechanism. Lazy vers is val only. An important restriction lazy only works with val not var. This makes sense when you think about it. The lazy computes a value once it caches it. Var implies a value can be changed which would conflict with the compute once contract. If you need a mable lazy initialized property you would use later need var instead which we covered in a previous video. Thread safety by default lazy is thread safe. The first call to the property even if multiple threads reaches simultaneously will only execute the lambda once. Other threads will wait until the first evaluation completes then receive the cache result. This is lazy thread safe mode, synchronize mode which is the default. If you know your code is a single thread, we can opt into a faster mode. V data list of string by lazy and type lazy thy mode none and load from disk. The lazy thread safety mode none skips synchronization entirely. makes it slightly faster. Only use this if you're certain that the property will only ever be accessed from one thread. For most code, the default synchronized mode is the right choice, safe and performant enough. All right, I'm going to end this video here and I'll see you in the next video. Back. Today we're covering getters and setters in Kotlin. If you're coming from Java, this topic will surprise you. Cotlin handles property access very differently and in a much more elegant way. If this is your first time hearing the term, don't worry. We'll build everything from scratch. By the end of this video, you'll understand what getters and setters are, why they exist, and how cot makes them implicit, and when and how to write custom ones. So, let's get into it. So, what are getters and setters? Let's start with the concept. A getter is code that runs when you read a property. A setter is code that a setter is code that runs when you write to a property. In many languages, Java being the classical example, properties are private fields and you interact with them through explicit getters and setter methods. So you call get name to read, set name to write. The field itself is hidden. This pattern exists for a good reason. It lets you control what happens when a value is read or written. You can validate on the way in, compute on the way out, fire events, log anything. The problem is the boiler plate. Java code is full of get name, set name, get age, set methods that do nothing except pass the value through. Cotlin solve this elegantly properties in cotling have getters and setters built in implicitly and you only need to write them when you want custom behavior. Implicit getters and setters in Katlin. When you declare a property in Kathleen, a getter and a setter are generated automatically. You never see them but they are there. So you have val you we have class user v name string val agent and we have Alex and 25 and behind the scenes cotling generates a getter for name that returns the stored value a setter for name that stores the new value a getter for age that returns the store value and no setter for age because it's val it's a it's read only when you write to user.name name you're calling the getter. When you write user name, you're equals Maria, you're calling the setter. The dot notation is not direct. It's not direct memory access. It goes through the property accessor. This is the key inside in Kotlin. Property access always goes to the getters and setters. Even if you haven't written any custom logic, the accessor layer is there. It just is just invisible because codling generates it for you. It's implicit. This means that you if you later decide that you don't need to add a custom logic when a property is read or written, you can do so without changing any call size. The syntax stay the same user.name regardless of whether whether the getter is trivial or complex. The backing back field and the field identifier. This is the most important concept in this whole video. So pay close attention. When you write to a custom getter or setter, you need a way to refer to the actual stored value, the underlying variable that holds the data. That's called the backing field. Here is the trap that catches everyone the first time. Suppose you try to write to a custom getter like this. V name equals Alex and I get parenthesis equals name. You want to return the value of name. So you write get equals name. But name inside the getter refers to the property itself. The getter calls itself. It's infinite recursion and it will crash you at run time with the with the stock overflow error. This is exactly why cine provides the field identifier. Field refers to the backing field, the actual store value, not the property. As you can see on the right hand side, field is a special keyword that is only valid inside the get or setter body. Outside of them, it doesn't exist inside them. It's only it always refers to the row store value for that specific property. So let's see it in a setter two. So you have without the field you would write name equals value which would call the setter again which will cause infinite recursion. With field you write directly to the store value. The backing field exists automatically wherever a property uses the default getter or setter or whenever you your custom getter and setter references the field. If your custom getter doesn't reference the field at all, for example, if it computes something from other properties, then there is no backing field. Implicit getters and setters. Now let's see custom getters and setters in practice. A custom getter lets you compute a value on the fly when the property is needed. Okay, so this uh this is the field identifier for the getter. we have return field and for the setter we have set value field equals to value. Now let's see the custom getter. Now let's see custom getters in practice. A custom getter let you compute a value on the fly when the property is read. You have class circle val area. You have getter math pi radius and time radius. You have pi r pi r squ which is the area of a circle. Every time you read see that area, the getter runs and computes the result fresh. There is no backing field here. Area doesn't store anything. It's computed from radius each time. This is a great use case for a custom getter, a derived value that depends on other properties. You could store it as a separate field, but when you have to keep it in sync with radius with a getter, it's always correct because it's always recompound recomputed. Another common use case formatting or transforming the value in red. So we have class user private val first name private val last name. I have val full name string get equals to first name and last name. Full name is never stored. Every red combines first name and last name on the spot. If you change either one f full name is automatically up to date. Custom setter. A custom setter lets you run logic when a value it's written. The most common use case is validation. We have a class user and have in in the slide we have age and name and those are part of classes but it didn't fit on the slide so I didn't put class there. But we have set and we have a first I have the variable a of type in the zero. Then we have set value. We have required greater than zero. So it should be greater than zero. And have field equals to value. And so is going to store if valid. When you write user age equals to 25, the setter runs with the value equal to 25. First require check is 25 greater than zero. Yes. Then field equals value which stores 25 in the backing field. When you write user age equals minus5, the setter runs with value minus 5. The required check fails and an exception is thrown. The field is never written. The backing field is essential here. The setter parameter is called value by convention. That's the new value the caller is trying to assign. You validate it then decide whether to store it with the field equals to value. You can also transform the value before storing it. So I can type v name string equals empty set value field equals value trim. Every time now name is said the value is going to be trimmed of y spaces before storing the caller writes user.name equals have space and what's stored in the backing field is Alex. From the caller perspective it looks like a normal assignment even though it had spaces because you called that trim. It's going to remove the spaces. Custom getter and setter. You can combine custom getter and setters on the same property here. So we have get which is going to return the field in lower case. Always going to read lower case and set to checks for at in the store field and uh it's trimming the values. So we have value trim which is going to trim the spaces in that uh in that value that variable. So next we have visibility on setters in cotlin you can change the visibility of a getter or setter independently from the property itself. The most common pattern is a public property with a private setter. So you have class counter v count in have private set count is publicly readable. Anyone can call c. count but the setter is private. Only code inside the counter can change the value. This is a clean way to expose a read only view of a property that is still that can still be modified internally. This is ideatic con replacement for the Java pattern of private field with a public getter method. Instead of get count and a private field, you write a public var with private set. The visibility modifier goes before the keyword. You can use private, protected or internal, whatever access level you need. Now let's look at val versus v and getters and setter. Let's be clear about val and v mean in terms of getters and setters. V declare the read only property. It has a getter but no setter. Once set the value cannot be changed through assignment. You can still define a custom getter for a val property. That's how computed properties work. Var declares a m mutable property. It has both a getter and a setter. You can read and write it. You can customize either or both. There is also a softly word a sub I can't pronounce that word. So there is also sly worth knowing. If you define a custom getter for a val that doesn't use field cotlin doesn't create a backing field at all. The property is poorly computed. It exists conceptually but has no memory slot. On the other hand, if your getter or setter references field references field anywhere, a bucking field is created. This is called the bucking field existing by demand. So this our discussion I'm going to end this video here and see you in the next video. Today we're covering enam classes and cotlin. A nam is a type that has a fixed name set of possible values. Instead of using row strings or magic numbers to represent states, you use name constants that compiler can check. It makes your code safer, more readable, and easier to maintain. So let's go through everything. What is an enam class? An inclam class defines a closed set of name constants. Think of it as type as type whose only possible values you list explicitly. Without elams, you might represent a day of the week as a string or an int. Both of these are, as you can see in the diagram, fragile. Monday compiles perfectly and crashes or misbehaves at runtime. The number one has no meaning without documentation with an enam class type class Monday, Tuesday and so on. While today day have equal day Monday. Now today can only ever be one of those seven values. the comparison forces this you can you can't accidentally pass day Monday it uh it doesn't exist so day Monday with two I doesn't exist you can't you can't pass an unrelated string or number the type system protects you in properties and methods in classes and col are not just list of constants they are real classes each enum constant is an instance of the enum class and they can have properties and methods of Inium class planet they have mass radius mercury venus earth and surface gravity the function each planet constant is constructed with its mass and radius the surface gravity method is available on all of them planet earth mass gives earth's mass planet mercury surface gravity gives mercury surface gravity notice the semicolon after the last constant earth before the method declaration when a neon class has a body with n members the list of constants must end with a semicolon. This is the only place in cotton where a semicolon is required. When with enam classes, enams and when expressions are perfect pair. When you use when with an enam compiler can check that you've handled every possible value and warn you if you haven't have enam class direction north south east west and have describe because have when dear for direction because when it's used as an expression it returns a value cy requires it to be exhaustive if you have if you leave out direction that west it's a compile error you cannot forget a case this one of the most valuable properties of enams. When you add a new value to the enum, every when expression that uses it will produce a compile error, pointing you to to every place in the code that needs updating. It's impossible to forget. Compare this to a when a string adding a new string value is silent. Your switch statement just falls false with no warning. Build-in properties and functions. Every in class in cotling comes with useful built-in members for free. Name the name of the constant as a string. ordinal the zero base position of the constant in the declaration value up look up so value of look up a constant by its name string be careful with value of it throws an illegal argument expression if the string doesn't match any constants exactly use it when you're confident that the string is valid no or wrap it in a try catch entries the complete list of all constants in declaration order entries is The modern replacement for the older values function. It returns an enum entries collection rather than an array. Both orbit entries is the ideomatic choice in modern coding. Abstract method in enam constants can override abstract methods defined in the enam class. This lets each constance to have its own behavior. As you can see here, each constance provides its own implementation of apply. This is powerful pattern. Instead of of a way expression that dispatches the two different functions, the dispatch is built into the enam. The operation carries its own logic. This is less common than basing enams with property, but is a clean solution when each constant has meaningfully different behavior. Enam in class versus seal class. A question that comes up often, should I use an enum or a seal class? Use an enum when all your values are simple fixed constants. Use an enum when each value has the same structure, the same properties and methods. When you need ordinal ordering, entries list or value of lookups. When you want exhaustive when expressions. Now use a seal class when different values need different shades, different numbers of types of data. Use seal class when some variants might carry associated data data others might not. Use seal class when you need subtypes that are full that are full classes with their own state. A quick example of the difference in an enam is great for a status with fixed states pending active close. A seal class is better for a result that can be either success carrying data or error carrying an exception because the two these two cases have different structures for simple uniform fixed values in for complex type hierarchy seal class seal classes are a topic for a future video. All right, I'm going to end this video here and I'll see you in the next video. Welcome back. Today we're covering seal classes in cotlin. If you've worked with enam classes, seal class will feel familiar, but they're significantly more powerful. If you've worked with Android development as well, you've probably already seen seal classes used for UI states, network results, and navigation events. Today, we'll understand exactly what they are, why they exist, and when to use them. So, let's go. The problem seal classes solve. Let's start with the problem. Imagine you're modeling a result of a network request. It can be one of three things. a success with data, an error with a message or a loading state while the request is still in progress. One approach is to use an enam class. Inam class have result success error loading that works for simple flags. But what if the success what if success needs to carry the actual data? What if error needs to carry the exceptions or the error message? In constants all have the same structure. They can have share property but they can't have different shapes. Another approach is a plain class hierarchy class result success error and loading and success error unloading all extend from result. This works but it's open anyone anywhere in the codebase could be a for subclass and when you write an expression to handle all cases the compiler can verify that you've covered that you've covered everything. You have to add an else can't verify sorry the compiler can't verify that you've covered everything. You have to add an else branch just satisfy the compiler. Seal classes solve both problems at once. So what is a seal class? A silk class is a class where all directs of classes are defined in the same file and are known to the compiler at compile time. The silk keyword restricts the class hierarchy. No code outside this file can create a new sub class of result. The compiler knows exactly and permanently what the com what the complete set of type is is. Three things to notice. First, the subasses are defined inside the se class. That's the most common pattern through though in modern culture you can also define them in some file but outside of the class body. Second, different subasses can have completely different shape. Success carries a string called data. Error carries a string called message. Loading carries nothing at all. It's an object a singleton. Third, you access the properties directly. Result data when a success results on an error. Nonability. No casting. Clean. Clean and type safe. Exhaustive when with seal classes have existed when seal classes. This is the most powerful feature of seal classes. When you use a seal class in when expression, the compiler checks that every subass is handled. If you miss one, it's a compile error. No else branch needed. No default case. The compiler knows the result can only be success error or loading. So it can verify that all three are handled. Now add a new subclass data class empty. Every when expression that doesn't handle empty now fails to compile not sealantly at runtime immediately at compile time. The compiler tells you exactly where to update. You cannot ship code that ignores a valid state. This is the fundamental guarantee. exhaustive handling and forced at compile time. S class versus enam class. S classes and enam classes are related ideas but they serve different purposes. Use an enam when all your values have the same structure and when you need ordinal ordering value of or entries list. Enums are constants. Each one is a singleton. A direction a direction in has north, south, east, west. They all have the same shape. Use a seal class when different variants need to carry different data when they when the cases have different shapes. So you class direct north south west. We have se class network state and have success here and loading wall extended from network state. Success carries a list of users. Error carries a code and a message. Loading carries nothing. The three have completely different structure. That's exactly what sealed classes are for. The rule if other cases are uniform same shape just different values use an enum. If your cases have different structure, different property, different types, use a seal class. Seal class is an Android UI state. The most common pattern you'll see in Android development is using seal classes to model UI state. This is the standard pattern in model Android with view model and cotlin. Every possible state is handled. If a new state is added say an empty state, the comparative mean ref flags every when expression nothing is missed. The UI always handles exactly what the view model can produce. This pattern seal class for sale when for handling is now considered ideicating model Android development. Seal class versus abstract class. Another comparison worth making seal classes versus abstract classes. An after class defines a template that subasses implement, but it's open. Any file, any module can create a new subclass. You have no control over who extends it. A seal class is closed. Only subasses you define in the same file can exist. The hierarchy is permanently fixed. We have abstract class shape, circle, and rectangle. For a sealed shape, a when expression on shape doesn't need an else. For a an abstract shape, it always does because someone might have added a new subclass the compiler doesn't know about. Use abstract when you generally want the hierarchy to be extensible when other code should be able to add new subasses. Use seal class when when the set of types is fixed and you want to you want exhaustive handling. All right, I'm going to end this video here and I'll see you in the next video. Back. Today we're covering object expressions in cotlin. If you've watched the singleton video, we already know about object declarations. Object expressions are different and easy to confuse with them. An object expression creates anonymous object right there in line without giving the without giving the name. So let's go through exactly what that means and when to use it. What is an object expression? An object expression creates an anonymous class and instantiate it all in one single expression. You don't give the class a name. It exist just for that one use. The syntax uses the object keyword the same word as object declarations but here it appears as an expression not a declaration. That object as you see block defines an anonymous class with a message property and a greet function. V greeter holds the single instance. There is no class name anywhere. you never needed one. This is the core idea. Object expressions let you define behavior and create an object at the same time without the ceremony of declaring a name class. Implementing an interface. The most common use of object expression is implementing an interface in line. Instead of declaring a whole new name class just to implement an interface once, you do it right where you need it. Notice what we have here. The colon after the object click listener. It tells you the cotlin that the anonymous class implements that interface. It overrides inside the override inside provides the implementation. This is exactly the same as Java's anonymous inner class which you've probably seen in Android code. The difference is the syntax is clear and integrates naturally into cotlin. You can also pass the object expression directly as an argument without assigning it to a variable first. Extending a class object expression can also extend the class or extend the class and implement interface at the same time. As you can see here have base ur sorry base handler. The anonymous class extends base handler and implements logable both at once. The comma separated list after the colon works just like it does for name classes. The object is created immediately and held and held in handler capture capturing variables from the surrounding scope. One important feature of object expressions they can access and capture variables from the surrounding scope including multiple variables. This is different from Java anonymous classes which could only access final variables. The anonymous object accesses count a local variable from the so the anonymous object accesses count a local variable from the enclosing function and modifies it. This works in cing because all the expressions close over the surrounding scope immediately. They capture multiple variables not just final ones. This makes object expression more flexible than Java anonymous classes for closure and callback patterns. Object expressions versus object declaration versus lambda. Three things that look related but serve different purposes. Let me make the distinction clear. Object declaration the singleton pattern object app config val team dark one instance globally accessible created once named use for singletons. Object expression anonymous object created in line val x equals object colon same interface of fun do it created. It's created once where it's written. No name used when you need oneoff implementation. Lambda for a single method interfaces when an interface has exactly one abstract method and is marked as functional. Cin lets you pass a lambda instead of a full object expression. This is almost always cleaner. The practical rule if the interface has one method and it's a sam interface use lambda. If it's a if it has multiple methods or you need to implement multiple interfaces or you need to extend the class, use an object expression. All right, I'm going to end this video here and I'll see you in the next video. I'm back. Today we're covering inheritance in cotrin. Inheritance is one of the core pillars of object-oriented programming and it's something you use constantly once you start building anything beyond a simple script. It's also it's also one of those topics where Kling made a deliberate design choice that differs from Java and once you understand why you'll appreciate it. So let's go. What is inheritance? Inheritance is the mechanism that lets one class acquire the properties and behavior of another class. The classic analogy. Think about animals. All animals breed. All animals eat. All animals move. A dog does all of those things. A cat does all of those things. A bird does all of those things, but a dog also barks. A cat also pours. A bird also flies. So instead of writing breeds, moves three times. Once for dog, once for cat, once for bird, you write it once in an animal class. And then dog, cat, and bird inherit it. Dog and cat both inherit from animal. They both have the breed and eat for free. And each adds its own specialized behavior on top. The class being inherited from is from is called the superass or par parent class. I'm going to repeat that. The class being inherited from is called the superass or parent class. The class that inherit inherits is called the subclass or child class. Open here is where cotlin differs from Java in an important way. In Java, all classes are inheritable by default. If you don't want the class to be inherited, you explicitly make it final. In Cotlin is the opposite. All classes are final by default. If you want a class to be inheritable, you must explicitly mark it as open. So why did Cotlin make this choice? It's not about safety. Inheritance is a powerful mechanism, but it introduces complexity. A subclass can override methods and change behavior in ways that the original author didn't appre anticipated by requiring you to explicitly opt with the open keyword. Cotlin forces you to make a conscious decision. Yes, I designed this class to be extended. It prevents accidental inheritance and the bugs that come with it. The same rule applies to individual methods and properties. If you want the subclass to be able to override the method, you mark it as open. If you don't, it can be overridden. So, it can't be overridden. Overriding methods. When a subclass wants to replace the behavior of an inherited me method, it overrides it using the override keyword. Notice three things here. First, make sound in uh animal. You have all of this clutter in one line, but because it didn't fit on the slide. But what you see here, this is part of this dog class. This is the body of the class. I put in one line because I don't have space for uh for it to for them to be all on their own line. Again, first you have make sound in animal which must be marked as open otherwise dog and cat cannot override it. Second, the override keyword is required on the subclass. In Java, override is optional. It's just an annotation for documentation. In Cotlin is mandatory. This catches bugs where you think you're overwriting a method, but you've actually misspelled it or got the signature wrong. Third, the last four lines show polymor. We have a list of animals, but when we call make sound on each one, the correct implementation runs. dogs for eggs, cats for whiskers. The list doesn't know or care that they're different types. It just calls make sound and the right thing happens. Calling the super class with super. Sometimes when you want to override the method, you don't want to completely replace the parent behavior. You want to extend it. You can call the parent version using super. Super.escribe calls the animals version of describe first. Then the dog's own additional line runs. This pattern calling the super and then adding to it. It's very common. It lets you it lets the child class and add behavior without losing what the parent already does. This is also works with constructors. When a subclass is created in call the parent constructor automatically, which is the which is why you pass the parents constructor arguments through the class header with a colon syntax. Abstract classes. Sometimes you want to define a class that should never be instantiated directly. It exists only to be subclass. For that you use an abstract keyword. You use the abstract keyword. An abstract class can have abstract methods. Methods with no body. Subasses are required to provide the implementation. So shape is abstract. You cannot write val s equals shape parenthesis. Shape exists only to be def to define the contract area and perimeter must exist and to provide share behavior like describe. So again shape is abstract. You cannot write val shape equals shape. Shape exist only to define the contract area and perimeter must exist and to provide share behavior like describe. Circle and rectangle must implement area and parimeter. If they don't, it's a compile error. Notice also that describe is not abstract. It's a regular method in the abstract class that uses the abstract methods. By the time by the time describe runs on the circle area and per parimeter are defined so it works correctly. Abstract class are automatically open. You don't need to add the open keyword. Inheritance versus interfaces. Another natural question when should you use inheritance and when should you use interfaces? Cotling classes can only inherit from one class but they can implement multiple interfaces. This is the fundamental difference. Use inheritance when the subclass is generally a kind of the parent class. So again use inheritance when the subclass is generally a kind of the parent class. When use inheritance when they share real implementation not just a contract. Use inheritance when the relationship is is a dog, is an animal, a car, a vehicle. Use interfaces when you're defining a capability, not an identity. Use interfaces when you multiple related classes which need the same behavior. Use interfaces when the relationship is can do. A dog can be trainable. So uh can a person So the relationship is can do. Again the relationship is can do. A dog can be trainable. So can a person. A common mistake is over using inheritance where interfaces would be cleaner. If you catch yourself writing a class just to share some utility methods. Consider whether those methods belong in an interface with a default implementation instead. The practical lure. Inherit when there is a clear parent child identity relationship. Use interfaces for shared capabilities. Seal classes and sealed hierarchy. Cotton has a special kind of class that extend extends the idea of inheritance in a controlled way. The seal class. A seal class is one where all subasses are known at compile time. You define them all in the same file. No outside no outside code can create a new subclass. The power of a seal class comes from when expressions because the compiler knows all the possible subasses. When you use as an expression it can verify that you've handled every case. If you add a new subclass to result every when expression breaks the compiler tells you exactly where to update. This is the seal class advantage. Exhaustive compile checked handling of every possible type is the right tool whenever you have a close set of related type types like network states, navigation events or UI states in Android. Common mistakes and best practices. Let me walk you through the mistakes I see most often with inheritance. Mistake one, forgetting open. If you try to inherit from a class and forget to mark it open, you get a compile error. This type is finally cannot be inherited from. you get the message the error message always check that your parent class is a marked is marked as open or abstract overusing inheritance inheritance a power is powerful but it creates tight coupling the subasses depends heavily on the parents implementation changes in the parent can s silently break the child prefer composition overhance when the relationship isn't a genuine is a is wrap objects rather than extending them mistake not calling super when you If you override the method and the parents version does important work like registering a listen or initializing state forgetting to call super method can cause subtile bugs. Always ask does the parent version do anything I still need? Mistake four everything make making everything open. Some developers coming from Java instinctively mark everything open. Don't do that. Keep things final by default. only weapon the things that you specially designed for extensions. All right, this our discussion about inheritance. This is the cheat sheet. This is what we cover in this video. I'm going to end this video here and I'll see you in the next video. I'm back. Today we're covering abstract classes in cotlin. If you already watched the inheritance video, you've seen abstract classes mentioned briefly. Today we go deep. We'll cover what they are, why they exist, and exactly how they differ from regular classes and interfaces. So let's go. What is an abstract class? An abstract class is a class that exists to be subclass, never to be instantiated directly. It's a blueprint, not a finished product. Think of it like an architectural blueprint for a building. The blueprint defines this building will have a foundation, walls, and roof. But you can't live in a blueprint. You have to build an actual house from it. In cotlin, we have abstract class shape. As you can see this diagram, shape is a concept is the idea that allows all shapes to have an area. But a shape by itself isn't a concrete thing. A circle is a shape. A rectangle is a shape. But shape alone is too vague to exist on its own. Abstract classes are automatically open. You don't need to add the open keyword. The open keyword. They they are designed for inheritance by definition. Abstract functions. The key feature of an abstract class is the abstract function. An abstract function has no body. It's a declaration without an implementation. Any class that extends the abstra class must provide the implementation. So I have shape, circle, rectangle. Notice in this diagram that if a class doesn't implement all abstract functions, you won't compile. That's the enforcement. The abstract class says all shape must be able to calculate their area and perimeter. Every concrete subass must honor that contract or fail to compile. This is the core value of abstract classes. They define a contract that is enforced at compile time, not discovered discovered at runtime. Concrete methods in abstract classes. Here is something that surprised some people. Abstract classes can also contain regular fully implemented methods. These are called concrete methods. As you can see in this diagram you have the class shape have abstract fun area perimeter and have describe and is larger than describe and is larger than are concrete. They have a full implementation right there in the abstract class and they're able to call area and perimeter even though even though those are abstract because by the time they run on any real object abstract methods are guaranteed to be implemented. This is where abstract classes shine. They can mix abstract requirements with shared implementations. The abstract methods define what subasses must provide. The concrete methods define what all subasses automatically get. Abstract properties. Abstract classes can also have abstract properties not just abstract functions. Have abstract class animal. Have abstract val. Abstract val sound of string. Have dog and cat. Name and sound are abstract properties. Dog and cat must override them in the in the and provide values. The concrete method speak uses those abstract properties and it works correctly because every animal subclass is guaranteed to have name and sound. Abstract properties follow the same rules as abstract functions. No initializer in the abstract class override in every concrete subclass. Astra class versus interface. This is the comparison level developer asks most about when do you use an Astra class and when do you use an interface? The key difference state. An abstract class can have constructor parameters and store state. Actual properties with values. An interface cannot store state. Properties in an interface are abstract by default and have no backing field. Constructor. An abstract class can has a constructor. The subclass is called an interface has no constructor. Inheritance. A class can extend only one abstract class, but a class can implement multiple interfaces. When to use an abstract class? When subasses share real state actual store values. When there is a genuine is relationship with shared implementation. When you need a constructed parameter that all subasses must supply. When to use an interface. When you're defining a capability or a contract, not an identity. When multiple unrelated classes need the same behavior, when you don't need to share state, the practical rule, if subasses share both behavior and state, use Astra class. If they only share behavior, consider an interface. And remember, a class can implement multiple interfaces, but only extend one class. Astra class versus open class. One more comparison, Astra class versus a regular open class. A regular open class can be instantiated can create object directly from it. It's it optionally provides methods that subasses can override. An Astra class cannot be instantiated. It's purely a template. It forces subasses to implement specific methods. It exists only to be extended. Use a regular open class when the class itself is useful as a standalone object. It just also happens to allow extension. Use an abstract class when the class is fundamentally incomplete by design. It only makes sense as a parent, never as a standalone object. All right, I'm going to end the video here and I'll see you in the next video. Back. Today we're covering interfaces in cotlin. If you watch the abstract class and inheritance videos, you already have a context for this. Interfaces are about defining a capability, a contract that any class can choose to implement regardless of where it sits in the class hierarchy. So, let's go. What is an interface? An interface defines a contract. It says any class that implements this interface must provide these functions. Unlike abstract classes, interfaces do not hold state. They have they have no constructor. They cannot store values in backing fields. They are poorly about defining behavior. You have clickable button and image view and both extend from the clickable interface. Both image view and button are completely unrelated classes. One is a button, the other is an image, but both can be clickable. The interface lets them share a contract without sharing a parent class. This is the key distinction from abstract classes. A class can only extend one parent class, but it can implement as many interfaces as it needs. Default implementations. Cotlin interfaces can have default implementations. Method bodies right inside the interface. This is a more powerful than Java's interface behavior sorry from Java before Java 8 and this one of the features that makes Scotland interface is genuinely useful have interface greetable have grid function and grid formal have personable robot gridable person doesn't override anything it gets both default implementation for free robot overrides grid but keeps the default grid formal Default implementation let to provide sensible behavior that classes can use as is or or override when they need something different. This is the key difference from abstract classes. Abstract method methods in an abstract class can have no body and must always be implemented implemented. An interface method with a default implementation is optional to override. Implementing multiple interfaces. This is where interfaces really shine. A class can implement multiple interfaces. Something that's impossible with class inheritance. So we have we have all of those interfaces and text input is all three things at once. Clickable, focusable and scrollable. No single uh no single parent class could have given it all of all of that without forcing an arbitrary inheritance hierarchy. Interfaces let you compose capabilities freely. Interface versus abstract class. The comparison developer asks about most. When do you use an interface and when do you use an abstract class? Use an interface when you're defining a capability, something a class can do. Multi use an interface when multiple analytic classes need the same behavior and use an interface when you don't need to share state or a constructor. Use an abstract class when you're defining an identity, something a class is. Use an abstract class when subasses generally share state. Store properties with values. Use an abstract class when you need a constructor with parameters all subasses must supply. A concrete example. Flyable is an interface. Both bird and airplane can fly, but they're not the same kind of thing. Vehicle is an abstract class. Car and truck are both vehicles and share fuel and speed properties. The practical shortcut. If there's a relationship is can do, use an interface. If the relationship is is is so use an abstract class and remember a class can implement many interfaces but can only extend one class in interfaces versus types. One powerful consequence of interfaces you can use an interface as a type any object whose class implements the interface can be referred by can be referred to by that interface type. I have interface drawable fun draw have circle square extends drawable dable and overrides draw the list is type as a list of drawable. It doesn't doesn't know or care whether each element is a circle or a square. It just calls draw on the right implementation when the right implementation runs. This is polymorphis through interfaces and it's what makes inter what makes interfaces available for writing flexible testable code functions can access an interface type as a parameter and any implementing class can be passed in including mock implementations in test. All right, I'm going to end the video here and I'll see you in the next video. Welcome back. Today we're covering data classes in cotlin. This is one of the features that cotlin developers genuinely love and once you see what it replaces you'll understand why. Data classes eliminate an enormous amount of boiler plate that Java forc you to write by hand. So let's go. So the problem boiler plate in Java. Before we look at what data classes are, let's look at the problem they solve. In Java, when you write a class to hold the attach user with a name and email, you end up writing a lot of code that has nothing to do with your actual logic. You write the fields. You write the constructor. You write the getters and setters. You write a two-string method so you can print the objects meaningully meaningfully. You write an equals method so you can compare two users by their values. You write a hash code method for using collections. And if you want to copy an object with an another with a field change, you write that logic manually too. That's six or seven distinct pieces of code. All of it repetited. All of it errorprone and all of it for a class whose only job is to hold some data. Cotlinian's answer to this is the data class. A data class is a class whose primary purpose is to hold data. You mark it with the data keyword and cotlin automatically generates all the boiler plate for you. So type data class user val email. That single line with the data keyword gives you everything. A generated to string that prints something meaningful. A generated equals that compares instances by their property values, not by reference. Two user objects with the same name and email are considered equal. A generated hash code consistent with equals so data classes instances work correctly and sets and as map keys. A generated copy function that lets you create and modify copy with one or more properties change. And the generated component 10 functions that enable the structuring. five things generated from one line and all of them are correct. You can't make a typo in an equals that forgets to compare one of the fields. So let's look at this uh tree in more detail. So we have the two string without the data class print line user would print something like user add random number not actually random but it's going to print a number the class name and memory address that is completely useless for debugging with data class you get you get a readable representation of the actual values. So you have next we have equals in cotlin equals equals calls the equals method. For a regular class two separate objects are never equal even if they hold the same values. For a data class equality is based on the values of the properties declared in the constructor. User one and user 2. U1 and U2 are separate objects in memory but they are equal because their values match the hash code. in cotlin. This is one of the this is one one this one is behind the scenes but critical when you put a data class you know when you put a you put data class instances into hashet or use them as hashmap keys. The hash code must be consistent with equals cotling generates this correctly and automatically. The copy function. One of the most useful things data classes give you is the copy function. It creates a new instance with some properties change and the rest keep the same is kept is kept the same. So we have val original val updated and have original.copy and we're going to we're going to change only the email and the rest is going to be copied. The original is unchanged. Updated is updated is a new object with the same name but a different email. You only specify the fields you want to change. The rest are copied from the original. This is especially powerful when working with immutable data. Instead of mutating an object, you create a modified copy. This pattern creating new state rather than mutating existing state is central to how modern Android development works, especially with the state in a view model and jetpack compose. I have a data class app state and you have is loading users error and each call to copy creates a new state object. Clean, immutable, no side effects. Dstructuring data classes also support destructuring the ability to unpack an object into individual variables in a single line. So we have val user user alex alexample.com and we have val parentheses and here is the structuring name, email equals to user. The parenthesis on the left unpack the user into its component parts in the order they declare in the constructor. Name gets the first property. Email gets the second. This is particularly useful for it's useful in for loops over when so this is particularly useful in for loops over a list of data class instances instead of writing user name and user email on every line this structure once at the top once at the loop head and work with clean variable names. So I'm going to repeat that instead of writing user.name and user.e on every line, you destructure ones at the loop head and work with clean variable names. Rules and best practices. A few important rules and best practices for data classes. The properties in the primary constructor are the ones used for equals hash code to string and copy. Properties declared in the class body outside the constructor are not included in any of those generated functions. This is a common source of confusion. If you put a property in the body, it won't affect fake equality comparisons. Data classes cannot be abstract, open, sealed or inner. They are final by default and indeed intended for simple data holding. Prefer val over v for data class properties. If the properties are mutable, two copies of the object can become unequal after modification which breaks the contract of using them in collections. Immutable data classes are predictable and safe. Data classes work particularly well with seal classes. Data classes. So we have data class success val data of type T is going to inherit from result is the standard pattern you saw in sealed classes in sealed class video. Each seal subclass carrying its own payload of its own payload is a data class. Again, data classes work particularly well with sealed classes. Data class as I said success val data of type is going to result. This is what I have in the transcript. So this is the standard pattern you saw in sealed classes video. Each sealed subclass carrying its own payload is a data class. Network responses. Model your API response as a data class. D serializes into it and get equals to string and copy for free. State and view model. Model your UI state as a data class and update it with copy state flow of a data class is the model Android pattern. All right. So this is what you get with data class. You get all those five uh functions autogenerated for you just by typing the data keyword as you can see here. Okay. I'm going to end this video here and I'll see you in the next video. Back. Today we're covering delegation in Cotlin. Delegation is a design pattern where an object hands off or to another. Cotlin has two kinds of delegation built into the language. Interface delegation using the by keyword and property delegation using by keyword also. But both are powerful both reduce boiler plate and understanding them makes you a significant better cotling developer. So let's go. So what is delegation? Delegation is a pattern where one object fulfills a responsibility by passing it to another object, the delegate. The classic alternative to delegation is inheritance. If you want a class to have a behavior of another class, you extend it. But inheritance is rigid. You can only extend one class and you can inherit everything whatever you want it or not. Delegation is more flexible. Instead of extending a class, you hold a reference to it and forward calls to it. Your class isn't a special kind of a delegate. It just uses it. In cotlin interface delegation is handled with the by keyword. When you write a class login printer private val printer and it's going to extend printer by printer. You're telling coin login printer implements the printer interface but every method call should be forward to the printer object. Cotton generates all the for forwarding code at compile time. you write nothing. This is the core idea. A wrapper class gets to implement an interface entirely through it delegate without writing a single forwarding method. Overriding selectively the real power comes when you combine delegation with selective overriding. The delegate handles everything by default but you can override specific methods when you need different behavior. Class login printer private val inner printer printer by inner and override print login printer delegates everything to inner except for print function which is which it intercepts log logs first then calls through the delegate. This is the decorator pattern adding behavior around an existing implementation without changing it. And in Cotlin, you don't need to write a forwarding method for every other function in the interface, only the ones you care about. Calling inner.print do explicitly passes the call to the wrapped object. You can log before, log after, modify the arguments, suppress the call entirely, anything. The delegate handles the rest. Delegation to multiple interfaces. Because delegation is an inheritance, a class can delegate to multiple independent objects, one per interface. We have class duck flyable swimable flyable by fly swimable by swim. Duck doesn't implement flyable or swim swimable itself. It delegates flyable behavior to fly and swimable behavior to swim. Both implementation are injected. You pass them in a in at the construction time. This is the composition of inheritance in practice. Instead of a deep classy hierarchy, you assemble behavior from small focus reusable pieces. Want a different flying behavior? Pass a different flyable. Want to sub want to swap the swimming implementation in a test? Inject a mock. The comma separated by closes in the class header are all you need. No BR, no forwarding methods. Cotling generates everything. Property delegation. The second kind of delegation in cotlin is property delegation. Instead of delegating interface methods, you delegate the getter and setter of a property to another object. The property delegate the syntax uses by and the property itself. Val name string by lazy compute name. Lazy is a built-in property delegate. The first time name is access it calls compute name and caches the result. Every subsequent access returns the cache value immediately. Delegate that observable wraps a property and calls the lambda. Every time the value changes, you get automatic change notification without any extra code. Both lazy and observable are just objects that implement the read only property or read write property interface unless you write your own property delegates too. If you have a pattern you want to reuse across many properties, you implement those interfaces and use it with real example caching with delegation. Let me show a practical example. Adding a caching to a data source using interface delegation. We have caching repository, private val repository, repository by real and we have private val cache. We have override get user. Caching repository wraps the wraps any repository. Every method except get user is automatically forward to real. Get user is intercepted. It checks the cache first. If the user is already there, it returns a cache value. If not, it calls real.get user stores the result and returns it. The original repository is untouched. You can add caching to any repository implementation by wrapping it. No modification to the original class needed. This is open closed. Open for extension. Close for modification. So we can also delegate to multiple list as you can see here. All right. I'm going to end this video here and I'll see you in the next video. Back. Today we're covering extension functions. One of Cotlin's most expressive and widely used features, extension functions let you add new functionality to existing classes without inheriting from them without wrapping them and without touching their source code. It's one of those features that once you understand you start seeing everywhere in the Star library in Android APIs and every well-written codling codebase. So let's go. So what is an extension function? The idea is simple. You want to add a method to an existing class but you don't own that class. In older languages the answer was either subclassing or writing a standard utility function. Cing gives you a third option an extension function. Here is the comparison. We have fun is email valid email of type string return email that contains at and the extension function approach string dot. So we have the class email valid. The syntax difference is the type prefix before the function name string easy is email valid and that's how you create the extension function. That's the receiver type inside the function. This refers to the string instance that the function was called called on from the caller's perspective. It read as if is mail valid is a real method on a string. This is purely a compile time mechanism because it doesn't modify the string class. The extension function is compiled to a static function under the hood. But the syntax at the call site looks like a member function call. Extending built-in types. Extension functions are most powerful when you extend tiles you don't own. You can extend string in list anything. We have fun string capitalized words and then you type hello world capitalize words. This has the a capitalized words method to every string including string literal. The split and join to string inside the function body uses this implicitly because this is the string being extended. You can extend int just as naturally. So you can type in prime parenthesis. And now you create an extension function for the integer class. Not every int has an its prime method. There is no wrapper class, no utility or just a function that reads naturally at the call side. Extending your own classes, extension functions aren't just for the built-in types. You can also add them to your own classes too. And this is where they become powerful design tool. Have data class user fun user grid and have the extension function created. Have fun user initial initials. The extensions live outside the user class. This means that you can define additional behav behavior for user in different files, different modules or different feature packages without touching the user class definition. One important rule, extension functions can only access the public API of the receiver. They don't have access to private or protected members. Inside grid, you can use name because it's a public property. If user had a private field, the extension couldn't see it. This is what distinguishes an extension from a member function. Generic extensions and nullable receivers. Extension functions work with generics too, letting you extend parameter size types fun angle brackets t list t have list tc or null. This extends list for any type. Second null is now available on every list. A list of ins, a list of string, a list of users. You write it once and it works everywhere. You can also define extension on nullable receivers. So you have v uh fun string the class the lvis operator dot or empty. Notice the receiver type is nullable string with a question mark. This means that the extension can be called on a nullable string without a null check first. Inside the body this is nullable. So you handle with lvis operator. This is exactly how cotling built in or empty is null or empty extensions work. So key rules member pri priority and static dispatch. The important rules that every codlin developer needs to know. Rule one if a class already has a member function with the same signature as your extension the member function always wins. The extension is simply ignored. We have class fu fun.hello Hello. You can't override the member function with an extension. If there is a conflict, the member wins. Always define define extensions with names that don't clash with existing members. Rule two, extensions are resolved statically at compile time based on the declaration type of the variable, not at runtime. We have open class shape. We have fun shape. F circle draw. Even though have val shape circle even though s holds a circle at runtime the declare type is shape. So shapes extension is called. This is different from member function polymorphis which dispatch is based on the runtime type. Extensions are not polymorphic. If you need polymorphic dispatch use a member function or an interface. All right I'm going to end this video here and I'll see you in the next video. Welcome back. Today we're covering generics in Cotlin, specifically type parameters. Generics are one of those features that seem intimidating at first, but once you understand what problem they solve, they become one of your most used tools. We'll cover what generics are, how to write generic functions and classes, how to constrain type parameters with upper bounds, and how to use multiple type parameters. So, let's go. The problem without generics, you have to look at generics. Let's understand the problem. Imagine you want to write a function that returns whatever value you give it. An identity function without generics. You would have to write one version for int, one for string, one for double, and one for every type you need. This is repetitive and it doesn't scale. Worse, you have no way to write a single function that works for any type while still preserving the type information. You might think just use any cotlin's root type, but then you lose the type information. You have to cast on the way out and and the compiler can't help you if you cast the wrong type. Generics solve this. They let you write a function or a class that works with any type while keeping all while keeping full compile time safely. Generic functions and classes. A type parameter is a placeholder for a type declared in angle brackets. You write t inside angle brackets or any letter t is just a convention. So you type fun angle brackets t identity value of type t. Then you have one function tree uses three different types. Cotton infers the type parameter from the argument you pass. You don't need to specify it explicitly. The same idea applies to classes. We have box t val content of type t. box angle brackets t can hold any type. int box get returns an int. string box get returns a string. The compiler tracks the type to through the type parameter and prevents from putting a string into an int box or vice versa. Upper bounds. Sometimes you want t to be any type but you also need to call specific methods on it. That's where the upper bounds are for. An upper bound uses the colon syntax. You have t colon and going to have some type. It means t must be a subtype or subtype of some type which guarantees that all methods of some type are available inside the function. So I have found t colon comparable of type t and have max. Then I have t colon comparable t means t must implement comparable. Again t colon comparable means that t must implement comparable. So the greater than sign operator works. You can now call max with any type that's comparable. Int, string, double, a data class that implements comparable without the upper bound. A greater than b would be a compile error because t could be anything including a type that doesn't support comparisons. You have t you have fun angle brackets t colon number here t colon number means t must be a number int double long float all qualify. This lets you call to double on each element without the bound to double would be a n a noun unknown on t generic classes in depth. Let's look at more complete generic class a stack. We have stack T and have private val items push pop size stack T can hold any type. The internal list is type as a list of T. Push accepts a T.OP and P return T. The type flows through the whole class consistently. When you create a stack of int, T is fixed to int for that instance. Every method now works with t with int. Specifically, the compiler prevents you from pushing a string into an int stack. Generic extension functions. One of the most useful patterns in cotlin is writing generic extension functions. Functions that add behavior to a generic type. You have fun t list of type t second or null. Second on all works or any list of t list of int, list of stringing, list of anything. The return type t is inferred from the list element type. You can also have multiple type parameters like fun angle buckers t r list t map first. T is the input type. R is the output type. The transform lambda converts from t to r. Both types are inferred from content. You don't need to specify either explicitly. Generics with seal classes. Generics and seal classes compose beautifully. This is the pattern you see everywhere in modern Android development. So have sealed class result out and have t. We're going to see out what it is in next video in the next videos. Result T wraps a successful value for any type T. The out keyword means T is coariant. We'll cover variance in the next video. And you have fetch you have id in result user and have this when you have fetch user returns result user inside the result that's success r is the smart cast to result success user so r data is a user directly accessible this pattern generic seal class for type results is ideomatic in cotlin all right I'm going to end this video here and I'll see you in the Next video. Welcome back. In the previous video, we cover type parameters, the basic of writing generic functions in classes with T. Today, go one step further. Upper bounds. Upper bounds let you constrain what T can be. And by doing that, they unlock the methods of that type inside your generic code. So, let's go. The problem unconstrained T. When T is unconstrained, the compiler knows nothing about it. It could be any type, a string, a custom class, a database connection, anything at all. So it won't let you call any methods on t because it can guarantee that those methods exist. Here is the classic example. You want to write a generic max function to return the larger of two values. This give you a compile error. The greater than equal operator is not defined on t. The compiler doesn't know what t supports. If t supports comparison, the fix is an upper bound. Type fun angle brackets t extends comparable. T extends comparable says T must implement comparable interface. Now the compiler knows that the greater than equal to is available and you can call the max with any type that's comparable int, string, double or a custom class that implements comparable, number and numeric upper bounds. The most common upper bounds you'll use are comparable and number. Let's look at number. So have fun t extend number. T extends number means t must be a sub type of number. That's int, double, float, long and other numeric types. The bound guarantees that two double is available because number declares it without the bound calling two double when t would be a compile error. A more practical example have fun t number clump value t min max t and it's going to return a double. Clump restricts a value to a range. You can call it with int, double, float, any number. The bound is what makes two double safe to call. Without it, you have a compile error. Custom interfaces as upper bounds. Upper bounds don't have to be to be standard library types. You can use any interface or class as an upper bound including ones you defined. We have interface printable and have fun of type. It's going to extend printable. It's going to print all. T extends printable means t must implement printable which guarantees print info is available inside the function. Any class that implements printable can be passed to print all. Any class that doesn't it's rejected at compile time. This is a powerful pattern. You define the contract as an interface. You use it as an upper bound and your generic function gets to call that contracts method safely on every T. Upper bounds and extensions. Upper bounds work seamlessly on generic extension functions too. This is where they become especially expressive. We have found T extends comparable list of type T that clamp all. Clump all extends any list of type T where T is comparable. The bound gives you the less than sign and greater than sign inside the lambda. The result is the new list with every value clamped between min and max. This is only this only compiles when t is comparable. Trying to call clump all on a list of any or a list of non-comparable types fails at compile time exactly as it should be. Upper bounds on generic classes. Upper bounds can also be placed on type parameters of a class, not just functions. Have class sorted list T extends comparable of type T. The sorted list T extends comparable of type T declares that T must be comparable for the entire class. This what makes items that sort val sort requires elements to be comparable. Sorted list int and sorted list string both work. Sorted list any is a compile error because any doesn't implement comparable. The bound on the class protects every method. You can create you can't create an unsortable sorted list. The constraint is enforced the moment you try to instantiate the class with the wrong type. All right, this our discussion. I'm going to end this video here and I'll see you in the next video. Welcome back. Today we're covering coariance and countervariance in cing generics. The out and in keywords. These are concepts that trip a lot of developers. But once you understand the core idea, they become completely logical. By the end of this video, you'll know exactly what out and in mean, why they exist, and when to use each one. So let's go. Why variance exist? Let's start with a question. We have a list of type int. int is a subtype of number. So should list int be a subtype of list number? Intuitively, yes. If you have a list of integers, you should be able to use it wherever a list of numbers is expected. You have val ins, list of int, val nums, list of number. And in cotlin, it does work. List is coariant. You can assign a list of int to a list of number. Now the same question for multiple list. You have val mins, multiple list one, two, val, m nums, multiple list of number. And you assign mins to m nums. This doesn't compile. Multiple list is invariant. Multiple list int is not a multiple list number. Why? Because if it were allowed, you could do this. M nams add 3.14. So we add the double into what was an int list and have al x int my in streak and have class exception at run allowing the allowing the assignment would break type safety. A mutable list needs to be invariant to stay safe. This distinction is the whole reason variance exists. Out coariance coariance is marked with the out keyword. When you declare a type parameter as out t, you're saying this class can only produce values of t. It never consumes them. And because it only produces t is safe to treat producer int as a producer number. We have class producer out t val of type t. I have val ip producer int producer 42 val np have producer number equals to a out the out t makes this valid out t means t can only appear in output positions return tiles it cannot be a function parameter that restriction is what makes coariance safe you can only read the out of the class there is no way to put a double into an in producer You've already seen this in practice. Cotlin's built in list out e is defined without. You can only read from a list. You can't add to it. So list int can safely be treated as a list number. The most important real world example is sealed result out the standard pattern for returning type results from a view model or repository in counter variance. Counter contra variance is marked with the in keyword is the opposite direction. When you declare an int, you're saying the class only consumes values of t. It never produces them. This makes the subtyping relationship flip. So I have class consumer angle brackets in T. I have val NC consumer number equals consumer val consumer int equals to NC and T makes this valid. A consumer number can consumer number can consume any number including int. So you can use it whenever a consumer int is suspected. The relationship is flipped. Consumer number is a subtype of consumer int. Why this is said because NC can pro process any number. It can certainly process an int. You're not adding anything problematic. You're just accepting a broader consumer. In t means t can only appear in input position function parameters. It can't it can't be a return type. Comparator int is the classic standard library example. A comparator for number can compare any int. So use side variance out and in at at the call side. So far we talked about declaration side variance putting out in or out in the class declaration itself. But you can also use variance at the call side on individual function parameters. This is called use side variance and cotling calls calls it a type projection. have a sum list list of of list of angle brackets out number. We're going to return a number. We have sum list 1 2 3 sum list 1.5 2.5. So you have a list of int, a list of double. List out number means accept any list of a type that is a subtype of number. You can pass list int, list double, list number. The function only only reads from the list. It doesn't write to it. Fun add numbers list multiple list in int and have list add one list add two we have val nums multiple list of number and have add numbers nums this is a multiple list of number and it's accepted multiple list in int means accepts any multiple list of a type that is a super type of int. You can add in to it safely. Multiple list number qualifies because number is a super type of int. Invariant versus coariant versus contariant. Let's put all three together in one place. Invariant invariant class box t not out or in box int box number are completely unrelated types. Neither is a subtype of the other. Covariant class source out T produces T. Source int is a source number. Subtype flows in the same direction as the subtype relationship. Contravariant class sync in T consumes T. Sync number is a sync int. Subtype flows in the opposite direction. The minomic packs producer extends consumer super. The producer uses out extends equals sub subtype. The consumer uses in super equals super type. Use out when a class or a function only reads the use in when it only writes t. Nothing and any as a variance boundaries. Two special types complete the variance picture. So two special types complete the variance picture. Nothing in Cotlin's bottom type is a subtype of every type. Nothing means this code never produces a value. It's used for functions that always throw or throw or for empty producers. Object loading result angle brackets nothing. Object empty result nothing. Loading doesn't produce a value. So result nothing is the right type. And because result out the is covariant result nothing is a subtype of result any it can be assigned to any result t any is cotlin's top type every type is a subt type of any sync any can consume anything is the most permissive consumer these two types mark the boundaries boundaries nothing is the most specific producer any is the most permissive consumer in a coariant hierarchy nothing sits at the bottom in a contravariant hierarchy and he sees at the bottom. All right, I'm going to end this video here and I'll see you in the next video. Welcome back. Today we're covering one of the most misunderstood parts of cotlin generics type eraser and reify keyword. If you ever try to check whether a value is a specific generic type at runtime and hit a compile error, this video explains why that happens and how refi solves it. So let's go. What is type eraser? The type eraser is a J JVM concept. When you write generic code in cotlin, the type parameter information exists at compile time. But by the time the code runs on the JVM, that type information is gone. It's erased. Here is the consequence. This compile fine fun t print type value t. That works because you're calling simple name on the actual value, not on t. The value is a real object at runtime. It knows its own type. But this doesn't compile fun or type in angle brackets. type value any the error cannot check for instance of erase type T at runtime T has been erased to any the JVM sees something like this fun type value any return value it's any there is simply no T left to check against this is the type eraser in action the JVM never knows what T was at trying time refi the refi cures all this type eraser But it requires one more keyword alongside in line. When a function is marked in line, the cotton compiler copies the functions by code to every call site. Instead of a function called the body is literally inserted where it's called. And when you also mark t as reified, the compiler can substitute the actual type at each insertion point because the type is known at compile time at each call size. We have in line reify t in angle brackets is type value any no more cannot check for erase type error at each call side. The compiler knows the is a string or int or whatever concrete type you pass. It substitute that type into the inline body. You also get t colass without ref t class. It's a compile error with refi. The compiler knows that T is and can resolve the class reference. Practical use filter is instance. One of the most common use of reit is filtering a mix list by type. Here is a custom extension that runs the built-in filter is instance in line funny refi t list angle bracket star that filter is type without refi you couldn't pass t to filter is instance t is erased with refi t is available at each call site and the compiler can do the filtering correctly cotlin's built-in filter in instance is self-written with three file ID when you call it the type you specify in the angle brackets is preserved by the inline mechanisms and checked against each element. This is the pattern you'll use constantly. Refit makes type based filtering concise and safe. Radical use JSON and class reference. Another another very common use case is any API that requires a class or a cotling class reference. The canonical example is JSON derialization without reified you most you must pass the K class manually. So we have found T any parse JSON JSON string class ZZ and we have that we reified T column class that Java is available inside the function. So you never need to pass it as a parameter. The call site is dramatically simpler. This this same pattern applies to anywhere you'd otherwise need to pass a K class or class parameter. JSON, Moshi, room time converters, retrofit converters and many other libraries. Android patterns start activity and view model. The two pattern that appear everywhere in Android code become much cleaner with rei. The first is start activity in line. Fine ref activity contest does start activity without referring time with refi. The activity type is inferred t col java it's resolve and the call site is one liner. The second is view model creation. So we have line fun refit view model fragment view model return view model provider this.get Get t class java again t col class class java is the only access it's only accessible because t is reified the view model type flows from the color into the factory call no separate parameter no casting these two patterns illustrate the core valuei it eliminates prepetive class arguments and let you type system to do the work what rei can and cannot do let's be precise about what Rei can not do. Let's be precise about what refi gives you and uh gives you and what it doesn't. With rey t you can check is t castless t access t with t as a type argument in generics like list of t or filter is instance. What refi does not give you you cannot call t parenthesis. There is no constructor access. You cannot create instances of the in a generic way. And refile requires inline. You cannot use refile on a noninline function or on a class type parameter. Only inline function type parameter can be refiled. One more important constraint. Java to code cannot call inline cotlin functions. If your function is part of a public API consume from Java. You cannot use line and therefore cannot be cannot use refile. This is a real limitation to be aware when you writing cross language libraries. All right, I'm going to end this video here and I'll see you in the next video. Back. In the previous generics videos, we cover type parameters, upper bounds, and variance. Today, we tackle the wear close. Cotlin's way of expressing multiple constraints on a single type parameter. The moment you need this to satisfy two or more bounds, the column syntax stops working and where takes over. By the end of this video, you'll know the syntax, the rules, and when to use it, and the common mistakes to avoid. So, let's go. The problem one colon one bound. First a quick recap of the single bound syntax. You use a colon directly in the type parameter declaration. We have fun t extends comparable one column one bound clean. This is the ideatic cing style for a single constraint. Now suppose you need t to be both comparable and serializable. You first your first instinct might be to chain another column. We have t comparable. So T col comparable and colon again serializable but this is a syntax error. The colon syntax allows allows exactly one bound. The moment you need to you switch to air. So you type fun t max serial. We have a t a a t and a b t and where t comparable and t colon serializable. The where keyword goes after the closing parenthesis of the parameter list before the opening braces or the equal sign in a single expression function. Each constraint is separate t bound pair separated by commas. This end logic n not or this is end. I'm going to repeat this is end logic not or t must satisfy every bound list. A type satisfying only comparable but not serializable is rejected at compile time. The most common real world use case is combining two interfaces. You define yourself. Imagine you're building a system where two items needs to be both printable and logable. So have interface printable interface logable. You have a fun a fun have a function of type in angle brackets we have t process and we have at the end of the closing parenthesis where t printable t logable. So two T colon printable t logable inside the process both item.print and item.log are directly available. The compiler has verified from work clause that empty. So the the compiler has verified from the work clause that t implements both interfaces. So both methods are guaranteed to exist. No casting, no null, no null checks, no runtime surprises. The implementing class provides both the print and log functions. If you pass a class implementing only printable compile error, only logable compile error. Only a class satisfying both only a class satisfying both bounds passes. That's the entire point. The compiler enforces the full contract at every call site. Three bounds were on classes. You're not limited to two bounds. The syntax supports any number. T must be all three. In practice, having more than three bounds usually signals that a combined interface would be cleaner. And we'll cover that tip shortly. But the syntax handle as many as you need. The wear clause also works on generic classes, not just functions. When you put the wear on a class, every method in the class body can use all the bound methods on T. T sorted on T. sorted works throughout because the class level constraint guarantees that T is comparable. You cannot instantiate repository with a type that doesn't satisfy both bounds. The compiler rejects it the moment you try generic class. So I have full class example. Let me show you a more complete example. A generic class using two custom interfaces with the wear clause. We have cachable, validatable and have data processor and have where t colon cachable or t equals validatable inside the process. So inside process item that validate and item that here are both directly accessible. The work clause on the class ensur ensures that the satisfy both interfaces throughout the entire class body in every method in every property initializer. Any class implementation any class implementing both cachable and validatable can be used as t. Any class missing either one is rejected when you try to create data processor with that type. The type system enforces the complete contract at compile time. Colon versus where. The rule is simple. One bound use the column syntax is shorter, reads naturally, and is the ideomatic style. Two or more bounds, use a wear. The only option you cannot chain columns. A practical tip. If you if your wear clause grows long with many bounds, consider creating a combined interface. Instead of repeating where t a t b so where t colon b t col a everywhere define interface ab so define the interface ab and put colon a comma b and use t colon ab this gives the constraint a meaningful name and keeps all your call sites clean real world example product search here is a practical scenario you're building a product search Products needs to be comparable for sorting and serializable for caching. You want a generic find cheapest function. You have product. You have the function find cheapest products. Mean or null is available because T is comparable. The serializable bound and source results can be cached and transmitted. Product product satisfies both bounds. So it passes the compiler check. Any future type you create that satisfy both bounds works with this function immediately. Zero changes needed. Common mistakes. Three mistakes that catch people out. Mistake one, chaining commas in the type parameter list. This actually compiles. So this actually compiles, but it declares two separate types of parameters T constraint by comparable T and the second a named type parameter called serializable. This is almost certainly not what you intended. So use where for multiple bounds on the same T. Mistake to confusing and with or. T must be both printable and logable. There is not no no or in Cotlin's generic bounds. If you need or style logic T can be either A or B. Seal classes or a custom super type is the right tool. Mistake three wrong position for where. So have fun t process item where t column printable where comes after the closing parenthesis of the parameter list before the opening brace of or equal sign putting it before the parameter list after the return type on a different line without the proper syntax. This won't compile remember after closing parenthesis before opening curly braces. All right I'm going to end this video here and I'll see you in the next. Welcome to the collection section. Until now we have worked with single values and individual objects. From here on we work with groups of them. A list of products, a set of selecting identifiers, a map from a user ID to a user record. Almost every screen in Android application display displays a collection of something. Cotling gives you three container types for this. There is list, there is set and there is map. Choosing between them is not a matter of preferences. is each answers a different question and picking the wrong one gives you the code that either slow slower than it needs to be or that fails to express what you want. So this lesson is partly a tour of the API and partly a decision procedure procedure three shapes of data. Here are the three types of three types side by side. A list is an order sequence. Position matter. The duplicates are allowed and you need elements by index. You can see the indices printed above the cells and the letter A appearing twice without any problem. A set is a collection of distinct values. No index, not duplicates. The second A is is drawn gray out because it was discard discarded when when the set was created. Notice what that means. The set does not re report an error and does not own it. It keeps one copy one and continues. If you passed five values and got three back, that is normal behavior. A map is a set of key to value entries. Keys are unique and you need a value by supplying its key. And at the bottom are three builder functions you'll use most list of, set off, and map off. How they relate? Before the details here is how the the these types relate because it explains something you'll notice constantly at the top is the iterable below it is collection below that is this list and set that shared an ancestor why we why the same operators work on both map and filter for each. So both works for map filter for each count and every other function in the in the next 11 lessons are declared as extension functions on iterable not separately for listen. So when you learn filter in lesson 130 you have learned it for both types at once. Map is drawn separately because it's not an iterable that changes how you use it. A map entries rather than a map has entries rather than elements. So you go through one of its three v three three views entries keys or values each of those is a collection and once you you are there all the usual operators applying we will you we will see that on slide in slide 15 read only versus mutable. Now the distinction that separates cotling collections from Java collections. Every collection types comes as pairs of interfaces. One the left is a list. The read only interface. It declares size get contains iterator. The operations that only read. You can you can you create one with list of this is your default. On the right you have multiple list. It extends list and adds add, remove, set and clear. You create one with multiple list of and you should use it only when the code generally needs to modify the collection the collection in place. The same pairing applies to set and to multiple set and to map and mutable map. The practical advice declare the read only types only if you have a reason not to a parameter declared as list product tells the caller that you'll need their data and not change it. Declare as declare declared as multiple product. The signature says the opposite whatever you meant it or not. Read only is not immutable. An important nuance because the previous slide can be misread. List means you cannot modify the collection to its reference. It does not mean the collection cannot change in the code. Source is a multiple list of A and B. No, we declare view with with the type list string and assign source to it. That compiles because multiple list extends list. Then source add C is allowed because source still has the mutable type and printing view shows A, B, and C. The card on the left explains why view and source are the same object. There is one list in memory. The read only type hides the mutating methods. It does not copy the data and does not freeze anything. The card on the right gives you the fix call. You have two list when you hand a collection to code that must see must not see later changes that allocates a new list and copies the element reference into it. This matter most when a class exposes a collection. The private multiple list returns a list that can be cast back and even without a cast the caller sees every every later change. Creating a list. Five ways to create a list each with a situation it fits. List of ABC. Build a read only list that from values you already have. Note you will write this most often. Multiple list AB builds a read read right list back binary list fast access by index fast slow in the front empty list returns an empty list. Note that the explicit type argument there is nothing in the call for the compiler to infer from list and we have five curly braces each time it builds a list of a given size from the index producing 0 1 4 9 16 useful for test data in fixed size structures. Build list gives you a multiple list inside the lambda and returns a read only list at the end. Use it when construction takes several steps and you don't do do not want the multiple types to escape. The note is a small optimization list of with no arguments and empty list return the same share object. So either so neither allocates reading from a list reading is a position base and there are exactly two ways you can go wrong. negative index or index at at at or beyond the size. Four elements index is zero to three. The the size is four and the last valid index is three, not four. That off by one is there is where most index errors came from products z square brackets zero returns. The square brackets are the get operator. So products that get zero it's identical out of range it throws index out of bound exception products that get null and you have nine return null instead of throwing the return type is string so the compiler force you to handle the missing the missing case products that get or else nine curly braces have question mark computes the fallback from the index products products that indices gives you the valid The RA the valid index range it cannot be wrong because it is derived from from the site. The warning is not an exaggeration. Index out of boundation is the most common runtime failure with list and get or null removes the whole category. If an index comes from anywhere other than indices use it modify modifying a multiple list four operations and one pair that is easy to confuse. Add appends with no arguments. It inserts an index and shifts everything after it. Remove removes an element by value. It removes the first occurrence and returns a boolean saying whatever anything was removed. Remove at removes by index and returns the element it removed. Set or square bracket assignment replaces the element at index. The size does not change. Now the wording with a multiple list string. You cannot confuse the first two because the types differ with a multiple list int both remove at remove remove one and remove at one compile and they do different things. One deletes the integer one the other deletes whatever is in the second position. The compiler cannot help you there. A set discards duplicates. The second types five values go in A B A C B with the repeats marked in in in amber set of gives back A B and C. A set A set holds distinct values and enforces that on insertion silently. Three consequences. There are no position. There is no set zero because a set has no first element. The the question you ask a set is does it contain this? Not what it's index not what is at index index two. So the question you ask a set is does it contain this not what is at index two contains is fast on list it scans element by element when a set it computes a hash and looks at it one place for 10 items the that does not matter for 10,000 check inside the loop it is the difference between an application that responds at one and one that does not insert order is preserved by the default implementation set off returns the links are set so iteration follows the order elements ments were first added. Three set implementation. Three builders functions differing in exactly one respect. The order you observe when you iterate set off and linked set off produce a linked set iteration follows insertion order at a small cost in memory. This is the default and what you want almost always because it makes the output predictable and test deterministic. Hashet off produces a hashet not guaranteed order and the order can differ between runs. It is the smallest and fastest wit for large and only for test membership against sorted setup produces a tree set elements stay in natural order or in the order comparator the comparator gives inserts are slower and use it when always need to elements element sorted anyway. So use it when you always need the element sorted anyway. The note describes a real situation. A test that compares a set to an expected list passing on your machine and failing on the build server and or an on order hash set is very often the case combining two sets. Sets supports three operations from mathematics. Set A holds 1 2 3 4. Set B holds three four five. A union B gives everything in either set one through five. A intersect B gives only what is in both three and four. A subtracts B gives you what is in A but not in B. One and two. This is one is not symmetric. B subracts A would give you five. All three returns a new set. Neither openend is modify. Worth stating because the name sound like sounds sound like commands and behave like expressions. that are generally useful. Comparing a set of already loaded ids against a set of newly fetched ids tells you with substract in each direction exactly what to add exactly what to add and what to remove. So all three return a new set. Neither operand is modified word star stating because the names sound like commands and behave like expressions. They are generally useful. Comparing a set of already loaded IDs against a set of newly fetched ids tells you with to subract in each direction exactly what to add and what to remove. Keys and values. The third type a map. A map answers one question. Give this key what is the value? The code builds the map from product name to pride to price. On the right is the same data drown as key to values rows. The type is map string double. The key type first and value type second. The note explains a piece of syntax that surprises people. There is no special map syntax in cotlin 2 is an ordinary infix function that builds a pair. So phone to 6990 is a normal function call written without a dust or brackets. Map off takes those two pairs as a v ar that matters practically. You can build the pairs anywhere. Manipulate them as list and call to map at the end. Nothing is special case in the language. Four ways to read the value. Four ways and the whole difference is what happens when the key is absent. Prices square brackets watch returns. No. The square bracket operator has return type and never throws. Price get value watch throws no such element exception. It returns type V not types V not nullable and the rest of that is an exception. Get or null default key 00 returns the default which is an ordinary argument and is therefore evaluated whatever or not is needed. Get or else key calculate takes a lambda. So the fallback is completely only when the key is missing. If if producing it is expensive, use this one. The card on the left on the left gives you the general advice. Prefer the square brackets. The nullable return type means the compiler will not let you ignore the missing case and making that decision explicitly is usually the point. The card on the right says we get when get value fs when a missing key would be a programming error rather than a possible state. Then an exception is the correct response. Modify modifying a multiple map. Stock square square brackets laptop adds equals 4 adds one entry. Stock phone at equals 9 replaces the existing value. Those two lines are identically informed. The same thing does those two different things depending on the current contents and gives you no indication of which happened. This is the call out assign assignment replaces silently. If you need to know whether the key was already there, check contains key first or use put which returns the pro previous value. Stock that remove form removes the entry and returns the value that was removed. Stock get or put watch curly braces zero reads the value for for a key if the key is substantive value is a lambda stores that result and returns. It read and initializes in one call. the card names the the pattern this enables counting occurrence is the standard example counts that get or put word cur square bracket zero gives a starting value for a word you have not seen so the increment that follows always has something to increment iterating a map the for loop iterates the map directly and the structure each entry into name and price that works because map entry declares a component one and component The same mechanism that lets you destructure data class. Below it are three views. Prices that keys has type set or string. Keys are unique. So set is the natural type. Prices that value has type collection double. Read that carefully. Not a set because values may repeat and not a list because there is no meaningful index. Collection is the type that is neither. Prices that entry has type set map entry. map entry uh angle brackets KV and is the it it's it is what the loop above iterates. These three views are how you connect a map to everything else in this section. To filter a map or count this contest, you go through one of them and every operator from the following lessons applies list versus set versus map. All three compared across five questions that separate them. Order the access. A list gives you access by index. A set a set gives you a proportional access. A positional access. So a set gives you a position access. A map gives you access by key. Duplicates. A list allows them. A set discards them. And a map allows duplicate values but never duplicate keys. Cost of contains. A linear scan for a list. A linear scan for a list. A hash lookup for a set. A hash lookup by key for a map. Builder. List of set of map of typical use order records membership test lookup tables. The note identify which row actually matters. Four of the five are about expressing intent and reasonable code works either way. The contains road changes the running time for your program. A membership check inside the loop against the list is quadratic. Against a set is linear and at small sizes you never notice. At realistic size is the difference between a smooth screen and a frozen one. Which one do I need? A short decision procedure. Three questions in this folders. Stop the first. Stop at the first. Yes. Do I look things up by a key? Use a map is the question people skip most often. Anytime you write a loop over a list to find the items whose ID matches something, you have describe a map lookup. Lesson 120 shows exactly how to build one. Must the values be unique? Use a set. It enforces uniqueness for you. So when you never so you never check before adding and and it makes membership test fast. Enforcing uniqueness manually when a list is slower and easy to get wrong. Otherwise use a list. It preserves order is the easiest the easiest reason about and every operator is in the next 11 lesson is defined on it. The order is deliberate. Most developers start at question three and stay there and that is where the performance problems in question one come from. Moving between the three. You are not locked into your first choice. Converting is one call set list set list to set removes the application and keeps the order in which each value was first seen. List instinct produces the same elements but returns a list. Choose by the return type you want. Distinct if you if you will you will index the result to set is the next thing is is the next thing is a membership test. If the me is the next thing is a membership test. Set that to list fives fixes an order so the elements can be indexed. This is what you do before handing a set to a recycle view adapter which needs positions. List associates with curly braces braces builds a map using each element as a key. The whole associate family is less than 120. Map that to list gives a list of pairs useful when you need to sort the entries because sorting requires an order and a map does not have one. Every one of these allocates a new collection and none of them modify the source. This is worth internalizing now with a very exceptions. Collecting functions in quotes return new collections. Four things that go wrong. Four mistakes are compiles without a warning. Declaring multiple list everywhere. A function that only need only should declare list should declare list. Multiple list gives you the color no guarantee and it also stops the passing your you you read only list at all. I'm going to repeat that for mistakes all compile without a warning. Declaring multiple list everywhere. A function a function that only reads should declare list multiple list gives you the caller no guarantee and it's also and it's also it it also starts them passing you a read only list at all. Returning the backing list. A private multiple list returns from a getter types as list has protected nothing. The caller sees every letter change and can cast it back. Return to list. Remove versus remove on integers. Both compile and both are wrong half the half half of the time. Scanning a list for an ID. list that first ID if that ID equal six is it's fine once inside the loop is the it is a nesty scan whose cost grows with the product of the two sizes build them up once outside the loop all right this is our recap I'm going to end this video here and I'll see you in the next video come back today we're covering binary search in cotlin binary search is one of those algorithms every developer should understand deeply not just how to call it but why it works and why it's valid We'll cover the concept visually, the built-in cotlin functions, how to implement it yourself, and how it handles not found cases. So, let's go. What is binary search? Binary search solves one problem. Find the target value in a sorted list efficiently. The naive approach is linear search. Go through every element from the beginning until you find the target. That works on any list, sorted or not. But in worst case, you look at every element. If the list has a million items, that's a million comparisons. Binary search is dramatically faster. It works by repeatedly halfing halfing the search space. So dividing by half the space every time. Look at the middle element. If the if it is the target, you're done. If it is less than the target, the target must be on the right half. If it's greater, the target must be on the left half. Discard the half that can't contain the target and repeat. This give you big O of log N time complexity. For a list of a million items, binary search takes at most 20 comparisons. For a billion items, 30 comparisons. The catch, the list must be sorted. Binary source relies on the sorted order to know which half to discard. If the list is in sorted algorithm produces wrong results, let me walk you through a complete example visually. We have a sorted list 2 5 8 12 16 23 38 66 72 91 10 elements. Indices 0 to 9. We're searching for 23. Step one, low = to 0, high = to 9, middle index = to 0 + 9 / 2, which is equal to 4. The elemental four is 16. The is 16= to 23. No. Is 16 less than 23? Yes. So the target must be on the right half. Move low up. Low equals to five. Step two. Low equals 5. High= 9. Middle index 5 + 9 / 2 7. The element is the end element at index 7 is 56. Is 56 equal to 23? No. Is 56 greater than 23? Yes. So the target must be on the left half. Move it. Move high down. High equals to six. Step three. Low equals to 5. High = to 6. Middle index 5 + 6 / 2 is equal to 5. The element is at index at index five. The element at index 5 is 23. Found. return index five three comparisons to find the target in a 10 element list. If you've done linear search 23 is the sixth element six comparisons the algorithm in cotlin here is the binary search algorithm written in cotlin and a trace of the steps on the right. The key logic is the while loop as long as low is less than or equal to high the there is still a range to search. Calculate mid as the average of low and high. Compart the element at mid to the target. Return the middle element if found. Move to low up if the element is too small. Move high down if the element is too large. When low passes high, we exhaust the search space without finding the target and we return minus one. Every iteration cuts the search range in half. That's where B log of N big O log of N comes from. Built-in functions in real code. You don't implement binary search by hand. Cotlin standard li provides it. Binary search also on sorted list and arrays. It returns the index of the target if you found. If not, it returns the negative number specifically the negative of the insertion point minus one. That in quad let you know that the the element wasn't found and exactly where it would be inserted to keep the list sorted. Binary search is the version of four custom objects. You pass a selector lambda that extracts the comparison key from each elements. The list must be sorted by the same key. Compare by combine with sorted with can can uh prepare your list before searching. Always remember the list must be sorted before you call binary search. Calling it on a unsorted list give you incorrect results with no error or warning. Binary search for custom objects. When you're working with a list of custom objects, binary search is the right tool. The list must be sorted by the same property you're searching for on. Okay. Again, binary search for custom objects. When you working with a list of custom objects, binary search is the right tool. The list must be sorted by the same property you're searching on. In this example, items are sorted by price. The binary search 0.99. It price finds the item with price 0.99. If you sort by price but search by name, you'll get wrong results. The selector in binary search must match the sort key. Implementing it yourself as an extension function. Understanding the implementation makes you a better developer even when you use the built-in. Here is here is a clean generic extension function. Use ash. So use ash hr one and sign right shift by one but equivalent to dividing by two but avoids integer overflow when low low plus high would exceed the int max value on very large list the return value minus low plus one when now when not found follows the same convention as Java encoding builtin it can cause the insertion point as a negative number the caller can decode it insertion point equals minus result plus one. This tells you not just that the value wasn't found, but exactly where it would go. Right, I'm going to end this video here and I'll see you in the next video. Back. Today we're covering comparable and comparator in Gotlin. These two interfaces are what makes sorting work on custom objects and understanding the difference between them gives you full control of how objects are ordered in any context. By the end of this video, you'll know how to use each one, how to chain multiple sort criteria, and how to write cleaner sorting code using cotlin's built-in helper function. So, let's go. The problem sorting custom objects. Sorting a list of integers or string is trivial. Cotlin knows how to compare them. The moment you introduce a custom class, Cotlin has no idea how to order those objects unless you tell it. Sort by name, by GPA, by students ID. Cotlin has no way to know unless you define the ordering. That's what comparable and comparator are for. Comparable. Comparable defines the natural word of a class. The natural order of a class. The default way objects are sorted when you call sorted or use comparison operators like less than sign or greater than sign. You implement it by making your class extend variable with a type parameter of itself. Then providing a compare to function. The compare to function must return a negative number if this is less than other, zero if they're equal, and a positive number if this is greater than other. The cleanest way to delegate to compare to on a property that already implements comparable like double into a string GPA compare to other GPA handles all the logic for you. Once student implements comparable students that sorted just works natural order by GPA ascending what compared to returns let's be precise about the return contract negative this order is less than order this object comes before the other and sorted output zero equal for ordering purposes positive this is greater than other this object comes after the other in practice you almost never write this logic by hand you delegate it on existing compare to the reverse to reverse the order just flip which side calls compare to other GPA compare to GPA returns a negative when the other has a has the smaller GPA which puts the larger GPA first descending comparator comparable gives you one natural order but if you need to sort students by name sometimes by GPA other times by GPA descending your leaderboard. That's where comparator comes in. A comparator defines a sort order independently of the class. You can create as many as you need. You use sorted with instead of sorted when passing a comparator. Compare by and compare by descending are cotling helpers that create comparators from a lambda selecting the property to sort by. They're shorter and cleaner than writing the full comparator A form by hand. Chaining comparators. One of the most powerful features of Cotlin's comparator utility is chaining. Sorting by a primary key, then secondary key and a territory key. The chain reads naturally left to right. Sort by department first. When the departments are equal, sort by salary descending. When salary is equal, sort by name ascending. This is the equivalent to SQL order by descending. Word by department ascending. Salary descending. Aim ascending express directly in cotling with no boiler plate. Then by as a secondary ascending sort then by descending as a descending. You can chain as many as you need. Comparable versus comparator. When to use each? Here is the decision framework. Use comparable when the class has one obvious universal natural order that will almost always be used for sorting. A date has a natural order. Earlier dates come first. a grade has a natural reward. The lower scores rank less. In those cases, implementing comparable on the class makes sense. Use comparator when you need to mult you need multiple sort orders for the same class. When you're sorting a class you don't own when you the sort order depends on context or when you're building a UI that let user switch sort criteria. The key inside a class has only one natural order via comparable. It can have unlimited custom orders via comparator. In practice, many cutling classes implement comparable for a sensible default and comparators are created on demand for everything else. Practical example. Here is a real world example. You're building a product listing page. Products should be sorted by rating, descending, the price ascending, then alphabetically by name. Widget A and widget B both have a 4.9 rating. Price breaks the tie. Widget A at 999.99 comes first. Gadget has a low rating, so it comes last regardless of price. Three sort criteria composed in three lines. No boiler plate. Comparable and comparator together. Comparable and comparator aren't much mutually mutually I can't pronounce that word. So comparable and comparator aren't mutually exclusive. Mutually exclusive. A class can implement comparable for a sensible default and still be sorted with a comparator when a different order is needed. Book implements comparable. So books that sort the words with no extra setup. When you need a different order, create a comparator and use sorted with same class three ordering zero conflicts. All right, I'm going to end this video here and I'll see you in the next video. back. Today we're covering access modifiers in cotlin. Access modifiers control who can see and use your classes, functions, and properties. Getting them right is one of the foundation of clean maintainable code. The less you expose, the harder is to misuse. So let's go through each modifier, see where it works, and learn the patterns that appear in real cotling code. The four modifiers. Cotlin has four access modifiers. Public, private, protected, and internal. Public is the default. If you write nothing, the declaration is public. It's visible everywhere from any file, any module, any class. Private seeks visibility to the smallest possible scope. On a class member, it means only inside that class. On a top level declaration, it means only inside that file. Protic is always private, but also visible to subasses. It only applies to class members. You can't use protected on a top level declaration. Internal is cotlin module level visibility. It means visible anywhere in the same module. The same grad sub project, the same intellig module, the same maven artifact. Nothing outside the module can see it. Private and internal at the top level. Access modifiers don't just apply to class members. They also apply to top level declarations, functions, properties, and classes declared directly in a file. At the top level, private means visible only within that file. This is a clean way to create file local helpers. Utilities does support the public API of a file without being part of it. Private fun helper print line helper private val secret hidden. Nothing outside this file can call helper or ask a secret. It's not accessible from test not from other module modules not even from other files in the same package. Internal at the top level means visible anywhere in the module. This is the right choice for implementing classes and functions that your module needs internally but you don't want to expose as part of your public API. So have internal class repository internal fun query list string empty list. If you're writing a library marking things internal is how you say this exist the module this exists the module uses uses it but it's not a contract you don't depend on public at the stop level is the default and means visible everywhere protected in inheritance protecting is specifically for inheritance scenarios protected member is visible inside the class that declares it and in any subclass but not from outside dog in this example can access sound because it's protected. It can access tag because they are in the same module and tag is internal. It cannot access DNA. Private means animal only. Notice that describe uses sound directly. It's in the same class where sound is declared. So protest is fine. But if you had a variable of type animal and try to call that sound on it from outside the class hierarchy that would fail. Proticing is for subasses not for colors. Getters and setters. One of the most useful cotlin specific pattern is applying different access modifiers to get and setter of a property. You have config host. You have uh in you have internal set private set. Host is publicly readable. Anyone can call config.host. But the setter is private. Only config itself can change host through update host. This enforces that the state changes go through a control method. Port is publicly readable but has no but has an internal setter. Other code in the same model can set port directly but external consumers cannot. This pattern public get etc is how you create a property that looks immutable to external callers but remains mutable internally. You can get the clean API of val from outside with the mutability you need you need on the inside. So I'm going to repeat that you get the clean API of a val from outside with the mutability you need on the inside backing properties. The most common real world access modifiers pattern in colin especially in Android core is the backing property pattern with a leading and underscore. We have user manager val users mutable list of user. So this is public mutable and we have write private backing plus public read only view have class user manager private val users val users list of user get and have underscore users. The public mable version is dangerous. Only color only color can add or remove items directly bypassing any loging you put in ad. The backing property version gives you external colors read only list view. They can iterate and read but they cannot mutate it. Only user manager can modify underscore users. You see this exact pattern with live data and state flow in Android. underscore state is a multiple state flow. State is the read only state flow. The underscore prefix is a cotling convention that signals this is the private backing property. All right, I'm going to end this video here and I'll see you in the next video. Today we're going deeper into cotling exceptions specifically the try catch and finally blocks. We looked at exception briefly before. Now we're going through the full syntax in detail. How multiple catch blocks works? what finally actual guarantees try as an expression and the utility functions the standard library gives you to handle errors without writing ro try catch at all. So let's go the full try catch. Finally, here is the complete syntax. As you can see in this diagram, we have three parts. Try holds the code that might throw if an exception is thrown. Execution jumps immediately to the matching catch block. So if an exception is thrown, the execution jumps immediately to the matching catch block. The remaining lines in the try block are skipped. Final Finally always runs no matter what happen in the try or catch. That last point is worth repeating. finally always runs. If the try block succeeds, finally runs. If an exception is caught, finally runs after catch. If catch retros, the exception, finally still runs before the ex before the exception propagates up. If there is a return statement inside try, finally runs before the function returns. This is what makes finally the right place for cleanup code. like closing a file, releasing a database connection, stopping a loading spinner. You put it in finally and you are guaranteed to execute. So you guarantee that it's executed. Catch and finally are both optional, but you must have at least one of them. A try block alone is a compile error. Multiple catch blocks. You can have as many catch blocks as you need. Cotlin checks them in order from top to bottom and executes the first one that matches. The golden rule put the most specific exception types first then the most general last. Why? Because number format exception is a subclass of illegal argument exception which is a subclass of runtime exception which is subclass of exception. If you put catch e colon exception first it matches every exception and the more specific blocks below it below it are dead code. The compiler will actually warn you about this. The last catch with the exception acts as a fallback. It catches it catches anything that wasn't handled by the blocks above it. This is useful when you want specific handle for known exception that but a safe default for anything is an expert. You can also catch multiple exception types in a single block using the pipe operator. This keeps the code compact when two exception types share the same handling logic. Finally the cleanup guarantee. Let me show you a real world example that illustrates exactly what finally is for. So we have final open file val reader and have buffer reader file reader path. We have return try and have reader catch and finally have and finally reader.close. Reader.close is in finally. Here is why that matters. If read text throws an exception, an IO exception, we catch it and return an error message. But before we return, finally runs and closes the reader. If read text succeeds and returns normally, finally still runs and closes the reader. In both cases, the file handle is released the file handle is released. No leak, no resource left dangling. This is the most purpose of finally guarantee clean up regardless of outcome. In modern cotlin you'll often use the autoc closable extension which wraps the pattern automatically. But understanding finally is what makes use parenthesis use function makes sense and there are plenty of situation where use doesn't apply. Try it as an expression. Here is one one of course the most useful features. Try is an expression. It has a value. So it returns a value. The value of a try expression is the last expression evaluated. If the try block succeeds, the last expression is the value. If an exception is called, the last expression in the matching catch block is value is the value. This replace a pattern that require null variables and two separate assignments in other languages. In cot, you declare the variable and assign it in one clean expression. Note that finally does not contribute to the value. You cannot use finally to provide a return value. Only try and catch blocks determine what the expression evaluates to. This also means that try expressions work naturally with when with with the Elvis operator and anywhere else cotlin expects an expression. Coten utilities require check and run catching. Cotlin standard library gives you three functions that reduce exception ber significantly. You've seen those briefly. Let's see how they interact with try catch. Require if is first validates functions arguments. This is what it does. throws illegal argument exception if the condition is false. Next we have check validates object state throws illegal state exception if the condition is false. Run catching wraps a block and returns the result no try try catch syntax at all. Run so run catching is particularly useful when you want to pass a result around or chain operations. The result type has get or default, get or false, map, fold, and more. It separates the point where you attempt the operation from the point where you decide what to do with the outcome. All right, I'm going to end this video here and I'll see you in the next video. Come back. Today, we're covering two features that sit at the heart of ideomatic cotling, lambda functions and higher order functions. Once you understand this, you'll start reading cotling the way it's meant to be read and you'll write less code to do more work. So, let's get into it. What is a lambda? A lambda is simply a function that has no name. You write it in line, assign it to a variable or pass it directly to another function. Here is the most basic lambda function. You have val grid curly braces print line hello and type grid. The curly braces are the lambda. You call it like any function grid parenthesis. Lambdas can take parameters and return values. The part before the arrow is the parameter list. The part after is the body. In cotlin the last expression in lambda is automatically its return value. No return keyword needed. The type of this lambda is int have parenthesis int int and arrow int that needs that reads as a function that takes. So that reads as a function that takes two and returns an int. Coslin type system treats functions as first class citizens. They have types. They can be stored in variables and they can be passed around just like any other value. Lambda syntax it is the it and the structuring. When a lambda has exactly one parameter and let you skip declaration it and use implicit name it val double m and have curly braces m colon int arrow n times two. Inside the map lambda it refers to each element of the list. This is one of the most common patterns you'll see in cotlin. If you don't use a parameter, use an underscore to signal that intentionally. So you have pairs pairs for each and you have num word and you have arrow print line nom and word. The parenthesis in num word is the structure in cotlin and unpacks the pair into two separate variables right in the parameter list. Higher order functions. A high order function is a function that either accepts a function as a parameter, returns a function or both. You already use them every time you call map filter or for each. Let me show you how to write your own. So you have operate numbers a b operation parenthesis in int arrow int. Operation is a parameter whose type is int. Returns an int. So it returns an int. The caller passes a lambda. Inside the functions, you call operation just like any other function. Operation A and B. Notice the lambda goes outside the parenthesis. That's the trailing lambda syntax. When the last parameter is a function, cotton lets you move the move it outside the parenthesis and into a block. This is what makes code like list. Curly braces is greater than five look so clean. function references. You don't always have to write a lambda. If a function already exists that matches the expected signature, you can refer to it with a double colon syntax. Have fun, it's even. Have boolean percent valil, it's even it. So, we have the same thing. Both lines do the same thing. colon colon is even is a function reference. It says use this existing function as the value to pass here. For short functions with clear names, function references often read better than lambdas. You can also reference member functions and constructors. String col uppercase is a reference to the uppercase method of string. String colon length is a reference to the length property. Both are used as functions because that's what they are. returning functions. High order functions can also return functions. This is the basis for patterns like function factories. We can have multiplier factory int and it's going to return a parenthesis int arrow int. Multiplier takes an int and returns a function specifically a function of type int. The return lambda captures the captures factor from the outer scope. That's a closure. The lambda remembers the value of actor from when it was created. Triple is now a function that multiplies by three. Quad quadruple multiplies by four. You created two specialized functions from one general purpose factory. This is a powerful pattern for building configurable behavior. Standard library map filter reduce fold. Kotlin Kotlin's standard library is built on higher order functions. Understanding them is essential for writing ideomatic cotlin. Map transform each element element and produces a new list. Filter keeps elements that satisfy condition. Reduce combines all elements into a single value sorting them from the first. Fold is like reduce but let you specify the starting value and the result type independently. You can chain those together. We have list of filter map fold. Each step in the chain takes a lambda and produces a new collection or a final result. No loops, no temporary variables, no mutation, just clear pipeline of operations. Wrap it up. Lambda functions are anonymous functions written with curly braces. The the short hand is available when a lambda has exactly one parameter. Higher order functions take function as parameter will return functions. Writing your own gives you powerful usable abstractions. Trailing lambda syntax when last parameter is a function moves outside the parenthesis. This is what makes filter map for each read so naturally. Functions reference. Use column function name to pass an existing function without writing a lambda. Use class colon member for a member for member functions and properties. Returning functions. A functions can return a lambda. The return lambda captures variable from the outer scope. That's a closure. The standard library map filter reduce fold for each flat map group by and many more are all higher higher order functions. Learning them replaces entire categories of imperative groups. So I'm going to see you in the next video. What are cor routines? Firstly, in order to understand cor routines, you need to understand how your application works when the user launches the application. So by default when you launch an application a default thread is created the main thread and this is the thread on which all the operations are performed and the operations are performed in a linear fashion line by line. What that means is that the next line of code has to wait for the previous line of code to finish execution and those operations are small operations like UI interaction, mathematical operations, small logical operations. So on the main thread we perform small operations. Now what about the long and heavy operations such as the ones which require the use of internet like upload or download a file, loading or displaying an image or running some heavy database queries. Well, if you run this heavy operations because the code is executed in a linear fashion, the next line of code will have to wait until this heavy operation is done and that will block the main thread. So the main thread will be not be able to do anything else while it's performing that heavy operation and this will appear to the user like the application has freeze and if the main thread remains blocked for a longer time the app will crash. Therefore running those heavy operations on the main thread is not recommended. To solve this situation we have a solution. Instead of using the main thread to perform those long operations, we can create background threads to perform those long operations. We can create a thread to perform network operations. We can create another background thread to download the file and we can create a background thread to perform a to perform database queries. By doing this now the main thread will become free for the other small operations. Those background threads seems to be like a solution, but there is a limit of how many background threads you can create because the device will run out of memory if you create too many and the application will crash again. Therefore, creating many background threads is not a solution. So, what is the next solution? And here comes into action cor routines. So when we use cor routines, we don't have to create so many background threads for each operation. Instead, we can just have one background thread on which we can launch a C routine. So you can launch a C routine to perform the long heavy operations like uploading a file. You can launch another C routine on the same background thread to do some network operations. And similarly, you can launch another C routine to download the file. And we can have more cor routines to perform other long and heavy operations. With just one thread, we can perform so many heavy operations. So this is what corines are. They are like threads in the sense that they can run in parallel. They can wait for each other and communicate with each other. But but they work within a thread. They are not threads. and you can create thousands of of cor routines without any memory problem. Now how to use cor routines? To use cor routines you need to first create a cotlin project. So I'm going to open intellig click on create new project I'm going to call it cotling corines for the language select cotlin and for the build system now select gradal and after I selected gradel this option down here gradal dl appeared from here select katalin and then click on create Now our project is going to be created and uh also you should make sure that you have internet connection in order to be able to have the project created. Once the project is created just go on the left hand side on the project pane and doubleclick on build.gradal gradal because here we're going to integrate our corins and we go inside here where it says dependencies and here we're going to integrate our corins and here we need to paste this statement this one. So I'm going to copy this and this is going to integrate corines in our project and I'm going to paste it here. Now this on the top this icon now appears here. So if you click on if you hover over it says lot gradal changes and this is going to integrate uh the core routines and it's going to apply the changes. So, I'm going to click on this U icon. Just wait a little bit. All right. Now, our project is ready. And now we can use Corins. But first, I'm going to create file. So, click on src, click on main, right click on cotling, go to new, and select cin class file, select file, and I'm going to call it main and press enter. And now we have our file. And now I'm going to start a discussion about corines. Now I'm going to close the build.grade. file because we don't need this file. And also I'm going to hide the project pane. And I'm going to add the main function. So I'm going to press m press enter. And by default the main function and the code within the main function is executed on the main thread. So let's add a comment here. Executed on the main thread. So let's add a print line here which is going to say u main program starts let's put colon dollar sign and we're going to type thread current thread. So this is going to give us the current thread on which this statement is executed dot name. So it's going to give us then the name of the current thread on which this code is executed. And now we're going to pretend that we're going to do some u work in in the background. But we're going to do this work also on the main thread. So I'm going to instead of because we we don't do actually that work, we're just going to pretend. So we're going to type thread. We're going to put the thread to sleep to for a few seconds. So going to type thread. 2000. So this I'm going to add a comment here pretending to do some pretending here to do to do some work. So here going to pretend that we're doing doing some work in the background and then I'm going to add here a print line which is going to say main program ends also colon dollar sign and thread current thread name. So all the code that we have here this code is going to be executed on the main thread. And because here we we type thread sleep for 2,000 milliseconds. So we're pretending like that we're doing some work for 2,000 millconds. This now when this line of code going to be executed, the thread is going to pause and then it's going to execute the next line of code. So if you run this code now, we get main program starts and we get main. Then we have that delay of 2,000 milliseconds for 2 seconds. Then we get main program end. And uh we did this on the main thread. So as you can see the this thread quarantine.name it's returning here main because the work is done on the main thread. But as you can see this is not uh the way we will do things because this blocks the main thread for 2,000 millisecond. And imagine if you have here a function which performs a heavy operation. It will it will block the thread like I explained in the introduction video. What we can do is creating a background thread and we're going to put that thread asleep. So that uh work that we pretending to do into that thread. So I'm going to type here thread. So we're going to type thread lambda expression. Press enter. And we're going to put this code. Now I'm going to put this code inside here. But I'm going to add a new two print lines. So I'm going to add here print line and I'm going to type fake work starts colon dollar sign thread current thread name. So that is going to give us the thread on which this uh back. So this is going to give us the thread on which this background thread is on which this code is executed. And uh I'm going to add also print line here. After we finish the work, we type fake work finished also colon. I'm going to type thread current thread that name. So what we're doing now is we we u creating a background thread and now we we're executing that uh work that that we imagine on this background thread so that we don't block the main thread the execution on the main thread and this code now is going to be executed without waiting for that work to be finished because now that work is done on the background thread. So now if I run this as you can see we get main program starts main program ends. So this line is of code is executed then this background thread is launch and uh then the execution of the of the code continues. So this code now doesn't block the main thread and it says main program ends and then we get fake work starts and we get thread zero. So this is the thread on which this background work is executed and then we get so get fake work starts. I get the name of the thread from this that we have here thread.0 and then then we get fake work finish and the code on the main thread and the code on the background thread it's executed in parallel. So in other words is executed concurrently and that means that the code is executed in parallel. So the the code on on the main thread is not waiting for the code on the background thread to finish. No, it just it's just u executes this line of code. It sees here that there is a background thread and then it goes to the next line of code and it's executed this line of code. So it's not waiting for the background. It's not waiting not waiting to the for this to finish in order to be able to execute this line of code. So this why we get here main program start main program ends main then we get fake work start our background thread and then we get fake work finish and now if you run this again you'll see the same uh output so get main program starts main program end so those two are executing in parallel with those two that we have here and this is because we're using a background thread now let's see how we can achieve the same thing using a Cor routine. So instead of thread I'm going to delete here what we have here and I'm going to type here core routine scope. Now but before we create our first cor routine I want you to notice something which is very important. So I'm going to change this back to thread. Now I'm going to run this code again and we get main program starts main program end. So we get main main then we get fake work start thread fake work finish that. So we get our code executed in parallel concurrently and what I want you to notice is that even though this last statement was executed in our main function our program didn't end there the program waited for the for the background to finish the work and then we get process finish with exit code zero. So then the application finishes. So by default the application will wait for the background thread or for the background threads to finish their work and only after that it will uh it will finish the application it will terminate. So let's now change this back to corin scope. So we type here corin scope. I'm going to explain immediately what is a corin scope. And now the coroutin scope is expecting here as a parameter a coroutin context. But first let's u talk about what is a coroutin scope. So a corin scope is usually used when you want to group a bunch of cor routines together. So if you want to have a bunch of cor routines to be grouped together because uh maybe one one cor routines depends on another cor routines on another cor routine and uh if maybe one of the coroutine fails you can cancel all of them. You can do that because if if you use them if they are in the same corin scope. So this is what corin scope is is used for. It's used to to group a bunch of corines together. So you can do you can have flexibility. You can do you can do you can do things if some something happens with one of the corines. Now this cor routine context here here you need to pass here need to pass uh on what here we need to pass first what kind of work we're going to do. So this is what we're doing with the corine constant. We're saying to the corin scope what kind of work we're going to do and on which thread we want to do that work. So we have a few option first is the main. So if I type main now let's press alt enter and we have this main.cot routines main means that this corines scope is going to is going to work on the main thread. But this main that we have here only works on u on an Android program. But now we have only intelligj. So we cannot use main. The next one is IO. And IO means input output. And this is used for um getting data from the internet or curing quering a database. So this is when you should use uh IO. And um you also have default. Default means doing heavy work. So if I put here default so you see that you have dispatchers.default you can press enter and basically what this means is that this cor routine scope is going to do some heavy work and it's going to use um it's going to is it depends on the corin what kind of thread it's going to choose from the thread pool. Next, you still have this error here because we need to launch the corine. We need to create the corin. And to do that, you put dot launch. And if you feel confused by what I talked about here about the default and main, don't worry because uh in the next videos I will go into much detail about them. So, we put that launch and now this is this creates the C routine. As you can see, it's a this corin scope. Now, if you run this code, you're going to see something uh interesting. We get main program start and then we get main the name of the thread. Then main get main program end and we get the name of the thread. But we don't get u our fake work starts and our fair fake or finish. Why this is because by default the program does not wait for the corin to finish the work is in comparison with the thread. As I said for the thread the application waits to finish it its work but for the cor routine it's not doing that. So what we can do what what we can do is that we can type here threads sleep and we can put here to to five to five milliseconds. And now this will make the thread to sleep for two and a half seconds. And because this only takes 2 seconds we'll get our output in the console. So if I run this now but this is not recommended to do in a real lab. So you get default dispatcher worker one and get main program ends. So now our output works our program work. So you get main program starts then we get our fake work. So you get our work on the which is on the corin and then we get main program ends and then the process. So now it's waiting because we said here explicitly to pause the thread to for 2 and 1/2 seconds and because this works takes only 2 seconds then it will have enough time to to finish this but as I said by default it's not waiting to for the C routine to finish but if you think about this is not this is not a solution because if instead of thread sleep because here you pretended to do some work you have a function which is getting some data from the internet you will have no idea how much time that it will take. So what we can do in that case in that case you will need to use something else and u I'm going to show you what what you can use instead of instead of thread that sleep here to make to wait for the cor routine uh in a in a short time. So now let's think about this thread sleep that we have here. So if you think about our discussion when when we discussed in our introduction about threads and corins and how they work. If you have a bunch of corins inside this corin scin scope that we created because thread does because we have here thread does sleep this will make the entire thread to pause for two seconds and if you have three or five corins all the corins will be stopped because the thread will be paused. So you need to use something else instead. So to use something else and to use uh to to not block the entire thread so we the other C routines can do their work. We're going to use delay and delay is used is used because it will only pause that specific C routine. It's not going to pause the entire thread. So the the other corines can do their work uh uh without being uh being being stopped because the thread was paused. So if I if you use delay, delay means just pause this specific corin not pause the entire thread. Let the other corines to do their job within the thread and just pause this corine. So this is what means. So if I run this now we get main program starts main then we get fake core starts then we get our pretending work which is this which delays the delays the cor routine for 2 seconds and then get main program ends now as I said in a real app you'll not know how much time it will take for the cor routine to finish its work. So you cannot uh use thread slip and pass uh a specific number here because you'll not know how much time it will take to do the work. So what we can do instead is we can delete this. So if you go up here and we press and hold control over the launch and if you click it's going to take us here and we're going to see that this is an extension function and it returns a job object. And what is this job object? This job object basically allows us to have some control over over a specific C routine. So if I type here val, I'm going to call this variable parent job because this is where uh this is the parent corine if you want because as you'll see later I'm going to add some child corins inside our corin scope that we created. Now what we can do down here because u the let's type parent here because this uh job is uh offering us some control over the corine we can instead of uh uh typing threadless sleep and waiting for a for a specific time. We can say we can say to the C we can say to the compiler to wait for this for the cor routine to finish its work and then finish the the program. So I can type it parent job and to make to make the compiler to to wait for the corin to finish we type parent job join. And this basically is going to is going to say to the compiler, hey, wait for this C routine that was defined here, wait to finish uh its work and only after that uh finish finish the the application or the program. But we have a problem here. And if you hover over here, it says suspend function join should be called only from a C routine or another suspending function. Anyway, this is also a suspend function. As you can see if I hold control on it. Now what are suspending functions? Suspending functions are functions which are marked are marked with the suspend modifier and they they they are not core routines but they can only be called from a core routine. You can never call a suspend function uh from u not from a core routine. So as you can see this delay is a suspend function has the suspend keyword there and uh now what we can do here to to to because here we're not inside the cor routine. So we cannot call join because you can only call suspending function from a cor routine. Here we can use what is called run blocking and run blocking creates a cor routine and u we can put now this code here and paste it here. I typed run catching not run blocking. So let's change it to run blocking. And now the arrow disappeared because now this creates a c routine. This run blocking. The problem with run blocking is that the run blocking also block blocks the thread. But this uh this is the only way in which I can present uh the code. So in a real you probably going to just type parent job join and that is going to work. But um here we need to you need to call this from within a core routine because uh this is a suspended function and a suspending function can only be called from a corin. Now if I run this code look what happens because now we say to the compiler we say to the program hey wait for this to finish. Now we get main program starts main fake word starts and we got we get our u our thread on which the core routine is operating and then we get also our thread on the code is operating one also another important thing to know is that even though the the corine is uh operating let's say on this thread when it starts it may happen that when uh the corine ends so when we executed this line of code to to end to to to do the work on another thread. So this is also important uh to know and now if I change this to let's say uh 5 seconds and if I run now because here said parent job join so wait for this C routine to finish it job it's going to wait 5 seconds then it's going to say fake or finish then set main program ends and get main. Now I'm going to close the console and uh I'm going to continue our discussion in the next video. Now it's time to start a discussion about corin builders and also in this video I'm going to show you how to create your own suspending functions. So what are coroutin builders? As the name implies, coroutine builders are some functions with which you can create coroutines and there are three coroutine builders that you will going to use and those are launch the one that we have here the async and the run blocking that we have here and we're going to look first at launch and but first I'm going to create a few suspending functions. Now to create a suspend function just type uh I'm going to make this function private suspend fun and I'm going to call it get data one and this is going to return a result. I'm going to going to mimic the behavior of getting the data from the internet. So I'm going to add a print line here. Actually let's define an argument here. Call it uh let's call it thread name. We're going to be of type string and we're going to add here print line and I'm going to type fake work one starts dollar sign and the thread name. So it should be thread name here. All right. Then I'm going to delay this uh spending function. So we're going to delay because this is going to be called from a corin. I'm going to delay the cor routine for two seconds. Then I'm going to add another print line which is going to say that the fake work one finished and the thread name and I'm going to create another function but first let's return something because we need to return I'm going to return the string result one. Right now I'm going to create another function. So I'm going to copy this but this is going to be called get data 2. So I'm going to paste here. I'm going to call it get data 2 and it's going to be be called fake work two. Fake work two and it's going to return result two. Now let's delete the code that we have inside our corin scope. So let's delete the code that we have inside our corin scope. And uh first we're going to look at the launch corin builder. And to use the launch corin builder you just type launch and then you have the lambda expression and and here we're going to use our code and I'm going to call our get data one function and here I'm going to type thread current thread name. So because this function expects an argument a string and going to pass the current thread on each this uh this uh corine uh is working and going to put here val result one because this returns a string which is called result one and I'm going to add here print line result one now going to create another uh cor routine so with also the corin builder line. So I'm going to type launch and I have a suggestion. Press enter and we're going to type here val result 2 equals get data 2. And here we're going to type again thread that current thread name and going to output in the the console read the value which is returned by the result two. Let's press ctr alt format the code and let's run our code to see what we have in the output. So this is how you can create your own suspending function. This is how you can use them inside your cor routines because this corin builder launch creates coroutines. So we have two corins here and as you you'll see they are running in parallel. So as you can see they start both at the same time and they also end at the same time. So we have so we have on the output main program starts main then have fake work one starts and we have default dispatcher dispatcher worker two then we get fake work two starts. So our second uh function is called and we have a different name for the thread. So we have fake we have default dispatcher work worker three after that uh it says fake uh work one finished and we have the name of the thread then and then we have result one this value here printed after that we get fake to finish and then we have the name of the thread also from here and then we have uh result two which is returned from our second uh suspend function get data 2 and then we get main program ends and then we get process finish with x equals z. So our application finished there and what we have here is first we have a parent corine which is this coroutine that we define here. So this one and inside this parent corine we have two child corins this one and this one. And those cor routines are nonblocking in nature. And what that means is that they don't they they launch a new cor routine which is not blocking the current thread in contrast with the run blocking builder corine. This is block blocking the the main thread but those corins which the corines created with the launch corin builder they create a coroutine which is nonb blocking which is not blocking the current thread. And if you press control here because uh we already looked at this. This returns a job object. And with that job object we can have control over the C routine. So I'm going to type here val and I'm going to call it job one. I'm going to put equals because that function returns a job object. Then val job two. I'm going to put equals launch. So now if you hover over here you see that this is of type job. This is because the launch corin builder is returning a job a job object. And now we have we can have control over the corines. We can also you can uh as we saw with the parent the parent uh job corine we can uh join then we can wait for the corine but you can also cancel the corine. So if you want to have those cor routines that we have here uh the job one and job two to run in sequence and not in parallel you can just type here job one join and this now will will wait for the first C routine to finish execution and then and after that it's going to execute the second cor routine that we created here. So if you run this look at the output we get fake work one starts then we get fake work one finish and get the result returned. So you get the first one the first c routine and then it executes the next c routine. So they are executed in sequence and um to show you this more clearly I'm going to create a variable here I'm going to call it val start time and I'm going to get the current time in milliseconds. So I'm going to type system current time in milliseconds. And that is going to give us the the current time in milliseconds. And u at the end here I'm going to type print line. I'm going to type total time colon dollar sign. I'm going to type system current time in millisecond minus start time. And that is going to give us the total time that it took to to finish the the to finish for the parent job to finish the work which was which is which is done by the corines ch. So now if you run this get fake war one starts. So you get them in sequence, you get a result on and we get the total time is uh 4 seconds and a few millconds there. This is because they are uh they are running in sequence and not in parallel. But if I delete the job one join so not saying to wait for the first coordinate to finish now we're we're by default they are going to be started at the same time. And now look at the time that is going to be show here. So now they both start at the same time. We get um fake work fake work one uh fake work one starts fake to start and we get for one finish get result one. Then we get fake work to finish and we get the name of thread and then look at the time we get only two seconds this time. Even though the total time for both uh both C routines is 4 seconds, we get 2 seconds because they are started at the same time by default. So if because I didn't put job on join, they are started and executed at the same time. So this why get only two seconds here. Even though inside the suspending function, we have two seconds for the first one and two seconds for the second one. because they are started at the same at the same time you only have 2 seconds. So this is the launch corin builder. Next we're going to look at the async corin builder and the asin corin builder is very similar with the launch corin builder. The only difference is what the the the asin corin builder is returning because with the launch corin builder the the value which is returned by this function get data one and by the function get data two is stuck inside the core routine. So we cannot get that data outside of the cor routines. But with the async corins as you will see we can get that data outside. So we're going to look at that now. So now let's look at the async corin builder and for that I'm going to hide the console and uh we just change the launch to async. So if I type async we have a suggestion and we fill that and we do the same thing here also async. And now if you run this you'll not see a difference uh in the output but I'm going to show you how with the async we can get the data we can get data outside of the c routine. So we get uh the same uh output no big difference. So they are very similar. I'm going to hide the the console. But what we can do now instead of adding the print lines inside the corine and getting this data inside the cor routine what we can do with a sync is that we can return this value. So the last line of code which is which is uh inside the async code routine is going to be returned back here. So here so let's let's change this. Let's press ctrl alt l now because this get data one function is returning a string that string is going to be returning this variable job one and you can see this is now of type defer string. So let's put that let's call it job deffort and it's going to be al type deffort and this is this can be a generic type and I'm going to type string here and we're going to do the same thing for this job deferred and it's going to be of type job deferred two and it's going to be of type uh referred the generic string. All right, let's call this actually job defer one. And now the value which is uh which is which is which is going to be returned by the get data one. Now it's going to be stored in this job defer one. So if I hover over here, you can see that this is of type job deferred. It's of type deferred string. The same here. Now how we can use that data? To use that data we can now outside the core routines we can add a print line here and we can use what is called the await function. So we can type here job deferred one dot await and that is going to give us the the value which is returned by the get data one function and I'm going to add a plus here. I'm going to put quotation marks forward backless n to add a space. I'm going to put dollar sign to get the data for the job defer to two. So I type job defer two dot await and that if you hover over this or if you press uh uh what this is going to do is going to return us the string. Uh this is going to return result one. This is going to turn result two. So now if you run this look uh at what you get in the output. So they they also start at the same time they finish at the same time as you can see in the output. So we have this fake or one starts fake one fake two starts fake one work fake work one finished and fake work two finish. Then we get result one and result two. And this the result one and result two is from this print line that we have here. And this is uh uh given to us by the job defer await and that is going to give us the value which is returned. Then we get the total time which is almost uh have you have two seconds and a few millconds there and then we get the main program ends and you have process finish with x code zero. So this is how you can with the async you can get the data outside of the corin and the data is the last line of code. So if I type here 12 now uh now we have an error because this is we define here deferred of type string and uh we're returning an integer. So it says type a mismatch required deferred uh should be required it's correct require string and it found int. So if you change this to int there is no problem. So the last line of code inside your uh your C routine we create with the C routine builder async is going to be the value which is going to be returned back to the to the variable here and it's going to be the value which is going to be here. So let's change it back to a string. Let's delete this. And now we have no problem. So this is how you can use the launch corin builder and this is how you can use the async corin builder and uh next we're going to look at what is global scope and why the using the global scope this is discouraged and uh with job deffort we can also use the join and cancel function at cancel function we didn't look yet but we're going to look in our next videos because this defer that we have here is uh inheriting from the job class. So you uh because it's inheriting from the job class, you can use the join and um cancel function. So you can type here job deferred one dot and have join cancel and uh so you can use those if you want and there is another interesting uh function that we can use uh using uh the the job object and that is the invoke on completion. So what we can do down here is you can use our parent job and put that and type invoke on completion and this function is going to be called when the parent job finishes the work. So when the all the child cines are finish are finishing their job and the parent job also finish finish by that finishes the job. This function is going to be invol invoked and um as you can see uh you have a lambda function we have it throwable so we can type here it question mark let so if u there is a throwable so if there is an exception I'm going to say print line and u going to say uh parent job failed and we're going to put colon dollar sign and it it message because going to get the message the error message otherwise we're going to put the Elvis operator so I'm going to put question marks question mark and colon and if there is not if uh the throwable is null we don't have any trouble that means that the parent has succeeded so we type here parent job success with two yes here. Now if you run this code look what happens. We get our two corines starting and finishing at the same time. Get result one and result two. And uh we also get parent job success because our because all of our child corines uh finish finish their work. The parent job also because of that finished execution and it finished with success because we didn't have any error inside in honor of our child. We're going to look at how we can have errors in the child and what you can do with in the parent if you have a error and uh so on. But for now just know that this invoke completion is called when the parent job completes the is completing the work. So when when when all the child corines are finishing the work the parent job also finishes and we get result on result on and you get parent job success because there is no error. So this is also an interesting function to to know about and u next we're going to look at the global scope and why the global scope is discouraged to use. Now what is global scope? First let's say that you launch an application and you are currently let's say in the login screen and within this login screen uh you launch a quinine let's call it coroutine one and then you navigate to another screen called home screen and uh within this home screen you launch another coroutin called let's say corine 2. Now if you navigate now back from the home screen to the login screen the corin 2 is going to be destroyed when the screen two is going to be destroyed. And this this happens because the corines the coroutines in the way that we launch them here they exist only locally. So they exist bound to the activity or the screen where they're launched. So when the screen gets destroyed the the the local corine also gets destroyed. Now if you launch a coroutin globally no matter from what screen you launch that coroutine it will survive the entire life lifespan of the application. So even all even though if all the screens are destroyed the cor the global corine will still work in the background. So this is the difference between uh launching a CI locally. So if you launch it locally, it's it bounded to the screen from which is launched and if launch it globally is not bounded to the screen from which it's launched. It can run in the background the entire life lifespan of the application. Now the question that may arise is when to use uh when to launch a corine globally. So when to use global scope and when to use local scope and the answer to the question is that it depends. And let's say that you click on a button to download the file. And if you want to download the file, you should download the the file in a C routine which is started globally because you you want to start downloading the file and then to you want to move on with the ongoing activities or with with of that specific app. So that uh download that downloading the downloading the file should not interfere with with the ongoing activities that you are currently doing in that app. Or another example is playing some music. You just start the music and you want to have that music playing in the background and it should not interfere with uh again with what you're doing in that app. And in those cases you should use global scope. You should use local local scope when you want to do a thing and you want to end that in there. Let's say that you want to login a user. In that case, you log in the user and then you move on to to let's say to the next uh screen and you end end that code in there. In that case, you should use local scope. So this is when and I'm going to show you how to use global scope. We just u delete this here and instead of corine scope we type global global scope.lunch launch and this is how you start a global quarantine globally and uh let's press Ctrl Z to undo this and you can also do the same thing here so instead of asking here we can type global scope so we can type here global scope launch but now we have an error because this uh here we said to that this type returns something and the global scope is not returning something so let's press Ctrl Z so This is how you will start coring globally and I'm going to end the video now and the in the next video we're going to look at cancellations, exceptions and timeouts. So we'll see you in the next video. So now it's time to start a discussion about cancellations, timeouts and exceptions and we're going to look at the first one and that is cancellations. And the question that may arise now is why we want to cancel a cor routine in the first place. And the answer to that question is that you want to cancel a cor routine. Let's say when it's taking too much time to do some specific work. In that case, you'll need a mechanism to cancel that corine. Secondly, let's say it may happen that you no longer need that result that the corine returns. In that case, you also need a mechanism to stop to cancel that corine. And uh another very important thing is that in order to be able to cancel a cor routine that corine has to be cooperative and that means that the functions which are used inside the corine has to be part of this package cotlin x.c Corins and if the functions which are using that inside that corine are not part of this package not you're not going to be able to cancel the corine. Now I'm going to change this now a little bit to to a launch and not to async. So let's type launch here. Let's change this to job one. also here and we're going to output the result that this function returns in the console. So I'm going to add the directly this in the print line. So I'm going to add this directly in the print line. also here. Let's change this to get data too. And let's comment this line of code because don't need this. code anymore and let's run our code the way it is now to see what we get in the output. So we get in the output fake work one starts fake two work starts then get fake work one finish we get result one then we get fake two fake work two finish and get result two parent job success total time 2 seconds so uh our output works well now let's say that I want to cancel the first c routine which is returning the variable job one so I can type here job one dot and to cancel a corin type cancel. Now I'm also going to type job one join because if the corin is not this let's say that the code is not able to cancel the corin then I'm going to wait for the corin to finish and those functions are so used in conjunction that there is a um there is a function which combines those two but I'm going to show you this after I run this code. So let's run this code now. So now we cancel the cor routine one immediately after we launch it. So let's see what happens. We get fake one work work one starts fake work two start and get now fake work two finished and we don't get fake work one finish because it was cancelled and very interesting we get result two but we don't get result one because our corine was cancelled and we get parent job success and this is also very interesting to see how the cancellation it is propagating to the next corine respective the corine 2 and how it propagates to the parent corine So the parent even though we cancel this score routine the parent is uh is is not throwing an error. It says that is success and it took also two two seconds. Now as I said let's change now this delay function and this delay function is part of that package that I talked about and I'm going to change it to thread. So let's see if now I can cancel this corine because that thread. is not part of this package. So in theory theory I should not be able to cancel the corine because that that code that the code that we have here is not part of that package. So our corin is not cooperative and we can't cancel the corine. So if you run our code now as you can see get fake one starts fake one finished result one. So if again get fake two start. So we get our fake one. So our first cor routine was not cancelled because it's no not cooperative and that means that uh the functions which are used inside that corin are not part of this package and thread. It's not part of that package but delay is part of that package. So if I change this back to delay and if I run this code get fake work one starts fake work to start and get fake work to finish and fake work one you don't get that it finish and you get only the result two and get parent job success. So now our corin is cooperative and that means that it's cancelellable and you can also combine those functions in one function because the join function is going to work only if the cancellation fails. So we can combine this in one function called job one cancel and join and this will give us the same output. So if you run this code we get fake warp one starts fake warp two starts and we get only the result two because the fake the first cor routine was cancelled and we don't get any result back and it the cancellation didn't propagate to the parent. So it didn't cause the parent to also fail and also didn't didn't cause the second corin to fail. The second corinu it work independently. It didn't it was it wasn't affected by the fact that this was cancelled. Now there is also a boolean flag called it's active which is going to return true if the corine is active and it's going to return false if the corin has been cancelled. So if the corin is not active and to access that boolean flank uh you can type here if I'm going to put not. So if it's not active then return. So return to the launch that mean that means that it's going to break this uh code here. So if you run this code we get fake work on starts fake to work to start and then you get result of par of success and so our code works well and this flag even though it's not showing very clear here what is doing but if you have some work you can say to you can you can put a if check and if that is not active you can break that work which the corin is doing. This is very very powerful to have this internal boolean flag to check whatever whether the corine is active or not. Next we're going to look at how to handle exceptions because when you cancel a cor routine what is actually happening is that the the function respectively the delay function is going to return is going to throw an exception. And to show you that I'm going to wrap our u our code in try catch and I'm going to catch here cancellation exceptions. I'm going to put calibration. I'm going to put here a print line. I'm going to type exception cut safely. I'm going to also add a finally block and I'm going to add a print line here which is going to say which is going to say resources closed safely. So let's put our code which is going to throw the exception. Let's press Ctrl Alt L. And let's put here U dollar sign X dot message. Now if you run this code we get exception cast safely standalone quarantine was cancelled and get resource resources closed closely closed safely and uh so we can uh wrap your code in a try and catch to catch the exception. But what if you uh cancel the C routine too and as you'll see if you throw your own exceptions what you going to do if you have job one job three job four job five what's going to do in that case you cannot wrap actually you can wrap all of all of that corines in a try cache but the code is going to be very messy and for that we have uh something else with about which I'm going to talk a little bit later for now you just need to know that the cancellation the cancel when you cancel a cor routine that's going to throw an exception. You can catch that an exception and try catch as you see as you saw in the output here. So right before we move to the next section let me show you how you can uh use your own cancellation message. So I can uh put here cancel and as you can see this expects as an argument cancellation exception and let's put here job one join. So here I can type cancellation exception and I can I put I can put a message here my own error message. So now if you run this this exception is going to be catch by our try and catch here and we're going to see when it's going to this line of code x that matches you're going to see our message. So if you run this so you can see exception uh so exception cut safely my own error message. So the exception was cut by our trying catch and it outputed the the message that we typed here. So this is how you can uh you type your own uh uh your own message for the cancellations. So I'm going to close this. Now let's look at timeouts. And let's say that a quarine is taking more time than expected to finish some work. In that case, you can use timeouts and you can specify an amount of time that you think that corine should finish the job and if not then that corine is going to be cancelled. And um we have two two corine builders, two corine timeouts builders and that is time out with timeout and with timeout or null. But first let's delete this code. So let's delete this code because we don't need this code. Now this also this also this also and we can so with time of this also coroutine builder with the difference that uh it will receive as you can see here a parameter of milliseconds. So let's put here let's say 1,000 milliseconds. And because our corines of our get data one it will take more than 1,000 milliseconds. It will take 2,000 milliseconds that is going to is going to is going to fail. It's going to throw an error. So if you run this and this also as you can see a corin builder but with this difference that has this here. So we get time out waiting for 1,000 milliseconds. So it says parent job failed. That's is because our corine takes more than one one second that we pass here to finish the work and for that it failed. So and because of that it failed. But if I change this to let's say 3,000 mill seconds and if I run this code now everything works fine because u because uh because the time that we pass for 4 hour with timeout is longer than the time it took for the corine to finish his job. So this is with timeout and let's look at the next um time out and that is with timeout or null. So we type here with timeout or null also pass milliseconds here. Let's put 1,00 milliseconds. And now if you run this now we get uh we get in the output fake work one starts fake work to start but we get only result job succeed and we get three three seconds here and uh with the difference with time out null is that time out or null is not going to throw in an error but with time out or null can return a value so if I put here 12 this value now is going to be returned in this variable. So you can see now job job one is of type int nable nable int and you can use that variable in your code. So you can put here print line job one and that is our uh 12 our value that was returned there and uh if if this uh uh times out so it it basically didn't finish in the time that we pass here is then it's going to return null. So u if I put here a print line and if I put job one because this is uh only 1,000 milliseconds and our work takes two that is going to return a null in our variable. So if you run this as you can see we have null that is the print line that we have and we have the the output that we have here. So this is how you can use timeouts. Next, we're going to look at how you can thrown your own exceptions and how can how those exceptions are going to affect the the child corins and the parent. So, I'm going to delete this code. I'm going to change this back to launch. So now it's time to look at how we can throw exceptions inside our corins and how those uh exceptions are going to affect the other corins and how it will affect also the parent corines. So how it will propagate to the parent corine if we if one of the child corins it throws an exception how it's going to affect the other child corins and also how it's going to affect the parent. And for that I'm going to go down here and I'm going to create another private uh suspending function. I'm going to copy this one and I'm going to call it get data tree. So let's paste it here and let's call it get data tree. Fake work three starts and fake work three finished. And let's return result three here. Now let's go up here and uh let's create another uh corine here. So we type val job tree equals and we're going to create a cor routine using the launch corin builder and we're going to add a print line here and we're going to type get data tree and now for the parameter thread name going to type thread current thread name and that is going to give us the current the name of the current thread. Now if you run this quad we get all of our quins starting at the same time and um we get then work one finished uh actually let's go down here because I think I missed something. So we get work one finish, work three finished, then we get work two finished, we get result uh get result three, result one, result two. So our code works okay and get we have only 2 seconds for all of our corines even though they together if they are executed sequ sequentially will take 6 seconds but because they are starting at the same time only have 2 seconds. Now let's say that I throw an exception inside the C routine. So let's say that I throw an exception inside this suspended function. So I'm going to type here throw. So this is how we can throw an exception and I'm going to um throw a generic exception. So exception I'm going to type here and I'm going to type the text error while getting the data in get data one and I am curious about how this is going to affect the other corines which are currently running inside our corin coroutine scope and how is that going to affect our corin parent. So what I interested in is is if the parent will fail or the parent it will say that it has succeeded. So now if I run the code let's see what happens. So now we get an exception and we get main program start fake one starts and we get an exception. We get our message error while getting data in get data one. So we get the message that we we typed here inside our exception and that uh exception is uh is the message we get it uh from here. So we get here actually no no not from here. Let's uh let's go down here. So we get here parent job failed error while getting in the data and get data one. So this is the message that we get here from here. So the per because first what happened the because we throw an exception in in the the job one it also caused the other jobs job two and job three to to fail to to not work and it also cause the parent to fail and we know from the previous video that you can surround this in a try and catch. So we can type here try catch and we can type here x and we're going to going to catch exception the exception that we throw in know in the child and going to put calibration you know the print line here and we're going to type exception session cut safely and I'm going to put dollar sign x dot message and let's change this school cut because that is bad English. All right, let's also add the finally block here finally. And let's print line. Let's say resur resource sources closed say free. All right. Now if you run this code because now we are actually I should put this inside our try and catch. So let's so we get the same error because I didn't put the code there. So let's copy our our code which generates the exception. Let's paste it inside the try. So what we're saying now hey I know that this can potentially throw an error. I know what error is is this exception that we define here. But anyway try to run that code and if an error occurs just catch that exception and uh execute the code. And what I'm curious about is if the next C routines are going to run and if the parent is going to fail or succeed. So if I run this code now now get fake work one start fake uh let's actually go up here to see to see the output. So get main program start main fake one start and we get excession cut safely error while getting get data one. So it cut the exception that we thrown in our get data one function and then it says resources close resources close safely. So this is the message from the finally block and then we get our fake work to start. So our second C routine now it's working. It's not affected by the fact that it was an accession throw in the the first C routine and get also fake work to start and we get the results for fake for fake work one and fake work two and also for fake for fake work two and fake fake work three because you get result three here, result two here, sorry, and result three here and very important the parent job succeed. So we get parent job success. So the parent didn't fail now. So the error didn't propagate to the parent and and it made the parent to fail. So this is one way to to avoid that error. But um let's say that I now I try an exception in the in the second function. What can I do? So let's say that I type here throw also exception and let's say that's going to say error or so quotation marks error while getting the data in get data 2. Let's see what happens now because as I as uh we saw previously now if u we run our code it's going to make our program to crash and we can again surround this in a try and catch block and um so we can type here try catch and we can type here x and exception. We can add a print line here which is going to say uh exception safely. So we're going to type here dollar sign x dossage also the finally block. So finally also print line I'm just going to say resources closed safely. So the final block is going to be executed no matter what happens here. So let's move the let's uh move the code that we have here inside our try. Let's paste it here. Now let's run our code. And because we we've surrounded our code within with a try and a catch block, it now it also didn't cause the it didn't cause the third C routine to fail and it also didn't cause the parent. So we get uh we get here parent job success and we only get result three because uh the other C routines uh for the other C routines we get uh so we get here error Y getting the data and get data one and then we get excession cut safely error get and while getting the data and get data two so we only get the result for our cor routine three. So but if you look at this code it's kind of messy because imagine if I have 10 corine or 20 corines then what can I do? I can surround them with all of them or try and catch I can do that but that is going to look messy and there is another way to to handle all of this using what is called the cor routine exception handler and a supervisor job. So what we can do instead instead of surrounding our code to try and catch we can delete this code that we have. So we can delete this. Let's delete this. Let's delete this also. This is also let's press Ctrl Alt L to format the code. And now if you run the code you get uh our app is going to crash. So our app crashed and also the par the parent failed you get error while getting the data and get data one. Now instead of surrounding our code with try and catch we can create what is called the cor routine handler. And to create a cor routine handler we can type here val and we type handler and we have a suggestion for the time corin handler and we put equals cor routine exception handler and this is going to be a lambda expression and for the first argument we're going to put underscore because we're not going to use the first argument. We don't you don't need that argument and the first for the second argument we type exception. So we type exception, we put the arrow sign. So because this a lambda expression and we add a print line here which is going to say print line error in one of the children and we put uh colon and we put dollar sign exception do message. Now what we can do now instead of surrounding our each surrounding each particular u child corin a try and catch block we can pass this handler that we created here to the parent corine and if uh if an exception is thrown in one of the children that exception is going to be handled is going to be handled by this handle that we created now But this only works if we use our handler with the supervisor scope in conjunction. So if I only pass here, let's we put here parenthesis and if I type here handler. So this now is going to handle if if there is an exception thrown in one of the ch child cor routines which are inside the cor routine scope. So now if you run this code now we'll get an error and the parent will fail because we also need to use uh the supervisor scope. So we get error in one of the children at least our app didn't crash so we didn't get the exception but to get error while getting data in get data one and parent job failed. So our parent job failed and it also caused the the other C routines to fail. And to use the to to make use of this handler we can type here supervisor scope. So supervisor scope we choose this one lambda expression press enter and we move our code which we want to supervise if you want. So you want to look at this code those corines at those child cor routines to see if they throw an error and if they throw an error that error is going to be handled by our handler that we created up here and it's going to it's going to show this message error error on in one of the children and it's going to say exception it's going to uh it's going to output the message for the particular corine. So we surrounded our child cor routines with the supervisor scope expression. We defined we pass our handler to the coroutine parent. And now we're saying hey if an error is thrown in none of the child in order of the child corins then handle this uh handle this uh error and make sure that it's not propagating to the next corins and it's not affecting the parent corin that is uh the which is the parent for the child corin. So now if you run this code look what happens. So previously we got that the parent failed and now we get so now we get we get we get that error while getting the data in get data one. So we get error in one of the children. So we get the message that we have up here error you know the children and we get access message. So we get this message and then we get fake or to start and this uh suspending functions this this corine also throws an exception. So you get again error in one of the children. Now we get error while getting data the data and get data too. Then we get fake tree fake work three starts. So our third corine starts and we get fake work three finish. So now our uh third corin is not affected by the fact that the first and the second corine. So our third coroutine is not affected by the fact that our first and second corine failed and we also get get result three and we get we get parent job success. So the parent now is s is success. It's not throwing an error. So this is how you can throw exceptions and this how you can handle those exception using the handler the corine exception handler you can you can pass it here and you can you surround your code your child corin with the supervisor scope and it's going to be handled by that instead of surrounding each individual corin with try and catch and that's going to look messy but you can just type this and that is going to take care of uh the errors so that the error is not propagating to the next corines and to the parent C routine and you can also throw another exception which is called a cancellation exception and that's it's a special exception and if you throw that exception that exception is not going to cause the other corines to fail. So there is another exception that you can throw and that exception is not going to propagate to the other corines which are currently working in that uh corin scope and it's also not going to propagate to the parent and let me show you how we can use that. So let's comment this code and instead of accession we can type here this special exession called cancellation accession. Now if I remove the supervisor scope let's say and also the handler as I said this is not going to propagate to the other corines and it's not u it's not going to affect the parent. All right so if I run the code now let's press control to format the code. So we get all all three quins starting and uh we get uh we get uh work to finish work to finish. So work one didn't finish because it throw that cancellation but it didn't cause the other C routines respectively corine two and cor routine three to to fail and it also didn't cause the parent to fail. As you can see here, the parent is success even though we didn't use any kind of try and catch or the supervisor job with the handler. So this is another session that you can draw in our code. And I'm going to end this video and in the next video we're going to look at how you can execute C routines sequentially concurrently in other words in parallel and uh lazily. Now it's time to see how we can execute our code sequentially, concurrently and lazily. And for that I'm going to delete the supervisor scope because we don't need this. I'm going to delete the handler because we also don't need this. Now uh we already touched a little bit on this on how to execute our code sequentially and how to execute our code concurrently in parallel and um to execute by default the way we have the code now this code is going to be executed. But first, let's go down here to see if I have an exception. I have this exception. So, let's delete this. So, by default, the way we have our cor routines defined here, they're going to be executed in parallel. So, by default, they are going to be started in parallel. And you saw that when you look at how much time it took to to launch and finish their their their work. So if you run this code this those corines are going to be executed in parallel as you saw in the last video. So you can see they start all at the same time and you get result two, result three, result one, parent job success and we get total time 2 seconds. Even though the if you add the time of all of our uh spending function is 6 seconds, but because they start in they they are started in parallel. So they start at the same time, it only took 2 seconds to execute all of them. Now if you want to execute them sequentially so one after another and in that case you're going to have six seconds just type job one join and that means that wait for this uh wait for this C routine to finish and then execute the next C routine and let's do the same thing for job two. So job two dot join. So what we're saying now is first wait for the first C routine to finish and then execute the second C routine and also wait for the second C routine to finish and then execute the third C routine. And now if you run this pay attention at the total time that it took to execute all of them. So if you run our code and if you look at the output as you can see it says fake work one start fake work on finish result one fake or two to start fake or to finish fake or three starts fake or three finish. So they are executed sequ sequentially. So you get result three par and the total time is 6 seconds. So this is because now they are executed one after another. So one finishes the next starts the the second finishes the next one starts. So the total time this way the total time now is 6 seconds. So this is how you can execute the code sequential sequentially. The next uh way of executing our corins is lazily. And lazily means that we're going to execute our corins only if we use the result that the corins gave us back in the code. So let's delete this because we don't need this. And I'm going to change the launch to async because it's going to illustrate better how that is going to work. So I'm going to change this to async. So async. So this is just a different quarantine builder. We looked at this in our video was about corines builders. Async also async. And um I'm going to delete this because now this value is going to be returned in the the variable job three. also here also here because the f the function get data one and get data two and three return a value and that value is going to be returned those variables so as you can see we have variable first it's of type def first thing I'm not going to type the type the type again because uh it can be inferred now I'm going to uh uncomment this code and uh I'm going to type job 1.08, job 2.08, the weight and um I'm going to put uh back a plus back uh I'm going to put quotation marks uh forward slash n and I'm going to type uh job three dollar sign actually job three dot job three8 and if you press u shift controlp as you can see this whole uh expression is of type string. So this is going to return the value that is going to be returned by those functions. So it's going to return result one, result two and result three. So we're going to get this back that we have here and here and here. Now if I go up, if I comment this code, let's say, and I don't use the result in my code and I run this code, look what happens. So now I'm not using uh the result that uh the cor routines are g are given back in my code but they are still executed. So as you can see here they are still executed and uh and um we get work one finish work to finish work three finish and we get parent job success but we don't use uh we don't use the results. So we don't use what those corines are giving back their work. You not you don't use that in our code. So it doesn't make sense to to have them executed. And in that case you can have a uh the cor routines starting lazily and starting lazily just means that is is going to start the corin only if you use that the result that that corin gives back in your code. And to start a C routine lazily you type you put parenthesis at the end of the async. So you put parenthesis and you type star. So this name argument start equals cor routine start and we choose this one cor routine start lazy. And now that corin is going to be started lazily. It's not going to be started if you don't use the result in our code. Also here we're going to type the same start equals cor routin start uh cor routine start lazy and um also here start equals cor routine start lazy let's press ctrl alt l and let's actually put this in a more concise way so this is more concise so if I So this like this and this not this. All right. And let's put a let's add a space here. Now if I run this code now you're going to you're not going to see that the corines are started because we're not using them. We're not using the result in our code. You only get main program starts. So we get main. Now the problem is that because um the we have here parent job join this never finishes. So let's comment this also. So comment with line comment I don't see where it is. So let's run this again. So get main program starts total time and get main program ends. So now we don't get our corines started because you don't use them inside our code. But if you use them inside our code. So let's uncomment this. So let's go to code and let's comment with line comment that is going to undo. Now if I use the result in our code. So if I use I type print line and job one.8 job 2.8 and job 3.8. Now because I'm using them inside the our code, I'm using the result that they are giving back. Now they're going to be started. So as you can see now they are not uh grayed out. So now if I run this we get main program start get fake one one start fake one to finish and so on. And then we get result one result two result three and parent of success total time 6 seconds. So now our corines are started because here on this line of code we are using the result that uh the suspending function are giving back to the variable that we define here because you use them inside our code. Now they are started but if you don't use them inside your code they're not going to be started. They're going to be staying there and going to be started only when you use the use them in your code. So this is how you can use um this how you can u start a corin lazily and now I'm going to end the video and then the anal video going to talk a little bit more about corin scope. So now it's time to look at the with context function and uh to understand why you need to use the with context function and how to use the with context function and for that I'm going to create a new function call a new suspending function. So I'm going to type private suspend fun and it's going to be called set text on main thread. It's going to have a parameter called input. It's going to be of type string. Now we use the width context function when you want to switch uh the current the current thread in which we are currently in to another thread. So if we are um specifically with this function with if you are uh now doing work in a back background thread and uh let's say that we want to update UI component or like a text view or something if you try to update that uh UI UI component from the background thread the app is going to crash. And what you can do in that case is that you can switch the context. So you can switch the thread from the background thread from from which you are currently in to the main thread because you can update the UI only using the main thread. So you can type with context you type main. So basically what you're saying here, hey switch the context, switch the threads from the background thread that I am currently in to the main thread because I want to do work on the main thread and we select the first one. So we press alt enter and we put curly braces and here you you add your code to update uh add code to update update the UI and I'm going to bring uh Android Studio to see how this looks in a real example. So as you can see here I have I have a button I have a text view and so on and I this code is similar to what we have in our IntelliJ. I have a coroutine and I start the C routine in using IO which means input output. Then I I have a function a suspending function get fake data and uh I have uh two C routines and here I have this function set text on main thread and I have this job data weight and this is going to return a string value. If you you press shift uh shift control p on this as you can see this uh is a is a string. So if you press control and if you go where this function is defined, this takes as an input a string and is switching the context to the main thread because on the main thread are our UI components the the text view and the button and we take that input we append it and then we assign that text to our text view and u for whatever reason I can run the the virtual phone but if I go here to activity.main and u if I go to code let's put some text here let's say uh for the text text so text just to show you how this will look so now this uh UI component that we have here text is going to be updated with the text that is given as as an input here from the from uh from inside our C routine and because we're inside our C routine and we are uh uh on a background thread and we want to update the UI component we switch the context so we switch the thread you say with context switch the context to main thread and then update that UI component because if you don't do that the app will crash because you cannot update uh you cannot update the UI components from the background thread. So this is the with context function and this is how you can use it uh in a real app. Back in the previous section we spent a lot of time on core routines launch async with context cancellations structural concurrency and by now you can write the synchronous code that doesn't block a single thread. So here is the question that opens this entire section. What happens when the answer you're waiting for isn't one answer? That's what a flow is for. A flow is a stream of many values produced over time and delivered as synchronously and is built entirely on top of the core routines you already know. Everything on the screen here, flow, emit, collect, the word called. We're going to unpack all of it in the next 15 minutes. By the end of this lesson, you won't have written a real flow yet. That starts in a couple of lectures. What you'll have is the mental model. And I promise you, every single lesson in this section is going to be easier because of it. So what core routines solve? Let's start by being precise about what core routines actually gave us because it's easier to blur the two ideas together. Corutins gave us three things. First work without blocking. You write suspend and delay and while your code is waiting the thread underneath is free to do something else. Second control over threads. Dispatcher.io IO moves a database read or a network call off the main thread and you get back onto the main thread automatically when it's done. And third structure concurrency a scope owns its core routines and when the scope dies everything inside it dies with it. That's a lot and it solve a real problem when does the work happen and where it does it run. But now let's look at the code on the right. Suspend fun fetch prices is going to return double. Look carefully at the return type. Double is singular, not a list, not a stream, one number. And that's the whole shape of a suspend function. Drown underneath. You make one call, you wait however long it takes, you get one value back, and then the call is over. The call routine resumes and moves to the next line. So every suspend function you have ever written makes exactly the same promise. One value once and then I'm finished. Flow exists because something that promise is the wrong promise. So I'm going to repeat that. Flow exists because sometimes that promise is the wrong promise. Let's slow this down because this is the limitation. The entire section is built around you get price. That's the circle on the left. The quar suspense does the dash line. And this part is generally great because the trade is freed the whole time. Something comes back from the network. You get your value 154.20 and then that red bar the stream is closed. Done. There is no channel left open between you and that function. No. Now there here is the problem. 1 second later the price is changes. It's 154.85. Now how do you find out? You don't. Nothing is going to tell you because the function already return your corin already resume. So your call in already resume and the call no longer exist. So what do most people do the first time they hit this? They pull. They write a while through loop with a delay of 1,000 in it and call get price over and over. And it kind of works. But think about what you actually build what you actually build. You're burning battery. You're hammering the server with request every second whatever anything change or not. and you're still late because if the price changes 200 milliseconds after your last pull, you won't know for another 800 800 milliseconds. Polling is a workar around for a missing language feature. Flow is that missing feature. Here are the two shapes side by side. And I want you to look at the timelines rather than the code. On the top lane is get price, a suspend function returning a double along the whole timeline. There is nothing to sell nothing and then the right at very at the very end one value appears that's it that's the entire lifetime of that call the bottom lane is get prices and notice the return type has changed to flow double now look at the timeline you have 15420 15485 153.90 1550102.31 and those three dots on the those uh so those three dots on the right mean it hasn't finish. It's still going. It might go for the rest of the app lifetime. Same data source, same network, completely different shape for fans. I wanted to hold on this image because it's the cleanest way I know to describe the difference. A suspend function hands you a photograph. A flow opens a window and leaves it open. Streams are everywhere. And once you see that shape, you start seeing it everywhere, including in apps you already built in this course. Live prices, a stock or a crypto feed that ticks every second. That's the example you will use throughout this section because it's the clearest one. Chat messages. You build a messenger app. You know, new messages arrive whenever someone else decides to send one. You can't ask for the final message. Room database. This one is huge. A room query can be declared to return a flow and then it remits automatically every single time a new row in that table changes. You write the query once and your UI stays correct forever. Location updates. GPS gives you a new position every few meters while the user is walking. Search as you type every key store keystroke produces a new set of results. Six keystrokes, six resources and connectivity. Online, offline, online again. That's not a value. That's a state over time. Now look at what all six have in common. Not one of them has a final answer. There is no last price, no last message, no last GPS position. Each one is a source that just keeps producing. and the source that keeps producing exactly the shape that flow describes just a return list. Now before I show you flow properly, I want you to walk through the two obvious things you you you would try instead because understanding why they fail is what makes flow click. First attempt just return a list. Gather all the values then hand them back. On the left, you can see it. A suspend function that loops 60 times, fetches a price, waits a second, adds it to a list, and after 60 seconds returns the list. It compiles, it runs, and it's wrong in three separate ways. One, you wait for the whole thing. Nothing nothing reaches the screen until the very last value lands. Your user sits and watches a spinner for a full minute even though the first price was ready was ready after 200 milliseconds. Two infinite sources never finish. A live price feed doesn't have a six tint value. It doesn't have a last value at all. So the function never returns and your app just hangs there forever. And three, this is the subter one. The data is still the moment it arrives. By the time you finally get a item number, one of one out of that list, its real value has already changed 59 times. You've handed the user a minor number and presented it as a current. So you present it as current. The core mistake is a mismatch of concepts. A list is a snapshot. A fixed set of values that all exist at the same instant. A live price fees is not a snap snapshot. It's never a snapshot. What about a sequence? Second attempt. And this one is smarter because a lot of people who know cotling will go straight here. Cotling has sequence. A sequence is a laz is lazy. It doesn't build everything up front. It produces one value at a time only when the consumer asks for the next one. You can see it on the left. Sequence curly braces a loop and yield to hand out the each value. And the laziness is generally what you want. That's the green box on the right. values are produced on demand and one at a time. That's exactly the behavior we are missing from list. But look at the red box. Look at the line four of that code thread sleep sequence calibration is not a c routine. It's a plain synchronous construct. You cannot call delay inside it. You cannot call retrofit endpoint inside. You cannot call a suspend query inside it. The compiler will refuse. None of the none of none of those are allowed because there is no quarine there to suspend. So the only way to wait inside the sync is to actually block the thread and look at what that does down the down at the bottom. That bar is your main thread. The that bar is your main thread. The green slivers are the tiny moments where the value is actually produced. All that red is the thread sitting there frozen doing absolutely nothing but waiting. On Android the red read red like that is a frozen UI. It drops frames and a responsive screen and eventually an N array any N any. So list gives us a synchronous but not lazy. Sequence gives us lazy but not a synchronous. What we actually need is both lazy and a synchronous and that is precisely what flows is. It's it lives in cotlin x.cor routines.flow same library as the cor routines you already using. It's not a separate dependency and under the hood is an interface the single method. That's it. The whole thing is remark remarkably small. Here is the definition I want you to memorize and then you will take it part word by word. A flow is a is an synchronous stream of values that are produced one at a time over time and consume without ever blocking a thread. three parts and they map exactly onto the three problems we just walked through. A synchronous is build on suspend when a flow is waiting for the network for the next stick of the clock. The thread underneath is free. That's the that's what sequence couldn't give us. Stream many values not a single result. That's what a plain suspend function couldn't give us over time. Each value is emitted the moment it's ready. Not B chopped and delivered at the end. That's what the list couldn't give us. three failures, one type that fixes all three. And I really do mean it when I say the rest of the section is built on that one sentence. Flow builders, operators, exception handling, state flow, share flow, all of it. It's elaboration on those three words where flow fits. Here is the picture I put on on a sticky note next to your monitor because it locates flow relatively to the everything you already know. There are only two questions. How many values and can it suspend button left of your mental map? Actually, let's go clockwise. One value synchronous that's double a plain value already in memory available on the line you're standing on. Nothing exotic. One value is synchronous. That's a suspend fun. One value delivered later without blocking. That's the entire previous section. Many values synchronous. That's a list and the sequence. Many values yes but computed on this thread which means blocking. And then the fourth box many values are synchronous. That's a flow. Many values over time never blocking. And notice for a long time cotton generally had a hole there. That's a that box was empty. You had to reach for ax Java to fill it. which is why you'll you'll still see observable and fable flowable in a lot of Android code bases and why you never and why we cover Java later in this course. Flow is a is cotlin's own answer written in corines designed for the language. So it's not some strange new paradigm is some is a is the synchronous version of a question you already ask every single day. Give me all of them as they come. Anatomy of a flow. Right, let's open it up. At the top of the screen, you can see a complete flow chain in one line. And I've colorcoded it deliberately because every flow you'll ever write has exactly three parts. Get prices. Does the producer is the thing that creates the value and emits them one at a time. in code does flow emit X and we'll write one at the next slide. Map and filter those are the intermediaries or as you'll normally hear them called the intermediate operators. They sit in the middle and transform filter or combine values are as they pass through. And here is the important part. They don't consume the values. They don't produce new ones out of there. They they're a pipe section collect. Does the consumer sometimes call the terminal operator? It asks for the values. And here is the thing that surprises everybody the first time by asking is is it is what starts the whole machine. Two rules for that diagram. Values travel one direction only. Producer then operators then consumer. There is no going backwards and the entire chain is completely idle until the consumer calls collect. We'll come back to the second one because it's the single most important behavior of flow. The producer, let's build the producer. Flow is the builder. You can see it on the left. F get prices. Flow double equals flow curly braces. That's a normal function, not a suspend function. And returns a flow double. When somebody calls get prices, they get a flow object back immediately, instantly. And the block inside has no run, not a single line of it. Whole heart is the whole of slide 14. So it's the whole of slide 14 inside the block. Emit is how you send the value downstream. Every call, every call to emit pushes exactly one value to whatever is collecting it. You can call it once, you can call it 100 times, you can call it forever inside the while through loop like you're doing here. That's completely legal. And for a live price feed, it's exactly right. And now the part that makes flow different from sequence, look at the lines three and five. API fetch price that's a suspend function. Delay 10,000 also suspend. Both of them are perfectly legal in there because the block inside the flow it's a core routine. Retrofit calls room queries delay with context any suspend function you like. That is the exact thing sequence couldn't do. some laziness. But now with suspension, instead of blocking, no freez no frozen thread, no red bar. One quick note before we move on. Flow isn't the only builder. If you already have the values in hand, there is flow of one, two, three, and any collection can become a flow with flow. We'll go through all of them probably in the properly in the basic flow builder lessons. For now, flow is the one to understand because it's the one that shows you the mechanism. The consumer now the other end the consumer collect is the terminal step. Look at the code on the right. We call get prices then collect then curly braces prices arrow and that lamb lambda runs once for every value to prod the producer emits. First price arrives, lambda runs, takes update. Second price arrives arrives, lambda runs again and so on. Three things you must know about collect. First is the terminal operator. It walks, it walks the chain all the way to the end and actually pulls the values through. Nothing else does that. Second, and this is why the code is wrapped in life cycle called data launch. Collect is a spend function. You cannot call it from ordinary code. It may it needs a core routine to live in in an activity or a fragment. That's usually life cycle scope. In a view model is view model scope. Third, every call to collect is its own independent independent run. Two collections means the producer executes size completely separately. Nothing is shared between them. Again, slide 14. And here is the trap that catches everyone at least once. In the orange box, collect suspends the caller until the flow finishes. Anything you write after the collect will not run while the values are still coming. And if your flow is a while through loop, which ours is then so which ours is then still coming means forever and that code after it will never that code after it will never run. Not a bug in flow. That's it's working. That's it's that so that is working exactly as design. You just have to know it. Putting it together. Let's put the two halves together and actually traces what happens. The block tickers returns a flow string. Inside we take three tickers, three tickers string and for each one we we emit it and then delay for a second. Bottom block we launch a C routine called tickers and collected pri printing each value. Now watch the console output as a button because the timing is the whole point. At the time zero, the instant we collect the flow block starts running. It emits a 189.4 and that gets printed immediately. Not after a minute, not after two, not after flow finishes. Right? Then then the producer hears delay 1,00 and suspends. And here is what matters. During the entire second, the thread is free and your UI is responsive. Animations, run, touches, register, nothing is blocked. 1 second in MCFT 412.7 is immediately imprinted. Another second have NVDA 118.2. too. Then the for each finishes, the flow block ends and flow completes and and only now those collect return and the corin moves whatever cames next. That's the whole model. Values arrive at a time. Each one deliver the moment it exists with completely free thread in between. So flows are called. Now the most important behavior in this lesson I want on and the one that trips almost everybody coming from array Java or from live data flows are called look at the left panel val prices get prices now we call the we called we've called the function we have a flow object sitting in a variable and what has happened nothing no emission no call no corin started the flow block has not executed a single line that variable is a recipe not a running process it cost you essentially nothing. Now the right panel prices that collect render it. Now the block runs the while loop starts fetch prices is called emit fires value flow down the collector. Collecting is what starts the work and this is a has a consequence people find surprising the first time. If you collect the same flow twice the producer runs twice. Two independent executions, two separate network calls, nothing shared, nothing cached, no coordination between them. That's what called means. The value are generated per collector on demand. The flow doesn't have a life of its own. It only exists while someone is listening. Now, is what always is that is that always what you want? No. Sometimes you want one shared source that's always running and broadcast to everyone and everyone sees the same values. Those are called hot flows. Shared flow and state flow. They're they're an entire station on their own. Section 15. And by the by then this could not this this cold hard distinction will be the same thing. Everything hangs on. So I'm going to repeat that there an entire section on their own section 15. And by the end the this cold hard distinction will be the thing everything hangs on for now. One sentence declaring a flow cost nothing. Collecting it starts the work. So let's fill in the middle of the pipelines at least conceptually. We'll write the this probably in a few lessons. on the left the source values coming out of the producer. So I have 109.40 412 minus one 118.20 they pass through map which transforms each one. Here is here here we're converting to euros one value in one value out every time. Then they hit filter which decides which one continue. That minus one value is bad data. So we do we drop it. Notice on the right there are only three values left not four. And then whatever survive reaches the collector. Three things to take away. Every operator returns a new flow. Map, filter, transform. Each one wraps the flow before it like nesting boxes. You're not mut mutating anything. Operators are called too. Chaining map and filter runs precisely nothing. It builds a description of what should happen. You could chain 20 operators and your app would do zero work. And the terminal operator is what starts it. Collect is the one you'll use most. But first to list count are terminals too. Those are the ones that actually pull the value through the whole chain. Producer, operators, consumer, cold, cold called, and then go. List, sequence, and flow. Quick side by side. So all three sit in one picture. How many? List gives you all of them at once. Sequence gives you all of them lazily. Flow gives you as many as arrive. So I'm going to repeat that. How many? List gives you all of them at once. Sequence gives you all of them lazily. Flow gives you as many as arrive. And that number might be unbounded. When a list is fully built before you ever see it. A sequence produces on demand. Item by item. A flow produces over time as the values come into the existence. Thread list blocks while is being built. Sequence blocks while it's producing. Flow never blocks it suspends. That one row is the reason flow exists. Best four. Use a list when you have a finished result set. Use a sequence for a heavy local computation over a lot of data. And use a flow for anything that keeps changing. If you only remember one thing from this table, list sequence are about data you have. Flow is about data that keeps arriving. Build on corines. I want you to close the loop on something I said at the start because it's generally good news. Flow is not a new world. It's corines. Everything you already learned still applies. Look at the code. Life cycle scope.lines prices collect render it. That's a normal C routine and it gives a job gives us a job. Then job cancel and the flow stops emitting. That's the whole cancellation story. Three consequences. Context preservation. A flow runs in whatever cores. So if your collect on the main thread the producer runs on the main thread by default if you need it somewhere else there is an operator called flow one and you use that never you and you use that never with context inside the builder flow activisely forb that and it will throw runtime if you try there is a good reason and we cover it later cancellations comes free cancel to the collecting corin and the producer stops it at its next suspension point. No registering a listeners, no boolean flags, no memory leaks from a callback you forgot to detach. This is a generally enormous improvement over the call back and listener patterns you'd otherwise write. Structure concurrency. A flow cannot out leave the scope collecting it. When your view model is cleared, view model scope is canled and every flow being collected in it stops automatically. So everything you've invested in learning corines pays off directly here flow in a real app. Let me show you where this lands in a real Android app because this is the architecture we're going to build in the coming lessons. At the bottom of the stack the data source room retrofit web soocket. This is where the flow is created where flow or room dao produces floaty. Above it the repository. It combines sources and caches it. It receives a flow and returns a flow. Then the use case business business rules and mapping again takes a flow returns a flow. Then the view model which turns row values into UI state. And finally the UI which collects and renders. Notice the pattern is that band across the middle is the same floaty traveling all the way up transform at each layer and collected only at the very top. two rules that follow from that. Only the top layer collects. Every layer below just returns flow. If a repository called collect, it would start the work far too early and hand it color a dead value instead of a live stream. Return the flow. Don't drink from it. And collect with the life cycle. In a fragment activity, you collect inside repeated one life cycle. So collection starts when the skin uses the visible and restarts when it comes back. There is a whole lesson on this lecture on 137 because getting it wrong is one of the most common flow bugs in production on Android. What comes next? Next lesson is short. Reactive programming putting a name to the style of thinking you just you we've just been doing. Then we start our first real flow use cases and build something. Then flow builders so you know every way to create one. Then terminal operators, all the different ways to consume. Then intermediate operators, the middle of the pipeline. And eventually in section 15 could cold versus hot state flow and share flow. You don't need any of that to understand what flow is. But every one of these lessons is going to make more sense because you now do. It's going to make more sense because you now do. All right. So, I'm going to end this video here and I'll see you in the next. Welcome back. In the last lesson, we define what a flow is. This lesson is shorter and it's about the style of thinking that goes with it because reactive programming is a phrase you'll see everywhere in Android job descriptions. And it's worth being able to say precisely what it means. Short version, stop asking for data, declare what you want, and react when it arrives. So, let's unpack that. Pull versus push. The entire paradigm turns one one question. Who takes the initiative? On the left is what you've been doing your whole career. Pull. You decide the moment you call a function, you get a value back. That's imperative. And there is nothing wrong with it. Except that if the data changes one second later, nobody tells you. So you call it again and again. On the right side, this push the source decides a moment. You said once what should happen to a value and from that one it happens every time a value appears. You're not driving anymore. You're responding. That's it. That's the whole shift. Reactive programming. It's a pushbased programming. You stop writing fetch it now and start writing. Here is what to do whenever it changes the spreadsheet rule. And if you if that sounds abstract, here is the thing that makes it click for most people. You already think reactively. Every spreadsheet you ever opened is a reactive program. A1 is five. B1 is three and C1 you didn't type eight. You type A + B1. A1 plus B1. You declare the relationship. Now change A1 to 9. What do you do next? Nothing. You don't do anything. C1 becomes 12 on its own because you never told it told it a number. You told it a rule. Nobody wrote recalculate C1. Nobody register a listener on CLA1. The relationship was declared once and the change propagated by itself. That's the entire idea of reactive programming. And you've been comfortable with this. You've been comfortable with it since the first time you open Excel. Cotlin flow is that for your apps data. Two ways to write it. Here is the same idea in code. A running total written both ways. On the left imperative, three variables. Total is a plus b. then a changes. And now look at what you have to remember. Okay, I'm going to repeat that on the left. Imperative three variables. Total is a plus b. Then a changes. And now look at what we have to remember. Recalculate the total. Then update the UI. Two lines that have nothing to do with your feature and everything to do with bookkeeping. And here's the real problem. That's one place where a changes in a real app. There are 11 miss one and we have a bug that only shows up on specific screen after a specific action. the kind that takes an afternoon to find. On the right, reactive total is declared once as a combine of a and b then a value equals 9 and total remits by itself is there is no bookkeeping line to forget because the relationship is the code. That's the practical argument for the whole paradigm. Not that is clever that it removes the entire category of of bug that it removes an entire category of bug. Everything is a stream. The second big idea is unification. Look at these three lines. Clicks the user tabs four times at unpredictable moments. Network responses come back mostly 200 to one mostly 200 seconds. uh one 500 database query results a different number of rows each time. Three completely different sources, different libraries, different threads, different failure modes. But look at the shape. Identical values arriving over time in a sequence. And that's the payoff once you all all three have the same shape once one set of tools work on all of them. I'm going to repeat that and that is the payoff. Once all three have the same shape, one set of tools works on all of them. Map works on clicks and one and one database rows. Retry works on network responses and on sensor readings. You learn an operator once and it applies everywhere. Instead of learning a different API for every source of data in your app three reactive libraries quick orientation because you'll meet all three in Android cotlin flow build on corines part of a standard ecosystem now called by default cancellation for free this is the modern default and it's what we use for the rest of this course section er Java the library that brought reactive programming into Android in the first place. Observable, flowable, single and enormous operator set on a generally steep learning curve curve, but is still everywhere in existing code bases. So you'll meet it. We cover it later in this course and after flow you I'll feel famili you'll feel familiar rather than foreign live data and Android's own life cycle aware holder simpler simple safe and very limited one value almost no operators is steadily being replaced by state flow which we get in a section 15 same idea in all three different aeroggonomics all right I'm going to end this video here and I'll see you in the next video come back Now let's build something. In this lesson, we set up the skeleton that every remaining lesson in this section plugs into the model, the data source, the repository, and the use case. And I want to be upfront about one thing. By the end of this lesson, there still won't be a single flow in the project. That's deliberate. And I'll explain why when we get there. So what are we building? Here is the target. target a screen showing a list of stocks ticker price that updates itself once per second. Now the most useful thing you can do at the start of any feature is to write the requirement as one plain sentence. Our is give me the latest list of stocks and keep giving it to me for as long as the screen is open. Read that again and notice which half is the hard part. It isn't give me the latest list. A suspend function does that fine. It's keep giving it to me. Those four words are the entire reason this section exists. Three things follow from that sentence. It is not one off request is going to be built in layers with the value traveling from the data source up to the UI and we do it one step at a at a time. This lesson is the skeleton the flow itself in lesson 116. The layers we will use there are there are these are our layers. This is the clean architecture trim down to to exactly what this one feature needs. No no more stock data source where data is born. Right now it it fakes it light later it it returns a flow stack repository the single source of true today. Today today it does nothing. Eventually, it's where combining and caching live. Use case one business action. That's the name of the course uses. And yes, it's a boring name, but it's it's honest is the first use case. Stack view model holds the UI state and is the only layer that will ever will ever collect. Main activity renders whatever the view model exposes and the rule under all of it. The dependencies point one direction only. The UI knows about the view model. The view model knows about the use case and the data source knows about nobody at all which is exactly what makes it testable and replaceable. The stock data class start with the model because everything else passes passes passes it around. Stock with a name, a price and a and a change. That's it. Four lines that three deliberate choices in there. That's it's a data class and that matters more than it looks. Equals compares by value. So later when a new list arrives, the UI can generally tell whether anything change instead of redrawing blindly. If data classes are hazy, the data class lesson earlier in the course is worth 10 minutes of your time. Everything is val. Immutable items are safe to send down a stream and that several collectors might read at once. a multiple object traveling through the pipeline in a race condition waiting for a busy Tuesday. And there are no Android types in here. No context, no parable, nothing from the framework. It's plain codlin which is what the domain layer to be unit tested without an emulator. Watch the type at the bottom of the screen grow through this section stock then list stock then flow list stock. The last one is where we will head it. What is a use case? Quick word on the pattern because use case gets thrown around loosely. A use case is one business action in one class with one public entry point. No stock manager, no stock service, one action. Three things worth noticing in the code. One section per class means the name and the test are both obvious. There there's no debate about what belongs in use case one because there is only room for one thing. operator for invoke is a small nicely. It lets callers write use case one instead of use case.execute. It reads beautifully at the call site and it cost you one keyword and it's trivally testable. Hand is a fake repository and you and we've covered you've covered the business role in about three lines. No Android, no emulator, no marking framework, not the return type to today list stock in this lesson in the lesson on Android CC that becomes flow list stock and that's the only line in this class that will change the files we create five files create them now and the app compiles and run by the end of the lesson stock. In the domain model package stock data source.k and data stock repository.k data use case one.kt in domain and stock view view model.k in presentation. The package name are a suggestion not a rule. What matters is the layering that the domain package doesn't import anything from data and that and that the data package doesn't import anything from presentation. If your team already has a folder convention, use uh use theirs the data source first file with any substance the data source. Right now it returns a hardcoded list of three stocks. Apple, Microsoft, Nvidia fixed prices, nothing clever. Two questions people always ask here. Why isn't this a flow yet? because I want to I want the wiring correct before I before I add the interesting part. Changing a return type later is one line edit and tangling bad layer bad layer bad layering later is weekend is a weekend get the I'm going to repeat that I add the interesting part changing the return type later is one line edit and untaggling and untaggling bed layering later is a weekend get the shape right first make it leave second why is hardcoded because if you set up the retrofit right now the Next 20 minutes of this lesson becomes a networking tutorial instead of an architecture lesson. A fake source keeps its focus and because of the layering is because the layering is right swapping for a real API later touches exactly one file repository and use case. Now the the now the two thin classes and I do mean thin. Stack repository takes a stack data source and exposes stacks which returns whatever the data source return one line. Use case one takes a stack repository and exposes invoked which returns whatever the repository return also one line. At this point, somebody always says these classes do nothing. Why they exist? Right now there that's a completely fair objection. They do nothing. But here is the thing. In three lessons, we add error handling. Where does it go? In the repository later, we add caching. Where does it go? Repository. Later, still the business rule that filters out STS with no price that goes in the use case. Every one of these has an obvious home because the home already exists. A layer that's empty today is a layer that you don't have to you don't have to retrofit under pressure tomorrow. That's the whole argument and it's worth the two files. The view model the view model is the first layer that actually does something stacks. So underscore stacks is a mutable live data and it's a private stocks is a plain live data and it's public. That's the standard pattern the UI can observe but it can never assign right access stays inside the class that owns the state and yes live data for now it works and it's what you already know from earlier in this course in lesson 120 we will see as live data and in section 15 we replace the entirely with state flow we replace it entirely with state flow consider this a stepping stone Now look at the load and it calls use case one and it assigns the result and that function is the problem in miniature. It's a pull. It's a pull. Somebody has to call it once and then the value sits there frozen until somebody calls it again. That's the gap the rest of this section closes. One diagram worth internalizing construction goes downward. The activity creates the view model. The view model is given the use case one. Use case one is given the repository. The repository is given the data source that that goes upward. Stack data source. Stack repository. Use case one. view model UI opposite direction and that's not a coincidence is the point. Notice the phrasing I keep using each classes given its dependency not creates the repository doesn't do stack data source inside itself. It receives one through it constructor that's construct injection and it's why swapping the fake source for a real retrofit one is one line change in whatever wires the up together instead of a search and replace through five files. It's also why every one of these classes can be tested with a fake. What is still missing? Let's be honest about what we've learned working today. The layers are wire are wired end to end. Dependencies are injected. A stock list actually renders on screen and every class is a unit testable still missing. The list never updates. Load has to be called by hand. Nothing reacts when a price changes. And then the the return type is a list, not a flow. Now read those four failures again. They're not four problems. They're one problem described four ways. Livestock is a snapshot. That's it. That's the whole gap. And in lesson 160, we change exactly one return type list becomes flow list stock. And because the layers are already correct, the single change fixes all of four at once. That's the payoff of for spending a lesson for spend spending a lesson one structure. All right, I'm going to end this video here and I'll see you in the next video. Welcome back. In this lesson, we're going to see every way to create a flow. There are four builders that matter in practice. By the end of this video, you'll be able to pick up the right one in about two seconds. And more importantly, you'll understand why flow curly braces is the one that actually matters and the other three are conveniences. So what is a builder? Start with the definition because it's less mysterious than the word suggests. A builder is just a function that hands you a flow. That's it. Look at the chain at the top. Builder, then flow, then operators, then collect. The builder is always the first link. Three things to keep in mind. It creates, it does not run. A builder returns a cold flow. Calling it executes none of the code inside. We hammer this in lesson 112 and it stays true there. It stays true here. You choose by asking where the data comes from. That's generally the only question. Values you already have, a collection you already have, values you you want to compute, a list API. Each one of those has it own builder. It's always the first link. Every chain starts with a builder and ends with a terminal operator. Flow of use it when you already have the values right here right now and you can type type them as arguments. Flow 1 2 3 4 flow API and so on. There's also empty flow when you need to a flow that emits nothing at all which sounds useless until you're you're writing a br a branch that has no data to return. two places this earnest it is keep it's the obvious choice for a fixed known set and is the test double favorite most flow unit test you'll ever write starts with flow of fake stocks because it lets you feed the pipeline known values in a certain what comes out look at the marble diagram all four values emitted immediately one after the other and then the flow completes thus the vertical bar at the end means done no more viruses ever. That's different from what we built in the last section and it matters when you get to the terminal operators like two list that need a flow to finish as flow builder two as flow when it so use it when the data is already exist as a collection a list a set an int range a sequence any iterable really it's an extension function so you call it on the collection list of and we type that as flow three things. It's a bridge, not a source. The data is already in memory. Asflow doesn't fetch anything or compute anything. It's just changing the shape so that flow operators become available to you. It emits immediately because there is nothing to wait for. Values are pushed as fast as the collector accepts them. And we're saying ask why first. If you've got a complete list already in memory and it's small, plane collection functions like map filter are simpler than converting to a flow and back reach as flow when you generally need flow behavior downstream. Combining with another stream, applying retry or feeding something that expects a flow flow builder tree flow. This is the one that matters. fun price is flowable double flow equals so equals flow inside a while through loop that fetches a price emits it waits a second and repeats three things to notice the block is a cor routine look at the API that fetch and delay 1 both suspend function both perfectly legal in here that's the entire difference from sequence and is the reason flow exist at all it runs on collect not on call calling prices parenthesis execute zero lines of that block. If you you get a flow object that you get the flow object back instantly. It may never complete a while through producer is completely legal and legal and a live feed exactly right. There is no rule saying a flow has to end. Look at the marble lane values a second apart and the flow is just keep going. Emit the rules. Emit is the only way values leave a builder. So four rules about it. One call, one value. Emit x pushes exactly one item downstream. Not a batch, not a collection. One, it's a suspend function, which is precisely why you can only call it inside the builder block. There is no code routine in ordinary code for it to suspend. In order is guaranteed. values arrive at the collector in exactly the order you emitted them. Flows are sequential, not reordering, not surprises. And the fourth one is the interesting one. Back pressure is automatic. Emit suspends until a collector has finished handling that value. So a slow collector doesn't cause a buffer to fill up and overflow. It just makes emit wait a bit longer. Pen that because it's a generally nice property in Eric Java back pressure is a whole topic with strategies and flowable versus observable in flow it falls out of suspensions for free. The producer can never run ahead of the consumer because the meeting is waiting suspend not sleep. Let me put flow and sequence side by side one more time because this is the single craziest way to see why flow exists. left sequence it yields a value then it has to wait and the only tool it has is thread sleep delay won't even compile in there because sequence isn't a c routine right on the right we have flow it emits a value then delay 1000 any suspend function you like on the page those two blocks look almost identical same identation same shape same l underneath what the thread is actually doing during that one second. On the left, blocked red. If that's the main thread, your UI is frozen. You're dropping 60 frames. And on the right is free. Thread goes off and does the work and comes back when the delay is over. Identical one on the page. Completely different on a phone. Does the lesson emit all. One more thing you'll need. And what happens when the values you want to emit are themselves already a flow? The natural instinct is emit other flow and that compiles which is annoying the which is the annoying part. But look at what it does. It emits the flow object itself as a single value. Your collector receives one item and that item is a flow almost never what you meant. What do you mean? What do you meant is emit all that follows every value from the other flow into this one. And there is a lovely pattern that falls out of it in the marble diagram. Emit header value yourself. So emit head emit a header value yourself. Then emit all the real source. Emit loading then emit all repository that stocks. That's how a show of loading state and the data flow is usually written. One emission you control then the whole upstream forwarded forwarded. Call back flow builder for call back flow. You'll need this on one Android one on you. You'll need this one on Android. Use it when you wrapping an old school listener API location update sensors a Firebase listener an SDK that predates C routines anything with a register and unregister. The example is location creates create create the call back object call try send inside it register it and then await close two things that trip people up it's try send not emit the call back fires from outside of the core routine it's called by the framework on whatever thread it feels like so emit it's illegal there try send is the nonsuspended version that works from anywhere await close it's mandatory is the optional politeness the builder will actually throw if you leave it out. It runs when the collector goes away and it's where you unregister forget it and you've leaked the listener. You leaked the listener which and it usually means you've you've leaked the Android the the Android activity too. That's the deal. Give it a way to clean up and you'll get callbacks converted into a flow for free. cancelable, composable and usable with every operator. Which builder when all all four side by side? So all four side by side flow of values come from arguments you type emitted incorree test and fix sets as flow values come from an existing collection. Emitted in list, ranges, sequences, flow values come from code you write, emitted whenever you call emit. Network, database, timers. This is the highlighted column for a reason. Callback flow callback flow values from a listen API emitted when the call back fires location sensors firebase and the hest summary at the bottom flow. So the honey summary of the bottom flow is the one that matters. The other three are conveniences. You you could write every one of them yourself using flow and the loop. A two second decision. Here is the decision as four questions. Walk them up. Walk them top to bottom. Stop at the first. Yes. Do I already have the exact values? Flow. Do I already have a collection list as flow? I I am wrapping a listener or a callback call back flow. Anything else at all? Flow. And in real work, nine times out of 10, you'll fall through line four. That's completely fine. Flow is being the default answer. Isn't a failure of the decision tree. Is the decision tree telling you the truth building the stock flow. Now, let's use it. This is the change we promised at the end of lesson 114. Before latest stock returns list stock a hardcoded snapshot. After he returns flow list stock build with flow curly braces inside while true emit a freshly random list delayed 1 second repeat and look at the marble lane the same three companies every single time but a new list object with new prices once per second does the distin does does distinction matters it's not the same it's mutating it's a immutable list replacing the old one which is exactly what our val only data class from lesson 114 was set up for. You will wire this for properly into the data source in the next lesson. For now, notice how little code it took. Flow, emit, delay. Three things, three ways to get it wrong. Before we finish, three mistakes. All three compile. All three are wrong. One, emitting from another corin. Look at the code. Flow calibrates with context dispatcher.io emit load. It compiles cleanly cleanly and it throws illegal state exception at runtime with a message about flow invariant violation. Why? Because the flow must emit from the corin that collects it. That's a guarantee flow makes and with context breaks it by switching to a different one. This is the single most common flow mistake there is. If you need to change the thread, there is an operator called flow one for exactly that and we cover it with intermediate operators. never with context inside the flow. Two, a builder does that never suspend. An infinite loop with no delay and no suspension point can never be cancelled because cancellation is checked at suspension points. Your flow will keep spinning after the view model is dead. Three, expensive work on every collect. Cold means per collector. If two things collect, your flow your network call happens twice. That's not a bugging flow. That's coldness working correctly. But if you don't want it, that's exactly what share in and share state flow are for in section 15. All right, I'm going to end this video here and I'll see you in the next video. Welcome back. This is the lesson where the app stops being a snapshot. We take the fake data source and turn into a real stream with flow and put the change up to the repository and the use case. And along the way, we cover the single most important habit you need when a flow crosses a layer boundary. One layer changes. Here is the whole picture. Before we touch anything, look down in the middle column. You have flow list stack at every single level. Data source repository use case view model. Same type all the way up. And now look at the two highlighted rows. The data source where the flow is created and the view model where it's collected. There are the only the those are the only two layers that actually do anything with it. Everything in between just passes passes it along which sounds lazy but in fact exactly right. And the next slide explains why. This is the this is the rule. If you take one thing from this lesson, take this one. A layer that receives a flow should return it, not collect it. Look at the code on the left. the wrong version. A repository that collects it declares a v last, collects the source, assigns each value to last, and returns it. Two things wrong with that. First, the moment you collect, you've turned a live stream back into a dead snapshot. You've thrown away everything the flow was giving you. And second, on an endless flow like ours, collect never returns. So that return last line is unreachable. The function hangs forever. It compiles and it deadlocks your app on the right. The correct version fun stocks is going to return a flow list of stock is going to be equal to source latest stock list one line still called still cancelellable still lazy. The rule of thumb collect is collecting is drinking. You do it once at the very end when you actually going to use the value. Every layer in between just passes the passes the glass along a real producer. Now the change itself in stock data source latest stock list becomes fun latest stock list flow of list of stock and it's going to be equal to flow curly braces while true emit generate stocks and delay of 1,000. The return type is the so you have three observations here. The return type is the API. Anyone calling this now knows they're getting a stream before they read a single line of the body. Types are the documentation that can't go stale. Nothing runs yet. Flow is called everything in that block is a discretion until somebody collects it. You'll come back to that. Generate stocks fakes the market. A random random walk on the prices. So the list visibility changes the each tick. So I'm going to repeat that. generate stocks fakes the market a random walk on the prices so the list visibly changes each each tick it's a help I will write on the next slide a stream of list before you go further read the read the type carefully because this catches people catches people flow st is a stream of whole list each emission carries the complete current stock list look at the marble lane each is a list of three List flow stack is the opposite. A fixed list of separate streams, one per company. Three independent flows that you would have to manage separately. Ours is the first one. And here is how to reason about it. The screen shows one list at a time. So one emission has to carry the whole list. Does the test what do the test what does the UI render in one go? That's what the one emission should be. Get this backwards and every operator downstream fights you. You need you end up writing flat map merge and combined to flatten streams you never needed to split in the first place. It's a genuinely painful mistake to unwind. So spend the two seconds now faking the market. Quick look at generate tax because it's a nice use of something we already built. We hold the currently a current list. Every call we map over it, generate a small random delta between minus2 and plus two and produce a new stock with the adjusted price with the adjusted price. And now look at what's done. S.copy price S price plus delta comma change equals delta. That's copy from the data class we wrote in lesson lesson 114. earning this keep its keep. So you're earning its keep one line instead of a constructor called listing every field. Three nodes copy makes the makes this trivial. That's the payoff for making stock a data class. We produce a new list of new objects every tick not mutated ones that matters downstream. GL would yield in the recycle view compares old against new and mutating in place would break the comparison entirely and when you swap this for retrofit later callers don't change the return type says flow list of stack that's the whole promise of putting the type at the boundary repository and use case now push it upward two files and the change in each is basically two characters Stock price stocks stock repository stocks the body is identical source latest stock list only the declared return type change to flow list of stack use case one that invoke same story body is identical return type change this is the payoff for spending less than 114 on structure a change at the very bottom of the stock ripple all the way up to the top and not one line of logic moved If though those layers hadn't existed, this would have been a refactor instead of it's a type annotation. And notice nobody called collect three layers deep and the and the stream is still cold, still unstarted, still completely cancelable. Nothing nothing has returned yet. Which brings us uh brings us to the to a sanity check I want to you to actually do. Use case one returns a flow object instantly. The while through loop has not started. There is no collector. So the producer has no reason to execute. Zero emissions, no delay, no generated stocks, no coring. Total cost so far one object allocation. This is the coldness doing exactly his job. And here is the thing worth doing. Put a break point inside that flow block right now and run the run up. It will never be hit. not once generally try it. Feeling filling that in the debugger does more for your understanding of cold flows than any diagram I can draw. In the next lesson, the view model finally collects and the entire chain wakes up at once. All right, I'm going to end this video here and I'll see you in the next video. Welcome back. So far, we used exactly one way to consume a flow, collect. And collect is the important one, but isn't the only one. In this lesson, we go through the terminal operators. Collect, collect, latest, first, last, single, to list to set count, reduce, and fold. What each one of you reach for it, and this is the part that actually catches people, which one will hang forever if you point them at endless flow. So what is so what terminal means? First, the definition because terminal is doing real work as a word. Look at the chain on the screen. You have flow, then map, then filter. All of that is called nothing runs. It's a discretion. And then collect, that's the switch. That's where the whole thing executes. Three properties to define a terminal operator. The it ends the chain. A terminal operator returns a value or unit, never another flow. I'm going to repeat that. It ends the chain. A terminal operator returns a value or unit, never another flow. which means nothing can be chained after it. If you can call map on the result, it wasn't terminal. This it's a suspend function every single one of them which means every single one needs a core routine to run in will come back to that because this is the most common compiler or people hit it starts the producer the builder and every operator in between only execuse because a terminal asked them to. And one rule that follows one terminal per chain. If you you've written two, you don't have one flow with two consumers. You have two chains and two completely independent runs of the producer. Four kinds of terminal. There are quite a few terminal operators and memorizing and alphabetical listes are useless. So group them will group them by question they by the question they answer. Consume every value. Collect collect latest launch in you want to reach to each of them as it arrives. takes just one. First, first first n last single. You want a single value and then you're done. Gather them all. Two lists to set counts. You want the whole stream materialize and note the warning already. This needs a flow that finishes. Boil them down. Reduce fold. You want one accumulated result from many values. Learn the four questions and the operator names take care of themselves. Collect. Start with the fundamental one because everything else is build on it. Stack flow collect list and you have curly braces list inside a launch the lambda fires once per emission in order on the collector's context. Three things it run once per value in order and and on whatever context the collector is running in. It returns when the flow ends, which on our endless stack flow means it never returns. That's normal and usually exactly what do you want for a screen is the primitive two list is two list is collect plus a multable list. First is collect plus an early cancel. Reduce is collect plus an accumulator. If you understand collect, you already understand the mechanism behind all of them. Collect is sequential. Now a detail that catches people out and it's worth 30 seconds. Flow123.colct delay 1,00 print line it. How long does it take? Not 1 second. Three. Because your lambda isn't a call back that gets fired and forgotten. It's part of the pipeline. Emit suspends until your lambda returns. The producer literally cannot emit value. That's that uh cannot emit value two until you you're finished with value one. Three consequences. Emit waits for you. That's back pressure and we get get it for free. Nothing to configure. A slow collector slows everything. Heavy work inside collect throttless the entire stream. If that's a problem, move the work off thread with flow on or use collect latest which we're about. You see nothing is ever dropped. And this is the flip side and is generally val valuable. Compare it to a callback fire house where where events fire whenever you're ready or not you whenever you are not and you silently lose the ones that arrive while you're busy. With flow that can't happen. The producer awaits. Collect latest. Sometimes waiting is exactly the wrong behavior. That's collect. Look at the diagram. Same source. Three values A, B, C. Arriving faster than you can process them. With collect, uh, A runs to completion, then B runs to completion, then C. All three finish. You did all the work. With collect, A starts, then B arrives and cancels a mid A midflight. So, it cancels a mid-flight. B stars, C arrives, and cancels B. Only C completes. That's it. When you a new value arise, whatever you were doing with the old one is canled. And that's exactly right. Whenever the previous result is already obsolete. Search as you type is the classic. If the user has typed two more letters, the result for the old query are worthless. So why finish computing them? Live preview previews map rendering anything where the newest value matters. One caveat. Your block has to be cancelellable for this to work. Meaning it needs a sus meaning meaning it needs suspension points. A tie CPU look with no suspension want to actually stop and collect latest cancel it. Cancel it first and first null. Now the take one family stock flow that first suspends until one value arrives returns it. And this is the key bit. Cancel the producer. cancels the producer. That's what makes first safe on on an endless flow. Our stock flow runs forever, but first takes one emission after a second, cancels the while through loop and returns. No leak, no hanging corin. There is also first curly braces and the predicate which keeps collecting until something matches then cancels and first or no three notes important. It cancels upstream. That's the whole reason it's it's usable on live sources. First throws first throws when the flow is empty. No such element exception if the flow completes without emitting anything. If empty is a legitimate outcome, use first or null and handle the null. It's perfect for one shot reads. reading the reading a value out of a data source. Taking a single snot of a live source for a widget or a share action anywhere you want the current value and nothing more last and single two more value terminals and both have a catch. Last gives you the final emission flow one to three last is three straightforward but look at the second example stockflow.last last that hangs forever and once you think about it is obvious. Last cannot possibly know which value is the lightest until is the is the last until the flow completes. Our flow never completes so it waits forever. Single is the stricter. It demands that the flow emits exactly one value. Flow off single is 42. Flow of one two single throws illegal argument exception. Zero values also throws. So zero values also throws that sounds hostile but it's generally useful and it use it use it when exactly one is a real invariant and you want the runtime to reinforce like a lookup by primary key if two rows come back you'd rather crash in development than silently take the first one and ship a bug to list and set uh sorry to list and to set the gather everything family to list collects every emission into a list to set does the same but duplicates sorry dduplicates so it doesn't have duplicate count just counts them all straightforward and all of and all co carrying the same very large caveat which is the red card on the screen on endless flow this this hang list has to wait for the flow to complete before it can hang you can handle your list it doesn't know if if it's finish otherwise pointed to our stock flow and the cor routine never comes back. No exception, no error, no log, just quietly stops. That's a horrible bug to diagnose on a finite flow. They're all completely fine. A room query that emits once a fixed flow of a network response. A fix, so a fixed flow of a network response. I'm going to repeat that. On a finite flow, they're all completely fine. A room query that limits once a fixed flow of a network response. And the practical tip at the bottom, if you catch yourself writing two list on a live source, what you almost certainly want is take in two list. Make it finite finite. Finite first, then materialize it. Reduce. Now the accumulators flow 1 2 3 4 reduce and have the lambda acc value AC plus value walk the three the boxes across the bottom. It starts by taking the first emission as the accumulator. So ACC is one then plus two is three then plus three is six then 6 + 4 is 10 one value at out at the end. Two warnings. An empty flow throws reduce has no seed value. It starts from the first element. So if it if so if there is no first element, it has nothing to return. It needs a finite flow for the same reason to list does. It can't produce a result midstream. Fold fold initial fold is a reduced with a seed and it fixes both problems. flow 1 2 3 fold 100 gives you 100 C because it starts from 100 not from the first element. Now look at the second example on the left. Empty flow int zero returns zero no crash because it's already has something to return. Now look at the right hand side because this is the difference people miss. Folds accumulator can be a different type from the elements there. They there were folding a flow string into a list string. So they were folding a flow string into a list string. The accumulator is a list. The elements are strings. Reduce structurally cannot do that. It's accumulator accumulator starts at the first element. So it must be the same type as elements. Fold doesn't have that constraint. So fold never throws an empty and fold can change the type when in doubt when when you're not sure use fold reduce versus fold three different side by side starts from so starts from reduce starts from the first emission fold starts from the seed you pass in accumulator type reduce must match the elements fold can be anything you like on an empty flow reduce throws fold returns a seat. Both are terminal. Both suspend and both need a flow that actually completes all of them suspend. Now here is the compile error you'll hit probably today. Top block fun button click value stack flow that first red squiggle. So red squiggle suspend function first should be called only from a corin or another suspend function. bottom block something wrapped in view model scope launch compiles every terminal operator suspend so every terminal operator needs a core routine and I want to say this clearly because I've seen it go wrong the fix is never to remove the operator it's not to make the function blocking it is definitely not to reach for run blocking the fix is to give it a scope view model scope in a in a view model life cycle scope in an activity or make the calling function suspend and push the decision up one level needs a finite flow. Let me put the whole danger list in one place because this is a table worth screenshooting safe on endless flows. Collect runs forever which is which is the point. Collect latest same cancelling state work as it goes. First cancel the producers the producer after one value hangs on endless flows last can't can't know the last value until it ends two list and two set wait for completion count same reason reduce and fold can't produce a result midstream on a finite flow every single one of these is fine it's only the endless ones that bite so the question to ask before picking a terminal is always does This flow end apply to our stock app where the flow is endless. The choice narrows fast. Collect keeps the screen updated forever. That's what what that's what our view model does and it's right. First grabs one snapshot. Generally useful a home widget or a share current price action where you want the value now and don't want an ongoing subscription. Collect latest. If rendering the list ever took longer than a second, this would drop still frame frames instead of a queing up behind them. Worth knowing it's there to list would hang forever only sensible after take like the line at the bottom stockflow take five to list. Take five makes the flow finite and now to list is save. All right, I'm going to end this video here and I'll see you in the next video. I'm back. Now it's time to do one more terminal operator and it's a slightly unusual one because it launching doesn't give you any capability. It gives you a better shape. By the end of this you'll understand what it does, what it doesn't do and why almost every model Android code basis uses it uses it. So start with the annoyance it solves collect suspense. So it always arrives wrapped in a launch block. Look at the code. View module scope launch stock flow map filter collect. There is nothing wrong with that. It works. It's correct. And if you never learned launching, you'll be fine. But three things niggle. Extra nesting. The interesting code is too indent deep before it even starts. You read it inside out. The scope is the first thing on the screen, but the thing it operates on, the flow comes second, and the actual work comes last. Your eye has to jump around. It gets worse with more chains. Three flows in one view model means three launch blocks each with its own set of braces. And now the file is mostly punctuation. So not wrong, just noisier than it needs to be. On each replaces the lambda. The fix comes in two steps and step one is on each. On the left work inside collect flow collect vo equals v. On the right the same work on in on each flow on each v stocks value equals V identical body but on each is an intermediate oper intermediate operator. It returns a flow not a unit which means you solve nothing yet. The chain isn't finished. It's on isn't finished on its own on each runs absolutely nothing. It's a cold as a map or a filter. Something still has to start it. That's the step two. Launch in scope. Launching is the internal operator that launches a c routine and colleagues inside it. Look at the chain now. Source source map filter on each launch in three things. It reads top to bottom source then transforms then side the side effect then where it runs in that order. U your eye goes down the page once. It's still a terminal operator. It ends the chain and starts the producer exactly like collect does everything we said last we said last lesson about internal operators applies it returns a job not unit and that's actually useful you'll get to it in a couple of slides the same code twice side by side left view model scope launch stack flow map that collect the curly braces render it seven lines two levels of Nesting right stockflow map one on each render it launch in four lines no nesting and I wanted to to be precise about what change nothing did same threads same scope same cancellation semantics same coldness the compiler generates near identical code this is poorly readable change which is exactly why it's worth knowing and exactly why it's not worth arguing about in a code review if a colleague prefers lunch plus collect They're not wrong. And here is the thing that usually removes the last bit of confusion. The actual implementation is four lines. Public fun tining scope routin equal scope. That's it. That's the entire operator. Scope. One launch one collect with no lambda because one each upstream already did the per value work. So there is nothing left for collect collects block to do the new behavior. Nothing about trading cancellations or coldness changes because there is nothing in those four lines that would change it. Honestly reading the implementation is the fastest way to stop being confused about launching. What launching does it's a convenience function and now you've seen the whole thing. Which code do I pass? The one real decision is the argument because the scope decides how long the collection lives. So which scope do I pass? View model scope dice with the view model survives rotation. This is your default for anything a screen needs. Life cycle scope dies with the activity or fragment restarts or restarts one rotation which is usually not what you want for data. There's a better tool for life cycle aware collection and you cover it in lesson 137 a custom scope for work owned by repository or a singleton fine but now you're responsible for cancelling and forgetting to it to to is forgetting it is a leak global scope never dies almost always a leak if you find yourself reaching for it stop and ask what actually owns this work and the thing to internalize launching does not Create a scope. It borrows one. You hand it and it inherits a scope lifestyle. Exactly. All the safety comes from your choice of argument. It gives you a job. Now the return value which is generally handy. Launching returns a job. So you store it ticker job equal stockflow on each render launch in and later ticker ticker job. Two uses pause and resumeum feed. Cancel the job. When the user opens a dialogue or navigates to a detail screen, launch it again when they come back. You stop the network chatter without tearing anything else down. It's a finer grain than cancelling the scope. The view model keeps leaving. Every other collection keeps running. Only this one stops. You couldn't do that as neatly with lunch collect. You will well you could launch also returns. So well you could launch lunch also returns a job but with lunch in the job is the natural result of the expression rather than something you have to remember to capture when not to use it. Three cases where launching is the wrong choice. When you need the result launching returns a job not a value. If you want the emitted data back one stock list or completed total you need collect first or two list inside the C routine you control. When the work must finish before the next line launch in a fire and forget by design the code after it runs immediately. What? The code after it runs immediately while the flow is still going. If you're thinking then I'll use the rest of the I use the result on the next line. That's not what happens when you're already in a cor routine inside the suspend function. You already have a cor routine. Plain collect is simpler than and clearer there. Introducing scope just launching inside the suspend function is a step backwards. Rule of thumb side effects launching values you need collect one chain read down read downwards here is the shape you'll see in almost every model Android codebase annotated to have source shape effect scope in that order. Once your eye is trained on that pattern, you can read an unfamiliar view model in about 5 seconds because every chain it will look the same. And the last line always tells you the lifetime. All right, I'm going to end this video here and I'll see you in the next video. Welcome back. So far, we covered how to create a flow and how to consume it. This lesson is about everything in between, specifically the four operators that let you hook into the life of a stream. on start, on each, on completion, on empty. Where they run, what they can do, what they can do, and one trap that catches almost everybody. A flow has a life. Here is the mental model. A flow has a life. It starts, it emits some values, and eventually it finishes. And there is a hook for each one of those moments. One start before the first value exists on each for every value that goes past on empty. If if it turned out there there are no values at all. One completion after the last value when the flow is done. One thing to be clear about up front because the name life cycle can mislead. This have nothing to do with the Android life cycle. This is the life cycle of the stream not of the activity and all four are intermediate operators. They return a flow. They're called and they run in the collector's core routine. None of them consumes anything. You still need a terminal operator at the end. On start first hook on start runs once after collection begins but before the producer emits anything. The classic use case is use the classic use is right there in the code on start loading value equals true. Three things exactly once per collector and I want to stress per collection. This is a code flow. So two collectors mean two independent runs and one star fires twice. If you're expecting a global this run once guarantee, you can get one. It can emit two. The block is a flow collector which means emit X is illegal inside it. Then that's more useful than it sounds and is the next slide is the natural place for loading. Anything that should happen the moment the subscription begins before there is data. Look at the marble lane. The on start effect happens before a a even exists. One start can emit. Here is the trick properly. One start emit empty list. Now look at what the collector actually sees on the right. An empty list first immediately then the real values as they arrive. You've prepared the value to the stream without touching the producer at all. Two reason that's nice. Instant first frame. Our stock flow takes a second to produce its first list list. With this, the UI gets an empty list right away. It can render the header, the empty state, the layout instead of showing nothing for a second. The producer stays untouched. This is a consumer side concern. The data source shouldn't know or care that one particular screen wants that one particular screen wants a placeholder. putting in the consumer's chain keeps the decision where it belongs on each second hook and the one you'll use most on each it runs once per mission and you can chain as many as you like one for logging one for updating set one for analytics they run in order top to bottom three properties one permission in order before that value continues downstream it passes the value through an unchange through so it passes the value through and change this is The key difference from map was what whatever came in in goes out. The return value of your lambda is completely ignored. Login analytics state side effects belonging on each transformations belong in map on each versus map. Let's make that distinction sharp because it's the one popular blur. What goes downstream on each sends whatever came in. Map sends whatever you return. What the lambda is for? On each is for side effect. Map is for producing a new value. Typically use on each log it, store it, react to it. Map turns into something else. And there is a and here is a test you can apply mechanically. If your lambda last line is an assignment or a log statement, you want on each. If it's an expression whose value can whose value you care about, you want map. I've been playing I I've seen plenty of code doing map state value equals it yet assign then return the value. So the chain still works. It compiles, it runs and it lies about intent but that's it lines about intent that's on each complete third hook on completion runs once after the flow finishes and crucially for any reason at all the lambda receives one parameter cause a nullable throwable and that parameter tells you which of the three things happened. The lambda receives sorry cause equals equals null. The flow completely normal. It run out of values and ended. Cause is an exception. The flow failed. You get the throwable. And I put a warning in that card deliberately. You get it, but you have not handled it. More on that in two slides. Cause is a cause is a cancellation exception. The so is cause is a cancellation. Cause is a is a cancellation exception. The collector was cancelled and the own completion still runs. That's what makes it useful. How flow can finish? Let me lay those three endings out probably because they generally different situations. Normal completion cause is null. The producer run out of values and return flow one to three after the third value. Exception cause is a throwable something through upstream. The flow is dead and the exception continues on down the to the collector. Cancellations causes a cancellation exception. The scope was cancelled. The screen closed video the view model was cleared. Somebody called job. And because it runs in all three cases on completion is the closest thing flow has is the closest thing flow has to finally block. That's the right way to think about it. Hide the spinner. Close the resource. Lock log the end of the session. Whatever must happen regardless of how it ended. On completion is not catch. Now the trap and this catches almost everybody once. Look at the left hand code. A broken flow on completion cause log then collect. You get a nice log line and then your app crashes anyway because on completion of serves it doesn't intercept it. It gets a look at the excession on its way past and the excession carries right on down right on down to the collector where it's thrown. The right hand version uses catch cause emit empty list. Catch intercepts. It consumes the accession and in this case emits a fallback value instead. Now the flow recovers the oneline version on completion sees it. Only catches only catch stops it. If you only log in on completion, you haven't handled anything. You just made the crash better documented. Catch gets its own full lesson in section 14 on empty. Fourth hook and the one you'll use list on empty. It runs only the flow completed without ever meeting zero values then done. Search result query on empty emit no results. Three notes. Empty is not the same as failed. A search with zero matches completed perfectly. It just had it just had nothing to say. That's the important distinction is easy to conflate the two in two in your error handling. You can emit a fallback. That's the classic use turns nothing into an explicit empty state value that your UI knows how to read, how to render. It never fires on an endless flow. Our hours always emit eventually and never completes on empty is dead code in your app. Rarely needed but when you do want an empty state is the cleanest way to express it. Position changes meaning. Now something important that applies to all four three operators only see what's upstream of them. Look at the two versions left source one completion map risky it collect if map throws does on completion see it no one completion only wrapped source the failure happens below it so it's invisible right source map risk it completion collect now completion wraps both the source and the map so it sees the exception every one of these operators wraps only the part of the chain above it. Think of it as nesting boxes. An operator can only ever observe what's inside its own box. Practical rule, put life cycle hooks as far as down the chain as you can, so they can observe everything. And the same rule applies to catch, which is the source of most why didn't my catch fire question you'll see on stock overflow. the full life cycle all four in one chain and you can read it as an in as an English sentence start the spinner handle each value cover the empty case and stop the spinner whatever happens that's the standard loading conting don't shape and it's expressed entirely in in the chain no flags no if statements no state machine in order let's trace it a finite flow of two values every hook attached so we One one start spinner show no value existed. Two one each value A arrives states updated. Three one each again value B arrives state updated. Then on empty skipped the flow DD so it never runs. Does the gray out row. Four on completion with co null spinner high hidden swap the source for empty flow and step two and three simply disappear while on empty fires instead. Everything else is identical. adding a loading state. Now, which of these actually matter for your for our app? Our flow is endless and that changes the answer quite a lot. On start, very useful. Show the spinner the moment view model subscribes before the first stick arrives arrives a second later. On each, we already use it. That's where stocks value gets assigned from lesson 120. One completion only fires on constellations. Our flow never completes normally. So in practice this runs when the view model is cleared still useful for a session ended log but don't expect it in a normal operation on empty never fires an endless flow always emits eventually dead code here the general lesson which hooks matter depends entirely on whatever your flow ends and ask that question first. All right. Three traps. Three things people get wrong. All the all three compile happily. So I'm going to repeat that. Three things people get wrong. All three compile happily. One, treating one completion as catch. Loging the cause doesn't consume the exception. Your app still crashes. Use catch to actually recover. Two, putting hooks too early in the chain. An operator only sees what's above it. on completion before map will never see map fail maps failure. Three, expecting one start to run once globally. It runs once per collector. Two collector means two spinners and two independent runs of everything below. And notice that the number three stops being surprising surprising the moment you remember flows are called. Almost every flow surprises surprise traces back to that one property. All right, I'm going to end this video here and I'll see you in the next video. Welcome back. This is one of the longest lesson in this section and it deserves to be because as live data looks like one line convenience and is actually the answer to a real sharp problem. We'll cover what the problem is, what as live data does about it, the two parameters nobody explains, the live data builder, the reverse conversion, and importantly when you should not use any of it. The UI is not safe. Start with the problem because if you don't feel the problem, the solution is just ceremony. Here is the fragment collecting a flow life cycle scope. View model stock.colct render it. That looks completely reasonable. It's also wrong in three separate ways. It runs while hidden. A user presses home. Life cycle scope is still alive. The fragment isn't destroyed, just stopped. So the flow keeps emitting. The network keeps working. The battery keeps draining for a screen nobody's looking at. It can touch dead views. An emission arriving after one destroy view runs render it against the view hierarchy does no longer exist. Best base case you know up. Worst case null pointer exception or you leak the whole view tree. It restarts on rotation life cycle in a fragment dies with the view. So every rotation cancels and restarts the entire flow. New network call new everything. And here is the root cause of all three. A flow knows nothing about Android. It has no concept of one start, one stop or a life cycle. That's a feature. It's a why flow works on the server and inly multiplatform. But at the UI boundary is a genuine gap. What live data solves? Now compare the two types honestly because live data is in almost every respect the weaker one. operators. Flow has dozens. Live data has map, switch map, and basically nothing else. Life cycle awareness. Flow has none. Live data has built in. That's the entire reason for existing where it runs. Flow runs everywhere anywhere in any platform. Live data. It's Android only sharing. Flow is cold and one run per collector. Live data is hot and shared and holds the last value. So each one has exactly what the other lacks. Flow is a brilliant pipeline and a poor UI boundary. Live data is a poor pipeline and a good UI boundary which gives you the strategy flow for the pipeline. Live data at every edge and as live data is the join between them. So as live data here is the operator val stocks live data list stock use case one as live data one line that's it three things no launch no correct the entire init block we wrote in lesson 117 and refactor in 120 just disappears that's it's it's a terminal operator it ends the chain but in this is the interesting part the collection doesn't start when you write that line it starts when somebody observes the live data we'll come back to that one dependency Android EDX life cycle life cycle live data KTX is if a as live data isn't resolving that's the missing line in your gradal file it's it is a live data builder under the hood it's not doing anything mysterious roughly it collects the flow inside the corin does the live data once and remits each value into the live data none of the two parameter because most people never look at them according context and a time time that defaults to 5 seconds. Both of those get their own slide because the timeout in particular is doing something clever that's worth understanding. It follows a life cycle. Here is the payoff. The collection starts when and stops with the with the server's life cycle state. One create no server activate flow not collected. One start observer becomes active. Collection begins. One resume still collecting. One pause still collecting notes that one pass does not stop it. One stop of server inactive. Collection stops. The rule underneath live data considers consider started and resume to be active and everything else inactive. And that's where the battery saving comes from for free with no code from you. The user presses home fires and your network call stop. They come on back when start fires and it resumes. You wrote one line. Why five seconds? Now the time out which is the generally clever bit. Here is the problem is solved. Rotation also fires on stuff. So if as late data cancelled the collection, the instant an observer went inactive. But every single rotation would tear the flow down and rebuild it. New C routine, new not call, new everything for a state change that talks about that takes about 300 milliseconds and lands you right back where you are. Look at the top lane. One stop, cancel, start, restart. Wasteful. Bottom lane with the 5 seconds time out. One stop and then the C routine is kept alive waiting. One start fires a few hundred milliseconds later and the same running collection is simply resumed. So 5 seconds is a grace period. Long enough to comfortably cover a rotation or a brief interruption. Short enough to generally back generally backgrounding the app stops the work almost immediately. It's a well chosen default and most of the time you should leave it changing the timeout but you can change it. So here is what the what the number means. 5 seconds the default. Leave it alone unless you have a specific reason. Zero for expensive sources. If every emission costs a paid API call or a lot a lot of battery, stop the instant the screen is hidden and accept the rotation restarts it restarts it. That's a real tradeoff in something sometimes zero is right. Large value. So large values. Be careful if you set 30 seconds and you're deliberately choosing to keep a cor routine running behind a hidden screen for half a minute. There are legitimate reasons and in progress of upload say but leave but have one as live data context in the other parameter the coring context use case as live data dispatcher.io IO moves the whole collection onto the IO dispatcher. Three things. It moves the whole collection. Everything upstream runs on that dispatcher. It's a blunt instrument. All of all all or nothing. Flow one. It's more precise use case that flow one dispatcher that has live data moves only the part of the chain above the flow on which is usually what you actually meant. We cover flow on probably in the next lesson. Usually leaves it empty. A well-built data layer already picks its own dispatcher. The repository knows he's doing IO and says so. If you find yourself needing this parameter, that's often a hint the dispatcher decision is in the wrong place. The view model shrinks. Now let's apply it to our app. On the left, we we what we had after less than 120 a private multable live data, a public read only view of it and a need block on each launch in nine lines on the right. F stocks live data list stock use case as live data two lines no backing field no you need block no line chin and here is the part that matters more than the line count the two lines behave better the collection now stops when the screen isn't visible which is the n line version never did an unusual thing in programming normally less code means fewer features here the code means more correctness because you've handed the life cycle problem to something that already knows knows how to solve it. The view is untouched and look at the activity. View models stacks that observe this list adapter submit list identical to less than 117 character for character. Three consequences. The UI says framework shape views observe live data. They have no idea a flow has ever involved and they don't need to. You can swap the source freely. Change the entire data layer room to retrofit fake to real flow to cal backflow and the activity never notices. It's easy to test. Live data has a well established test helpers like jet get or await value and your re layer tests don't need coring test rules. This is the strongest argument for doing the conversion in the view model. It puts one clean boundary between the reactive word and the Android word live data directory. There's also a builder for when you want more than a straight conversion. We have stocks live data emit loading val data rip that fetch one emit success data three nodes. It's a core routine. Suspend calls one legal insight exactly like flow. The fetch once can be retrofit call. Emit source other forwards another live data into this one is the emit all of this world. Useful when you want to emit a cache value and then switch it to a live source. Use it sparingly. If your data layer already speaks flow as live data is simpler and keeps the pipeline in flow where the operators are live data earns its place when the live data is the whole logic. The marble lane shows the classic shape loading then success without a flow anywhere. live data flow. The conversion goes the other way too as flow. So legacy as flow map launch in two things. It's a migration bridge. You inherit a codebase full of light data. You want to write a new code with flow operators. Rub the world source and carry one. That's generally a common situation. The result is not cold. This is the softly. It's backed by a hot live data. So it belongs like a hot source. The underlying value is shared and collectors see whatever the live data currently holds. Don't reason about it as if it were flow. Useful in a real uh migration, not something to design around from scratch. Three ways to expose a state. Let's put the three options side by side because you'll meet all of them. Multiple live data fill manually inside the launch life cycle where no operators best for Legacy XML code is no flow anywhere as live data field automatically life cycle aware full flow chain upstream best for XML plus or flow data layer which is exactly your app state flow filled with state in or a multiple state flow not life cycle aware on its own it needs repeat on life cycle full flow chain best for compose and more than code state flow is a section 15 is section 15 and it's where we end app. But for now in an XML app as live data is the pragmatic choice and honestly it remains a perfectly good choice in production XML apps today. When not to use it three places where as live data is the wrong answer just by compose compost has collect a state with life cycle which does the life cycle aware collection natively from a flow. Converting flow to a live data just so compos can convert it back to poor is poor ceremony use state flow inside the data layer live data is an Android type putting it in a repository or a use case drags the framework into code that shouldn't be poor cotlin which kills your unit test and blocks any move to multi multiplatform convert at the view model boundary and nowhere else when you need a current value synchronously Live data value can be null before the first emission. So every read has to handle null. State flow always has a value by construction. If there is always a current state matters to your design, live data is the wrong container. But in an XML project with a flow data layer as live data is generally the simplest correct answer and I don't want to cave to I don't want the caveats to obscure that four things that bite four pitfalls none of them produce a compile error one expecting it to run without an observer as live data live data collects nothing until something observes it no observer means no network or no log focus on nothing and it looks completely broken. This accounts for most most my life live data doesn't work questions. Check your check you actually called observe two observe this is in a fragment use view life cycle owner not this the fragment the fragment outlives it view. So observing with this means the observer survive view con destruction and you leak the classic fragment leak and is still everywhere. Three assuming value is never null before the first emission is null. Every reads has to cope. Four converting too early. Do it as do it as the view model boundary. Fly data in a use case is a design smell. Which one should I expose? Three questions top to bottom. Is the UI written in Jetpack compose state flow? Is the XML with a flow based data layer as live data? Is it XML with no flow anywhere? M multiple live data. And that's fine. Our app is XML with a flow data layer. So as live data is the right answer for now. In section 15, we'll revisit one state flow is one that will revisit once. Revisit one state flow is on the table. All right, I'm going to end this video here and I'll see you in the next video. Welcome back. We've done builders at one end and terminal operators at the other. This lesson is the middle the operators that shave value as they pass through. Map, filter, transform, take, distinct until change and flow on six operators that cover most today work plus the two rules that decide whether your pipeline is efficient or wasteful. So what is an intermediate? What intermediate means? the definition first and it's mechanical. An intermediate operator takes for flow and gives you back a flow. Look at this uh strip flow a map flow B filter flow B in out in out three properties completely called chaining 20 operators executes nothing. You're describing not doing flow in flow out. That is literally the definition. If it returns anything else, it's terminal applied per value lazily. The lambda runs each value passes through. So the lambda runs as each value passes through not up front on batch while it's uh which is different from collections. List map process everything immediately. Flow map process each item as it arrives. And the mental model each operator wraps the flow above it like nesting boxes collecting and wraps them from the outside in. The picture this that picture explain almost every why did it behave like that question in this lesson. Map first operator and the one you use most map one value in one value out and the type may change. Stack flow map list list size turns a flow list of stack into flow in three things always one to one three values in three values out map never drops never adds if you need to drop that's filter if you need to add that's transform the type can change flow A becomes flow B that's most of what you use is for the lambda can suspend map curly braces repo and reach it it's completely illegal. You can make a network call inside the map. That's not true of lists map and it's a generally useful difference. The marble lens show shows it source one emits 1 2 3 map it map it times two emits 2 4 6 saying count different values. Mapping to a UI model. Here is the most common real and use of map. Turning domain data into display data. We declare a shock UI a stock UI with a name preformatted price string and a boolean word for whatever it went up. Then we map our flow list of stock into flow list of stock UI. Three observations formatting belongs here. The domain keeps a double because that's what the price is. The UI gets a string that's already formatted with the two decimal places and currency symbol. Your recycle view adapter should never be doing string format. By the time data reaches it, it should be display ready. Two maps, two levels. The other map operates on the flow. The inner map operates on the list inside each emission. That nesting confuses people the first time. Read the types and it resolves. Flow list needs one map to go to the list and another to get to the stack. Still testable. feed the change flow of fake stacks and assert the stock UI object that came out. No Android, no emulator. Filter second operator filter keeps the value that match the predicate silently drop the rest numbers that filter it percent to equals equals Z stockflow that filter it is not empty. Two variants worth knowing. Filter not null obvious and filter is instance click which is which I flagged as gem. It narrows a flow event to a flow click filtering and casting it one in one step with the type system on your side. If you're working with a seal class or of events you'll use it constantly. Two properties many in few out. Unlike map filter changes how many values reach the collector. The marble lens shows it one, two, three, four in, two, four out. The other ones simply never reach the collector. Transform third operator transform the general one. Inside the transform block, you call emit yourself and as many as you like or not at all. Have stock flow transform emit emit if. So one input value up to up to three output values. Three things zero to yen to zero to yen out per input. Emit nothing drop a value once it transform is many times to expand it. Map and filter are both transform literally map is transform emit of it and filter is transform if predicate yet emitted. It's not a metaphor. It's how they they're implemented. Reach for the for it last. If map or filter express your intent clearly use those. The reader instantly knows the cardality transform is for when they generally can't take in fourth take in the the first n values then cancel upstream. So take the first n values then cancel everything upstream. There is a family take in take while drop in drop while two things and both matter. It makes an endless flow finite. This is the operator that lets two list and reduce work on live sources. Remember the danger list from lesson 118. Stock flow take five. Two list is safe. Stock list to list hangs forever. It cancels the producer after the end value upstream is canled. Look at the marble lane with take two. C and D are never produced at all. Not produced and discarded. Never produced. No wasted network call. No wasted computation. That's a nice property and it's the only it's only possible because the whole chain is a cooperating pip pipeline rather than a set of independent callbacks. Distinct until change. Fifth distinct until change suppress the value that's equal to the one immediately before you. So look at this lane source. You have source a a ba output ab three notes consecutive only. Notice the final A survives even though there wasn't there was an A at the start because it isn't equal to the B immediately before it. This is not distinct. It doesn't remember anything is seen. It remembers one value. It uses equals which is exactly why our stock is a data class. Remember lesson 114 where I said that equals comparing by value would matter later. This is later with a normal class equals reference references equality and every new emission is a different object and distinct until change would never suppress anything. This thing until change by compares one on one property instead of the whole object given great when your model when your model has a time same field that changes every emission but but that you don't care about that in our app. This is generally useful. Our fake source emits every second whether prices moved or not. And this stops the UI redrawing for nothing flow on dispatcher. Sixth. Six. And the one with the rule you must remember flow on. So have repository tax map flow on map and collect. Flow on affects everything above it in the chain and nothing below. The upward only rule is the entire operator. Read the comments. The two lines above flow on run on IO. The two lines run on whatever context is collecting. Three practical notes. Position is the argument where you put is the configuration. We can use it more than once for different stages and put it near the source right above the expensive part. Flow one never with context. Now the payoff of on promise from lesson 115 on the left. The mistake flow with context io emit load it compiles at runtime you get illegal state exception flow invariant is violated on the right the fix emit normally inside the builder and put flow on IO on the chain outside it and the reason it's worth knowing because it turns our memorized rule into an understand one understood one flow guarantees that emission rise in the collector's corin that's what makes flows predictable and what let collect be sequential with context inside the builder Tra are guaranteed by emitting from a different C routine. So the library detects it and refuses it. Refus it. Yes. Flow one changes the context properly from the outside by wrapping the upstream in a different C routine and channeling values across the boundary. Same intent correct emission never with contest inside flow always flow on outside it. Order changes cost. Now a ruler that cost nothing to follow and can save you a lot of work. Left map is expensive yet then a filter is valid. You transform 1,000 values and then throw 999. Uh so to throw 990 of them away a,000 expensive calls right we have filter valid then map expensive 10 then expensive calls identical output 100fold difference in work two cards narrow early put filter take distinct until change as high as high in the chain as corre correctness allows but only when the equivalent if the filter needs to inspect the map value filtering on a field that only exists after transformation then you have no choice and correctness beats cost every time. Don't reorder it mechanically. Rewarded when the two orderings are generally equivalent. None of this runs yet. Worth restaging it that because it's easy to lose sight of of after a lesson full of operators. Let's look at the chain filter map distinct until change flow on assigned to a variable called pipeline. Look admission so far zero thread use zero work done zero it's a description pipeline is a value you can pass it around store it in a field returning from a function it's reusable collect it twice and the producer runs it twice independently is still called a terminal start it collect launching has live data and nothing before that 20 operators no work that's the property that makes building these chains shape the working set a reference table seven operators that cover most of what of what you'll write map on in on out changes the value or or the type filter keeps matches drop the rest also filter it instance transform emit zero one or many per input the general case take and drop in limit or skip takes An endless flow finite and a cancel upstream distinct until change. Suppress a value equal to the previous one. Use uses equals flow one moves everything move everything above it in and onto another dispatcher on each per value side effect that passes the value through and change. There are dozens more combined zip the bounce flap m or last latest sample all useful none of them foundational. this seminar the full pipeline everything from this lesson in the view model we've been building since lesson 114 read it downward source narrow duplicate transform choose choose a thread handy to the UI six lines with callbacks and manual threading that's easy to understand and every one of those 100 is a chance to leak something or forget to post back to the main thread and notice the ordering follows the rule from 11. The two chip narrowing operators come before the expensive mapping. Every line of it is still called until the activity observed it. Four common mistakes. One side effects in map. Map state value equals yet works but lies lies about intent. Use on each for effects on map for transformation. And this one supervis code review and confuses somebody 6 months later including you. Two with contest inside flow runtime crash every time flow one is the operator. Three expecting this thing until change to duplicate globally. It comp compares against the immediately previous values only not everything he has seen. Four mappings before filtering. You pay the transformation cost on values you're about to discard. All right. I'm going to end this video here and I'll see you in the next. Welcome to this section. Everything so far has assumed the happy part. The network answers, the database is there, nothing throws. Real apps aren't like that. So this session is about failure. Where it comes from, how it travels through a flow, and what you can do about it. This lesson is the foundation, the catch operator. The next two lessons build on it with exception, transparency, and retry. So three places it can break. Start with where failures come from because the flow through is not specific. So flow throw something is not specific enough to act on in the producer the network calls call inside flow times out the database reads throws this is the common case by wide margin in an operator a map that pares a male for response a division by zero anything your own lambda can throw and your lambdas run inside the pipeline. So the exceptions are the flows exceptions in the collector. Your own collect blocks block sts updating a view does no longer exist for instance. And here is the thing thatifies them. Whatever it starts an exception travels downstream. It moves from where it was from toward the terminal operator. And if nothing stops you on the way, it surfaces there where it's either crashes your app or gets picked by the core routines exception handler downstream one direction. Remember that because it explain everything else in this lesson. Nothing is handled for you the default behavior. So you you we're clear on the baseline that print one and then throws a IO exception at the collect call and as you can see in this diagram three consequences values before the failure arrived normally the collector got the one everything emitted before the break was delivered then the flow is dead there is no resuming no skip one and carry on the fire fail flow is finished permanently the exception surface surface is at collect which means that It propagates into whatever cor routine called collect and from there into that cor routines parent and so on up the structure concurrency tree in a view model that cancels the view model scope wrapping collect in try first instinct for most people wrap it in a try catch block and I want to be fair to fair to this it does work anything throwing the producer or an operator will land on the catch but two problems It also catches your own collector. If render has a bug and throws a null pointer exession that lands in the same catch and gets reported to the user as though there were a network error. A UI bug is now indistinguishable from an offline device. That's generally bad. You'll chase it in in the wrong layer. It cannot recover the flow. You can display an error, but you can't substitute the value and let the stream carry on. The flow is over. So works but blunt catch the flow native answer is the catch catch operators have stock flow map catch curly braces three properties upstream only it sees failures from above is never from the collectors below that's the whole difference from try catch and it's the next slide it can emit the block is a flow collector so you can substitute the fallback value to the collector receive and the and the collector receive it receive is as though nothing went wrong. It's an intermediate operator called chainable part of the pipeline. It runs it reads as part of the chain instead of wrapping it. Catch only sees upstream. This is the single most important property. So let's be explicit. Left on the left we have flow, throw, boom, catch, emit, collect. The failure is above the catch is cast and then recovered. Right. on the right have flow catch collect and have throw boom the failure is below the catch now it's cut not your app your app crashes I want so it's not cut I need to explain that I'm going to repeat on the right we have flow then we have catch collect and uh so on the failure is below the catch no cut so it's not cut it's not your app crashes and I want to frame that correctly It is deliberate not not a limitation. A bug in your UI code should not be silently absorbed by your network error handler. Those are different problems. They have different causes and they deserve different treatment. If catch grabbed collectors exceptions, every UI in crash every UI every u every UI deserves a different treatment. If catch grab collectors exceptions, every UI UI crashes in your app would would quiet quietly turn into an empty list. And you'll never know. Practical consequence. Put catch as low in the chain as possible. So it covers every operator above it, but obviously still above the terminal emitting a fallback. Now the recovery part which is where catch earns his place over trying catch. Top lane the upstream AB then fails. Without a catch the collector sees AB and then an exception. bottom lane with catch emit those the collector sees AB and then a catch value and the flow completes normally two notes pick a sensible default empty list a cash value or an explicit error state your UI knows how to read how to render sorry how to render the last one is usually best because empty list and and we couldn't load anything look identical to the users to the user otherwise the flow is still still complete catch doesn't does not resume the source it if it was a value and then the flow ends that's a sharp edge and it gets its own slide shortly catch some retros others now every exception should be swallowed catch gives you gives you the cause so be se selective and expecting failures recover no network time out HTTP 500 there Normal operating conditions for a mobile app in the user should see a sensible screen. Unexpected failures reach row null pointer exception illegal state exception class exception soling those solo soing this highs bugs and makes them nearly impossible to find in production because your crash reporting never sees them. I'll put this bluntly. Catching throwable ball and doing nothing nothing is how bugs survive the production. Be specific about what you're recovering from. Where to put catch position since the so where to put the catch position since it changes the behavior. Left source catch map collect the catch only covers the source. If map throws, it escapes. Right. Source map catch and collect both are covered. Default position immediately above the terminal operator. The way it wraps everything. Move it higher only when the when you only when you deliberately want a narrower scope. And it's perfectly legal to have two catches in one chain and and an inner one that handles parsing failures specifically and an outer one for everything else. That's a real pattern, not a hack. Catch versus on completion. We meet on completion lesson 131 and people conflate the two. So let's separate them properly. What it does with the exception catch intercepts it on completion of service. So the catch is intercepting it and on completion is observing it. Can can it emit? Catch. So if it answers the question, can it emit then it should be a catch. On completion? No. Does it run when it's no error? Catch? No. Only on failure completion? Yes. Always success and cancellation too. Use it for catch recovering on completion cleaning up. And they're not rivals. Use both catch to recover and completion to hide the sniper whatever happened. which brings up ordering. We have on sack flow on each catch on completion and launching catch above on completion the exception is handled first. So on completion is the cause equals null. Usually what you want but the time you clean up the error has already been dealt with with on completion above catch on completion sees the row exception then catches handles it. useful if you specifically want to log the original failure before recovery. Both are valid. Know which one you written. You've written wrapping a result type not a pattern you'll see everywhere in model Android. Define a seal class loading success error. 10 use case map on star catch one flow three possible states and the UI that renders all of them with a single when notice how the pieces of the pieces we've collected fit together map rough success on start pretends preends not loading catch converts failures into expedited state nothing here is spe special error handling framework is four operators you already know this is the standard shape for a model Android screen and it's worth getting comfortable with it. Handling a dead network apply to our stock app. We have stocks use case map onear catch as live data offline is expected. Show message not a crash. Everything else retros should still be allowed but but know the warning. The flow stops after catch even though our source is still an endless while true loop. Once it throws the it throws and catches handles it the stream is over. The screen freezes on the error state and never recovers even when the network comes back. That's not good enough for a live feed which is exactly what retry fixes in lessons 130. Catch does not resume. Let's make that sharp edge explicit because it surprises people. Catch emits a replacement value and then the flow completes. There is no going back to the source and asking for value. The producer knows but no, there's no going back to the source and asking for value C. The producer is already gone. It's corine unwound. If you want to try again, that's retry, which resubscribers the whole upstream. Next, we're going to cover in the next lesson. And if you want the stream to stay alive through errors, that's a different design. Handle the error inside the producer and emit an error value rather than throwing. Then the flow never fails at all. It just emits a different kind of item. The those are generally different architecture architectures and it's worth knowing you have the choice. Five ways to get it wrong. Five pistols. Everyone compiles. One, catch above the failing operator. It only sees upstream. Put it below everything you want to cover. Two, catching throwable and doing nothing. You've hidden a real bug behind the fallback value. Three, expecting cash to resume the source. It emits once and the flow ends. Four, try catch around collect for upstream errors. It works, but also tracks your own collector's bugs. Five, catching cancellation exception. Catching deliberately ignores it. Cancellation is not a failure. That's not a bug. It's the design and is the subject of lesson 133. Four line checklist. Here is the habit worth building. Four four questions for every flow that touches a network or a database. Where where can this be realistically fail? So where can this realistic fail? Producer, operator or collector? Which failures are expected? Recover from those who emit which are bugs retro those. What must happen regardless? That goes in one completion. Four questions, 30 seconds of thought that most production error handling bugs simply never happen. Two problems left. Catch solves one thing well and leaves two up open. Why can a flow not catch its own emissions? There is a rule called exception transparency that forbids wrapping emitting try catch. Does the next lesson if it explains a runtime crash otherwise find completely baffling. How do I try? How do I try again instead of giving up? Catch substituses and ends. Retry resubscribes to the whole upstream and gives it another go with back off if you want it. That's lesson 130. All right, I'm going to end this video here and I'll see you in the next video. I'm back. A short lesson now. One rule and a runtime crash that will otherwise waste an afternoon of your life. It's called the exception transparency and is the reason you can't just rub a meat and a try catch even though that's the most natural thing in the world to do. So here is the code look at it and tell me what's wrong with it. Honestly, nothing looks wrong. It reads perfectly. Try to fetch and emit. If that fails, emit a fallback instead. That's exactly that kind of defensing code all train to write and it throws at runtime. Java illegal state accession flow excession transparencies violated the emissions from another detected cotton isn't being awkward here is preventing a category of bug that would otherwise be almost impossible to find let me show you which one a flow must be transparent the rule started properly a flow implementation must never catch an excession that was thrown by its own collector excessions travel downstream and they must be allowed to keep going three parts of that excession ations go one way from whatever they thrown downstream to the terminal operator never back up the chain. We established that last in last lesson. Emit is the boundary. Everything that happens after emit every operator below and the collector itself is downstream. Wrapping emit and try catch puts your catch block catch block in the wrong side of that boundary. The runtime enforces it. The library checks and throws rather than let you bug through it silently. One sentence to remember never wrapping a meeting in a try catch. What would otherwise happen? Now it follow the exception and the problem becomes obvious. So now follow the exception and the problem becomes obvious. Step one emit value. The producer hands a value to the collector and remember emit suspends until the collector is donuted. Step two the collector throws reader it hits a n pointer exception somewhere in your UI code UI code. Step three, that exception apps back through the emit call because the emit is the function that still is still that was still on the stack and lands you a catch block in the in the producer. Step four, you helpfully emit a fallback and there it is. A UI bug has been reported as a network failure. Your producer, a data source that knows nothing about views, has silently solid a crashing code it has never heard of and handed back a plausible looking fallback value. Everything looks fine. The list shows cache data and you'd spend the days staring at your network layer looking for a problem in a viewholder. That's the bug the rule prevents. Fix catch only the risky work. Let's first fix and let's first fix and it's a small edit left the broken version emit inside the try right the fixed version identical intent the try block now wraps only the thing that can actually fail the network call and the happens outside of it nothing downstream is inside the try so nothing downstream can be swallowed the rule is satisfied and the code arguably arguably is better too because it's Now obvious that the only failure being handled is the fetch. The second fix and usually the better one. Look at the producer throw. Look let the producer throw handle it downstream with the catch operator. See reasons with this is with this reason this is better. The producer stays honest. The his job is to report what happened if it's not the data source sourc's business to decide how the app should feel about a network failure. Different screens, different policies. One color wants wants to show a catch list. Another wants a full screen error with retry button. A third is a background sync that will just log and give up. And the source doesn't care and shouldn't have to. Easier to test. Assert that float throws. So assert that the float throws does the one test. Assert separately that the consumer recovers. That's another two small tests instead of one tangled one. I want to be careful here because people overcorrect after meeting this rule and start avoiding try catch inside flow entirely. The rule is narrow. Try API fetch catch fine no meet inside so nothing downstream is covered. Try finally clean up fine. Finally without a catch doesn't intercept anything it and it's the normal way to release the resource. If X emit A, emit B. Fine. The rule is about catching, not about control flow. Emit as conditionally as you like. Try emit catch the one forbidden shape and the only one that throws. So it's a precise rule about one specific construct, not a general bun. A runtime check, not a compiler one. Now worth understanding is why this surprises people. Now compiler can't see it. A try catch around the suspend call is completely ordinary cotlin. There is nothing in the tile system that marks emit as special. So there is no compile time check available. The collector checks at runtime flow tracks with corin and emission came from came from. If an emission arrives from a cont that doesn't match the invariant was broken and it throws. So it fails when it runs. And here is this this the the string often only the error part often only in on the error part your happy part works perfectly the code ships and the one one day the network fails your catch block fires and now you get an illeal state exception on top of the original problem. That's the real reason they internalize that's the real reason to internalize this rule rather than wait to meet it. The crash appears exactly when your app was already having a bad day. Why would contest was banned? Now, let me let me tie this to something from 150 because they're the same rule wearing two bahhats left the lesson 150 flow with context emit emitted from another cord in right from this lesson flow try emitex catch cut downstr exception. So cut a downstream exception both break the same guarantee the animation belongs to the collector's corin and the path from producer to collector must stay unbroken change the context and you move cor and you intercepted the path so invariant two ways to break here and you'll notice very similar error messages once you see them as one rule rather than two arbitrary rest neither one surprises you again three rules to carry practically there are three things one Never wrap a meat in try catch. Wrap the work that can fail. Emit the result afterwards. Two, let producer producers throw a data source reports reports what happened. It does not decide the up reaction. Three, handle fails with catch downstream where the customer is because where the customer use. Yes, because the right recovery generally depends on who's consuming. Follow this stream. You'll never see the transparency exception again. All right, I'm going to end this video here and I'll see you in the next video. Welcome back. In the last lesson, we said catch gives up. It substitutes the value and the flow ends. Sometimes that's right, but the dropped Wi-Fi packet isn't a permanent failure. And giving up on the first attempt makes your app feel far more fragile than it needs to. This lesson, retry, retry when exponential back off. And the ordering rule that makes the difference between working and silent and doing nothing. So catch gives up. Here is the gap. in two lanes with catch A arrives B arrives then a failure and the catch emits a fallback and the flow completes done whatever the source was going to produce next you'll never see it again with retry A arrives B arrives a failure and then the whole up the whole upstream is resubscribe then we have A again B again and onward the failure was a bump not an ending for anything transient time out a lost packet a server that has briefly overloaded The retrying is a much better answer than surrendering to a fallback value. And the two aren't rivals. A real pipeline usually has both. Retry above and catch below. Try a few times. If it still doesn't work, then fall back. Retry n. So three things. N it retries not attempts. So n is retries not attempts. Retry three means the original try plus three more. Four in total. That's off by that's off by one catches people. So that off by one catches people. So read it as three more G goes. It restarts the whole upstream. Everything above retry runs again from the beginning. Not just the failing operator. The entire chain above it. Then it gives up. After end retries, the exception is allowed through. It's allow true and continues downstream. Where this example catch picks it up. The marble lane shows the good case. Three failures then successes on the fourth attempt. The user never knows anything went wrong because flows are called. Here is something worth appreciating. Retry only worries because flows are called. Step one, the catch fails. An exception reaches retry from somewhere upstream. Step two, retry resubscribes. It collects the upstream flow again. A completely fresh subscription. Step three, the producer starts over. Flow executes from its first line. A new network call is made. That third step is the whole thing because the flow is a recipe recipe rather than a running process. Running it again is meaningful and a lot of a lot of a lot in a hot in a hot flow can can't be retried this way. There is only one shared execution has already happened. That's one reason retry leaves on the cold side of your pipeline below the source and above any sharing you add later with share in or share state in in section 50. upstream runs again. Now, right so the upstream runs again. Now the consequence of the whole upstream runs again because it matters enormously. Safe to retry flow emit API get stocks that retry three a get is in the pontent asking twice. It is harmless. You just get the data twice and only the successful one reaches the corrector. Think before retrying flow emit API placeholder retry three. Now imagine the request succeed on the server but the response was lost. Your client sees a timeout retries and place a second order then a third then a fourth. That's generally a bad day for somebody. So retry any read you like for a right you need either an indep independency key on the server so the back end recognize the applicate or a retry predicate narrow enough that you you're certain that the right never landed don't retry writes blindly retry with predicate which brings us to being selective retry and have curly braces cause I exertion Transient retry IO exceptions stuck sockets time out section HTTP 5003 a second attempt generally might work permanent don't HTTP is 404 a parse error a bad API key is trying 404 four times just watches battery and delay is showing the user the error they always going to get and the nice detail the predicate is a suspend function so you can delay inside it before returning true which is how Because you get spacing between attempts with the simple retry form retry when the general form gives you more gives you more two parameters the cause and the attempted number attempt starts at zero first try attempt zero second attempt one watch the off by one when you write your bound it's a suspend lambda delay inside isn't just legal is the whole point of having this form return true to retry false let the exceptional continue downstream. That's retryen and retry and curly braces both generalize into one operator. If you need the cause and the attempt count and a delay, this is the one exponential backup and here is the pattern you should reach for for by default for anything hitting a network. So I have stock flow try when I have cause attempt look at the weights attempt zero attempt zero waits one second attempt one waits two attempt two waits four attempt three waits eight why because if the back end is overloaded hammering every 100 millisecond makes things worse for you and for everyone else using the server backing off gives you the room to recover and jitter in production a little randomness on each delay stops every client in the world is trying to at exactly the same instant after an outage which is otherwise a lovely way to take your own server down twice. Always bound it. So and always bound it which is the next slide. Pitfall retry above catch below or ordering and this is one this is not a style preference. It changes whether your code works at all. Correct. source retry. Catch emit the fourth times and if it still fails, fall back. Wrong. Source catch emit empty retry three. Retry never fires. Why? Because catch has already consumed the exception and emitted a fall back. By the time the stream reaches retry, there is no failure left to react to from retry. From retry point of view, everything succeeded. And notice how quiet that failure is. No crash, no warning, no error. Your retry logic simply never runs. and you won't find out until the you're debugging why your app gives you in gives you instantly on a flaky connection resilient stock feed everything from section 13 and 14 in one chain have stocks retry when map on start catch on completion as live data read downwards try try up to four times a growing delay success successes in success state show of spinner first fall back to an error state if it never recovers and always clear the spinner at the end. Fix operator does generally production grade error strategy and it's readable in about 10 seconds. Four ways to regret it. Retry is the it's easy to misuse and expensive when you do one one unbounded retry. Retry went through against the permanently broken endpoint loops forever drowns the battery and never shows the user an error. Always bounded. Two, retrying non in the indempant right for order orders placed because the response was lost independency key or don't retry. Three, retrying without a delay for instant attempts against the server does down achieve nothing except for failures in the same second. Four, catch above retry in the exception if the exception is consumed before retry sees it. So retry silent tree never runs. bounded, delay, selected, and above catch four properties. Check it all four. All right, I'm going to end this video here and I'll see you in the next. Welcome back. This is the final lesson of this section and is the longest because cancellation is the ending that happens most often in a real app and is the one with the most quiet traps. We'll cover how it actually works. Why is cooper cooperative the loop that can be cancelled and why catch ignores it and the try catch that makes a cor routine immortal. Three ways a flow ends. A quick recap of where we are. Completion the producer run out of values and return normally. Covered. Failure. Something true. Lesson 138 132. Covered. Cancellation. Somebody stop it from outside. That's today. I want to point out something about frequency. Completion, failure are the ones you think about when you're designing, but cancellation is what actually happens most in every app all the day. Every time a user closes the screen, presses back or navigates away is the normal ending, not the exceptional one. You cancel the core routine. First thing to internalize there is no flow that cancel. And then and there doesn't need to be. So you have val job stack flow on each render it launch and you have view model scope job cancel this one collection stops every collection in the scope so this one collection stops every collection in the scope stops three points the cor routine owns the flow a collection lives inside the coroutine cancel the cor routine and the collection stops there is nothing else to cancel it's cooperative Nothing is still is killed or interrupted. The corine is marked, cancelled and stops it suspension and stops at its next suspension point. That's the next slide. Cleanup still runs on completion finally blocks a weight closes in a call back flow. All of them fire. It waits for a suspend. Let's make cooperative concrete because it's the source of two later traps. Follow the timeline. Emit, cancellation, check, fine, carry on. Delay, check, fine, carry on. Then cancel is called from outside. The corine is flagged. Nothing stops yet. And the next delay and that throws cancellation exception. So cancel only sets a flag. The corine keeps running until it reaches a suspension point. Emit delay a network call and that point is where it throws which has an im an immediate coralary. A suspension free loop cannot be cancelled at all. That's the next slide. And it's one of the nastier bugs in this course. The uncancellable loop. Look at the left hand here. You have flow v I while true and you have hairy v heavy CPU work I ++. There is no emit, no delay, no suspend call of any kind inside that loop. So there is never a moment where cancellation can be observed. The user closes the screen. The view model is killed. View model scope is canceled. And that loop keeps burning a CPU core until process dies. No error or no warning. Just a phone that gets hot and a battery that drains. The fix is on the right. Corin sorry current corine context that ensure active call that once per iteration. It throws cancellation exception. If it if the corine has been cancelled and does nothing otherwise that's it. one line and the loop cooperates. Rule any CPUbound loop inside the C routine is an issue active function. Some builders do not check a subly that is rare but buffling when it bites. So a sub a sub a subity that is rare but buffling when it bites. Flow checks for cancellation on every emit. So most of your code is already fine. That's why you never hit this. But some builders take a fast path and skip the check for performance to have the range one to 1,000 as flow is the usual example. A tight loop over a range where the check would cost more than the work. So a cancel collection over a large range can run completion anyway after cancellation. Sorry, my slides. So a cancel collection over a large range can run to completion anyway after cancellation. The fix is the cancelellable operator 1 to 1,00 as flow cancelable collate. It has the check on every value only reach for it or if you actually observe the symptom but it's worth knowing the operator exists because otherwise the behavior looks like a bug in the library. It's an exception. Now the mechanism because it explains the next three slides. Cancel. Cancellation exception unwinds the core routine. It's a normal exception. Mostly it's thrown. It propagates up the stack. Finally blocks run on the run on the way. Nothing exotic. But the machine treats it as a normal. The corin framework specifically does not report a cancellation exception as a failure. Canceling is not crashing and your parents scope isn't torn down because a child has canled. So don't catch it. Catching is it and carrying on breaks. Carrying on, breaks structure concurrency in ways that are generally hard to debug. And we're about to see exactly how catch ignores cancellation. Here is the nice consequence. The catch operator deliberately ignores cancellation exception. So if you have stock flow does catch emit fallback launch in view model scope. Choose the screen and that block does not run. And that's exactly right. A closed screen is not an error. You do not want an error. Something went wrong. State flashing up because the user navigated it way away. Without this behavior, you would have to write if E is not cancellation exception in every single catch you ever wrote. A plain try catch is not polite. So catch exception around a collect will catch cancellation exception because cancellation exception is an exception and that breaks cancellation. Which one is more solid reason? Which which one is more solid reason to prefer the operator? It understands corines. A generic try cash does not solid cancellation. And here is the worst trap in this lesson. This is how a corin becomes immortal. You have the code while true try delay emit catch trace it. Cancel is called delay throws cancellation exception. The catch blocks catches it because cancellation expression is an exception. logs retrying and the loop continues. Delay throws again, catch again, log again forever. The screen is gone. The view model is destroyed and your C routine is sitting there in an infinite loop making the network calls, holding reference, writing to a log nobody reads. The fix is on the right. Catch cancellation exception first and retrow it. Then handle everything else. So catch e cancellation exception. If you write a generic catch e of type exception anywhere inside the corin that retros needs to be there on completion sees it. Catch ignores cancellation on completion does not it sees everything on completion cause when cause equals null causes cancellation exception else. Three endings three branches one hook. This is the shape to use when you need to distinguish the user left from it broke for analytics for login or for releasing a resource and notes that the order matters in that when check cancellation exception before the generic health or cancellation gets logged as failures they can cancel upstream something or noticing cancellation isn't only to a flow from outside operators use it as a normal tool take to Take two on a flow A B C D rather after B arrives take cancels the upstream it's real cancellation the upstream C routine receives a cancellation expression so on completion up there runs with a cancellation cause this isn't a special case it's the same mechanism C and D are never produced not produced and discarded never produce no wasted network calls first does the same one value then cancels which as we said back in lesson 118 is exactly why first is the is safe and and on an endless flow cancelling after a deadline. Two ways to say give it give it it give it give it if it if it takes too long on and they measure different things. So two ways to say give it give up if this takes too long. So I'm going to repeat that. Two ways to say give give up if this takes too long. And they measure different things with timeout 500 5,000 flow collect bounce the total dash duration of the whole collection 5 seconds after you start whatever is happening is throw timeout cancellation exception flow time out 5 seconds. The flow operator it bounds the gap between consecutive emission. As long as a value arrives at least every 5 seconds it never fires. That distinction matters for one shot network request with timeout is right. The whole thing should take under 5 seconds. For a live feed like our stock ticker with time out would kill it after 5 seconds and perfectly healthy operation of a perfectly healthy operation. What you actually want is there is time out fires the feed goes quiet. So fires if the feed goes quiet peak based on what you're bounding the whole operation or the or the silence suspending during cleanup. One more mechanical detail and it catches people writing cleanup code. It's in a cancelled code routine. Every suspend call throws immediately which includes the suspend call in your cleanup block. So this fails on completion the beta save drafts suspend call throws instantly. The fix on completion with contest non-cancable the DB save draft. Non- cancelable is a contest that says this block must be allowed to finish but use it only for short cleanup work. Wrapping real work in non- cancelable is precisely how you build code that cannot be stopped. You've reintroduced the immortal C routine problem. Just more politely clean up only and keep it quick. Who cancels what the in practice you rarely call cancel yourselves. Here is how does it here is who does it for you. View model scope canceled in one cleared. The view model is destroyed for good. Survives rotation. Life cycle. Scopes canled in one destroy. Fires on every rotation. Repeat on life cycle. Cancel on one stop and restarts on start. Add life data. cancels on one stop plus the 5second time out from less than 120 taken first the chain cancels itself once and it has in and it has what it needs and that point of of the that table and the point of that table choosing the right scope is how you choose your cancellation policy that's the actual decision everything else follows what happens on on back let's take it through your own app one The user presses back. The activity finishes and the view model is cleared. Two, the scope is cancelled. View model scope cancel. Cancels every C routine its. Three, the cancellation stops. Our collector is sitting inside delay and that delay throws cancellation exception. Four, the producer unwinds. The while through loop exits on completion runs with the cancellation cause and everything is released. Four steps and zero lines of code from you. That's what structure concurrency buys and it's worth posing on. Not one flow in this entire course has needed a manual unregister remove listener or a dispose. Compare that to the callback based in Android code you might have written before. How to leak. Anyway, three ways to defeat all of that. One global scope. It has no parent and is never cancel a flow that launch three tree three there. So a flow that launch there outlies every screen in your app and the only thing that stops it is the process dying. Two, so cancellation exception. The generic catch e exception inside the loop from slide nine makes the core routine immortal. Three, CPU loop no suspension point. Nothing ever checks the flag. So nothing ever stops. Audience sure active. Every real flow leak I seen in one of those three. If you're hunting one, start there. Five questions. The habit asked of any long live flow before you ship it. Which scope owns this cancellation? View model usually. Does the producer suspend regularly? Emit and delay both count. I am catching exception anywhere. If so, the retro cancellation exception first. Is there a clean up to do? One completion plus one cancelable if it suspends. Should it stop when the screen is hidden? That's repeat on life cycle less than 137. Five questions. Most of of the time all five answers and five five answers are already correct by default which is the point. You're checking that you haven't accidentally defeated the defaults. The ones five traps restated the ones that cause the most time to diagnose. Catch ye exception around collect catches cancellation exception two retros it explicitly. We throw explicitly. Throw it explicitly. A while loop with no suspension point can be can't be cancelled. Add current in context that insure active expecting cancel to be instant. It's cooperative. The corine stops at its next suspension point which might be a second away. Suspending in cycling up throws immediately unless rob and with context non-cancable. Treating cancellation as a failure. It's normal ending. Don't show the user an error screen because they press back. Number one is by far the most common and by far the hardest to spot in code review because it looks like careful defensive programming. Section complete. That's section 14. Catch transparency retry cancellation. Failures are handled declaratively. Catch, retry, and on completion are operators in the chain. Not try block scatter through your view model. Cancellation is automatic. Pick the right scope and structure concurrency does the rest. But one collector still everything in sections 13 and 14 has assume exactly one consumer per flow. Every collect gets its own private run of the producer which is what section 15 changes shape flow and state flow. One execution many collectors and that turns out to solve the rotation problem we've been quietly living with since lesson 117. All right, I'm going to end this video here and I'll see you in the next
Generated algorithmically for Search Engine
Indexing.