Kotlin Tutorial for Beginners: The Kotlin Programming Language Full 9-hour Kotlin Course

Video Statistics and Information

Video
Captions Word Cloud
Reddit Comments
Captions
- Hey everybody. My name's Donn Felker and welcome to the Kotlin programming language course. I'm gonna be your instructor on this journey. You're watching a nine hour plus long video of the Kotlin programming language. Now, there are two ways to watch this. You can watch this nine hour video if you prefer, or if you prefer to view this in a more playlist like fashion with smaller bite-size lessons, there is a playlist that's linked down below in the description. You can click that and watch the same exact content just broken apart into over 130 different lessons. The goal by the end of this course is that you'll be able to start providing value on any Kotlin project that's out there. We'll cover things such as learning variables, functions, scope classes, how to work with generics, how to work with lists, manipulate lists, mapped various different data structures, arrays, and we'll even talk about lambda expressions a little bit. We'll wrap up the course with you creating your first Kotlin project and be able to execute it locally on your machine. When you're done with this course, you should be able to walk into any Kotlin code base and feel okay and familiar with the code enough to start providing value. Now, there are things, of course, we won't be covering such as very advanced topics, such as code routines and flow and things like that. However, once you have the solid fundamentals covered that are in this course, you'll be able to hop right into those topics with ease. Now there's one last thing I wanna let you know before we get going. The installation procedures of this can change drastically, depending upon when you're watching this video, which means is when you install the Kotlin IntelliJ community edition, which is covered in the first few minutes of this video after my introduction here, it may look a little bit different. The webpage will probably look different, the installation might look a little different. And the first page where you start creating a new project might be a little bit different. So what I recommend is once you get IntelliJ installed, following the instructions you see on the website, create a new project and make sure you just select Kotlin. And then from that point forward, everything should flow smoothly for you. Now, I look forward to you seeing you at the end of the video to congratulate you. Until then if you have any questions, feel free to reply below, and I'll see you at the end. Good luck. To get started with Kotlin development, you'll need an integrated development environment, also known as an IDE. You can get one for free@jetbrains.com by going to tools IntelliJ. Next, find the download link and click that. From here, you can download the community edition, which is free for the platform of your choice. From windows, Mac OS to Linux. I'll be using Mac OS in this course, so I'll download the community edition. Once the file is downloaded, you wanna go back to the download page and follow the installation instructions for the platform that you're running. I'm not going to walk through all the installation instructions for each platform as they do change from operating system to operating system. And at times the installation instructions also changed themselves, which brings me to the website that you're viewing now on your screen may be different from the website that you visit in your browser. If that's the case, be sure that you just find IntelliJ find the community edition and find the installation instructions. Once the application is installed and you're ready to use IntelliJ, you can start developing with the Kotlin programming language. Once you have IntelliJ installed, you'll start the application and you'll see a window that looks something like this. You'll click create new project and you'll have a list of various different options on the left here. If you wanna go down to Kotlin and select Kotlin JVM, click next, go ahead and give it up by name. So we'll call it Caster Kotlin. You can give it any name that you would like. You can leave the project STK, the libraries and everything as default, and we'll click finish. You can go ahead and hit close if you receive a tip of the day and you can close these windows down here, these little notifications. On the left-hand side here, you'll see that you have what's known as the project structure window. So we can view the project packages and various different sustains inside of this window. The first thing that you wanna do is expand this folder. You'll see an SRC folder. This is where our code is going to go. So to get started, let's create our first file. Right click on the folder, go to new Kotlin file class and let's just call it main.kt. KT is extension for Kotlin files, click okay. At this point, you're ready to start writing your first line of Kotlin. If you receive a notification like this, you can choose to install the new Kotlin plugin if you'd like. If you do not receive one of these new notifications, you can go ahead and disregard this part of the video. You will receive these types of notifications when new versions of Kotlin and the new version of Kotlin plugins have been released. You can go ahead and click install. It'll download the new plugin in the background here, and it'll show you the progress here. This is the plugin that's for IntelliJ here. You can actually see these plugins by going to preferences. And then you go to plugins. Go to preferences, you type plugins here. You can see all the various plug-ins that are installed inside of IntelliJ here. And you could search for Kotlin and you'll see that we have just recently installed it, so we would need to restart the IDE, as you can also see through this window down here. After you click on the restart button, IntelliJ will restart and the new version of Kotlin will be loaded. We can close this window and we're back in our main.kt file. At this point, the Kotlin plugin has been updated inside of IntelliJ and you're ready to go. To create your first hello world application in Kotlin, what you'll do is create a main function in your file. And that's just gonna be fun main. You have open and closed parentheses and then open and close curly brackets. And anytime type this code inside of IntelliJ in any Kotlin file, you're gonna get this little play button here. And with this play button will do allow you to run it, debug it and so forth. And we'll talk about that here in a second. Now, if we were to run this here, we would get nothing on the screen. So what we wanna do is we actually wanna print something to the screen. We wanna print the words, hello world. Now the print ln function is actually built into Kotlin. This allows us to print a given message and align separator to the standard output stream. The standard output stream at this point is just gonna be inside of our run window down here. So if we go back to our main button, hit play and hit run main.kt, you can see that compiles and it completes. It may do a little bit slower on your machine, depends on the performance of your machine. And then here in the output, you'll see, hello world. And with that, you've created your first hello world in Kotlin. Variables are the underpinning of your applications. To create a variable in Kotlin, you'll use the var keyword, and then you provide the name of your variable. In this case, we'll call it a full name and then you set it equal to a value. Here, I'm sitting at equal to a string value of Donn Felker. We now have a variable called full name and its contents are Donn Felker. If we wanna print this to the screen, we can use the print ln command, and then we can go up and run it from this play icon and hit run main KT. It'll compile, as we can see down here, and the output will show as Donn Felker. Now, I run this. We can see Donn Felker and John Felker. We can also set this value tool to a variable that's empty and print this out as well, even though we're not really gonna see anything, what they full name that's empty, we can run here, we'll see, just kind of an extra line breakdown here. Instead of one, two, there's now two line breaks because we have an empty one here. And also working with variables once we have access to them, we can start accessing properties of these variables. So let's say that we wanted inspect some properties of the full name and we wanted to see if it was empty. I can say is empty and that's a method that's on this property. So we can check to see if the full is empty. I'm gonna hit command B to go to the declaration and we can see that it returns this method, which is called is empty returns true if the char sequence is empty. So char sequence a string. If this is empty, it's gonna return true, otherwise false. So if we run this, we'll get Donn Felker, John Felker, a blank line and then we're gonna see the word true. So we'll run that here. And then you can see in the window down here that we see Donn Felker, John Felker, blank line, and true. The key thing to remember with variables that use the word var is if you need to change them, if you need it, change of variables at any time, you'll want to use a var. If say age equals 32, we can reset it and there's not gonna be any problems whatsoever. I noticed one additional thing. If I were to type the word var again on age, we get a red squiggly line. And what this says is there's conflicting declarations, meaning we cannot define the variable age twice within the same scope here, which is inside this main function. So if I wanted to reassign it, I wouldn't say var again. I would just simply say age equals 32, and then I can reassign it. I can also print this out to the console as well and type in age and we'll see that the age is going to print 32 is initialized it with 30, reset it to 32 and now it's gonna show as 32. One final note is that the age variable was also implicit to an integer. So again, we did not specify a type. The compiler was able to determine based upon what we set this to, that the value is an integer. You can create mutable variables in Kotlin with the var keyword, but if you'd like to create read only variables in Kotlin, you need to use the val keyword. So let's try the same thing that we did before. We're gonna create a new variable, but this time we're going to use the val keyword, and we're gonna set it to Donn Felker like we saw before, and we're gonna print this to the screen. And at this point, nothing has really changed. The output's going to be the same. We've created a variable called full name, it contains the contents, Donn Felker. And when we, we run it, we get Donn Felker down here at the bottom. However, the big difference here is when we use the val keyword, we're creating a read only variable, which means I can only assign it once. Therefore, if I want to assign the variable again or I attempt to I'll change this to John Felker, and then we automatically get a red squiggly and IntelliJ because IntelliJ Kotlin plugin realizes there's a problem. And what is saying is the val can not be reassigned. So even if I say, "Hey, know what, I'm gonna ignore the IDE "you want it to run a compiler." I try to compile this, we're gonna get a compiler error. It's gonna say, "Hey, look, we couldn't compile this "because there's an error." Vals cannot be reassigned here and it's on line number five. So we go over here to line number five, and there's our problem, full name cannot be reassigned. So once we have a variable that's created with the val keyword, we can not reassign it. And the same goes for other types too. So if we were to use age and say 32, that's fine, you can print that to the screen as well. And then if we tried again to reassign val, we could say that's 42 now, we could see that this is not going to work. And again to verify it, we'll run, which will fire off the compiler will say, hey, that the val cannot be reassigned. We're on line eight right here. That's what this first number is, the line number. And we're on line eight and its val 42 cannot be reassigned. So we cannot reassign vals, so these are read only variables. These are great for things that you do not want to change in your application, you don't ever expect them to change. And you'd like to verify that and enforce that through the compiler. Up until now, all variables have been implicitly declared. Therefore, if we have a variable and we call a full name and we set it equal to a string equivalent to Donn Felker, the type is going to be a string. And we can actually verify that by doing a little bit of inspection here on the actual type. So I print the full name and do colon colon and tell it, "Hey, what class does this full name actually belong to?" And if we run it, we can actually see that it comes back as a Kotlin string. So we're working with a string here. Now, there are times that you would like to specify the type of the variable explicitly, and you can do that by providing a colon after the name of the variable and then providing the type. So the here would be string. I would see Donn Felker. If perhaps I put the wrong type here, perhaps I tried type integer and then I set it equal to Donn Felker, we get actually an error. And it says, well, the type is mismatched. We require an int because the full name variables and so you're trying to set a string, that's just not gonna work. And if we try to compile it by skipping the IDE, it's gonna say the same thing. The inferred type is string, but the actual, but int was expected. So either we have to change the explicit type or we need to change their value here. So for example, if we wanted a age and we wanted to explicitly provide the type, we would provide the type here. So anytime you'd like to provide the type for either a val or a var, you just have to type the variable name, colon and then the type that that's going to equal to explicitly provide the type to your variables. Kotlin has many built-in types. Let's go ahead and explore the number types. The lowest number type is a byte. So I'm gonna create a variable called myByte and it's of type byte and I'm gonna set it to a value of eight. Bytes are equivalent to eight bit signed integers. The next one up is a short, so I'll call this one myShort and will explicitly provide the value of short. And I'm gonna set this one by two equals 16, and this is a 16 bit signed integer. So as you can see, as we move up, the numbers are going to get bigger, meaning that the values that they can store are going to be bigger, which brings us to the most popular, probably in regards to program that you see, and that's going to be the integer value. And this is going to be equivalent to a 32 bit signed integer. Next up we have a Long. Now long is a very long number, so it's kind of a good name for it. It's a long number that you could use, and that's going to be equivalent to a 64 bit signed integer. There are gonna be the most common types that you use when you're working with numbers. So you're gonna be using these. And most often you're gonna be probably be using an integer value as it will cover 90% of your use cases. If you need to use a long number of sorts of very long, then you wanna use a Long. And those times a new do wanna use that, perhaps you're storing numerical values that are very lengthy. However, there are also times when you do need to store decimal value. So if you do need to score decimal values, there's also something known as a float. And so say like this, a float, and we're gonna say this one is equal to 32.00 and I'll put an F here 'cause that means in literal terms that this is a float. And what this thing is equivalent to is a 32 bit floating point number. So, this is a smaller decimal we can use. Now, if you need to use a larger decimal, you're gonna use a double and that's gonna be called double and that's equivalent to 64 and it's gonna be a 64 bit floating point number. So, these are kind of the basic numbers that you're going to use inside of your day-to-day application development. Now there's something interesting about each one of these. If we go to the definition of any of these, and I'll start with byte, we can see that byte extends number. We can also go back to the previous file short, when we'll look at that, it also extends number. And as we walk down, each one of these, you're going to notice that every one of them from integer to long to a float, to double et cetera, all of them implement the number abstract class. So let's go take a look at that again, what does that look like. If we go to the number implementation, we'll see that it has a bunch of functions on here that we can access, which is interesting. Which means if we go talk about this for a second, that every time, every object, everything in Kotlin is an object. So even this integer value is an object. This long is an object, this byte is an object, which means that we can call methods and properties on these. So if I want to print ln the, myByte. Perhaps I want to perform division operation. There's a bunch of map operations you can perform on here. These are built into there from the various different types. If I want to say to double, I could say to double, and that would turn my bite into a double. And this actually comes from the number class, remember. If we go to the parent declaration, which was command do, we could see that this came from two double and so it returns a double. And so it would take this byte and turn it into now a double. So if we were to print this and let's get the type information by providing colon, colon class, and then running it, you would see that it does return a double. Now, if we were to come over here, we could actually change this to a long. We could run it again and you see that returns say long. So here we go, eight and we forgot to provide the values there, say class, run it again. And we'll see that we now have class Kotlin.long. When working with certain numbers, such as longs, we can take and we can use the literals and we can say big long, let me say this is a long value. And then we can actually do some cool syntax. And so what this does is it's built in syntax to Kotlin that makes it easier to read. So this is equivalent to one million. So if I were to print line and we were to print this big long in here and run it, we would actually see that this is just going to print out as one million, but it makes it just a lot easier, these underscores do, because you can read it. This is very valuable if you have a constant in your application that you know is always gonna be one million, or it's always gonna be 1000 or something like that. You can make this a lot easier to read, because let's say you're gonna go for one billion, this is much easier to read than this because you're not sure by looking at this, is that one billion or is that 100 million? It's really hard to tell, but just by adding these simple separators, we can actually tell real easily that that would be one billion. One of the additional things that we can do with numbers is we can of course add them together. So we could say my int. I'll say new int equals my int plus 12, and that would equal 44, but we can print that to the screen and say new int. And when we run this, we see down here in the bottom that we're going to get 44 right there. But we can also do an additional thing by using the built-in methods. Plus, I'm gonna say 12. Now we can run that here. So there's some nice syntactic sugar to say plus, we're gonna get 44. We can also do minus and we can run this and you'll see that we also get 44. And there's a bunch of other map operations that we can inspect inside of here by simply providing the dot to get the code completion and implement them. And so if you're not familiar with a much of these, you can hop into there. And let's say, for example, you're not too familiar with the word and is, you can take command B and go to that and say, we perform a bit wise and operation between these two values. If you wanna do bit rise map, you can do that with the and, you're could hit plus, you can do multiply or times and then you could run that. So 32 times 12, it's gonna run here by 384. So these are the basic types from smallest to largest. We're gonna start with byte, short, int, long, and then we'll get to decimals, you have float and you have double. These are the built-in number types in Kotlin. In Kotlin, strings are also known as objects. So if we stick with our traditional example of the full name and we explicitly define the type of string and set it to Donn Felker, we now have an object known as full name that contains the value of Donn Felker. We can call different methods and properties on the full name variable by typing dot to get perhaps the length. This will provide us the length here. I'm gonna duplicate this line by pressing command D. I can also get the actual characters which are gonna provide us a extreme of characters. I can do a whole bunch of different other things on-site of here as well. So for example, I can decapitalize it. So let's do decapitalize. And if I were to run these, we're gonna see that we have Donn Felker and then decapitalize was decapitalizing this, if this was a sentence, so I could say, hello world. If I were to run it again, we can see that the sentence Hello world is decapitalized by decapitalizing the first letter. If I realized I wanted everything to be lowercase, let's say two lower case and I could say run, and then that would actually allow me to create this as all lowercase. Strings are built inside of Kotlin with double quotes. So we could just say, first name is Don, and that's a double quote. There's also another type. Anytime we're working with like characters, we can then use what's known as a char. So I could say file C and that could be of type char equals X. Hold on a second, here's the weird thing. It's only a single character, why is it complaining? That's because in Kotlin, if you want to define a character, you have to define it with single tick quotes. So single quotes allows you to implement a char, which has a single character representation. Now, there are some things that are some caveats to this. So there's also characters such as the tab character that you have to escape with a slash T, there's the end for the new line and of course the double slash for backslash and various other different types of escapes that you're going to try to do. In Kotlin, a char represents a 16 bit unicode character. So anytime you need to use a single character, you're going to wanna use char. Now this doesn't limit you from also using a single character string if you would to. You could say string foo equals Y, and this would still work. You could still work with a value of string. However, a string is going to be larger in memory, than a character. But you could still use a string if you'd like to. However, if you know that you're only going to be working with single characters, then you would use the char type. Sometimes when you're declaring your strings, you need to declare them in multiple lines. So I might say, I have a message here and this message would be hello, I put a new line here. My name is Donn Felker, how are you? Then I would print this to the screen. And then once it's ran would see a nice little message here, Hello, my name's Donn Felker, how are you? However, sometimes this gets kind of ugly and there is actually a better way to do this in Kotlin, and you can actually use the triple quotes here. You can say, hello, my name is Donn Felker, how are you. And then I'm just gonna run this again. And I'm using the shortcut key to run it, which if you see here is actually just going to be a control+shift R on a Mac and you notice the same thing came out here. Hello, my name is Donn Felker. But what does this trim indenting? What is going on here? This is basically a rostering triple quoted and then we have the trim and dent function, which basically, if we look at it, does it detect the minimal indentation of the input lines and removes it from every line. So if I remove this line, what you're gonna see down here is this is actually going to be indented. It's actually gonna have a new line, and then it's gonna be indented. So let's watch this, we'll run this again. And you can see there's a line above and it's all been indented. So if I put the trim indent back, what it basically is doing is removing all of this and the new line that's up here. I'll rerun it and it shoves it back in there. Now I can also do something called replace indent. Like, for example, if I know that maybe I'm building some string and I'm kind of building this stuff up manually, I can replace that indent with something that looks like ABC, and some, maybe a pipe symbol or whatever like that. And if I run this, it'll replace that indent, which was all those things with these on each line. So ABC hyphen pipe, hyphen, which is kind of cool. So if I'm building something, I can actually kind of provide these pre formatted things there. I can also say my trim margin and I can provide a prefix. So maybe I've decided that I already know that I just want that prefix to be these three arrows. So I'll run that and it will run and it says, "All right, well, I didn't trim anything "'cause I didn't find these three arrows." So what this means is these three arrows would need to be right here. Let's say for whatever reason, that string had these three arrows. We'll run that and there we go. Now it's not gonna print those. If I remove this, we'll actually see a print with those three arrows and this area here. Now by default, trim margin has default itself to pipes, which means I could set this to a pipe. I could run that and there we go, it's just gonna replace those pipes by default it checks for pipes. So you can do a nice string here, multi-line string with triple quotes. Another thing you can do is let's assume that you have a name and we'll just call it Don. And sometimes you want to print a message like we have here. Let's print this message, Don. Let's say, we wanna say hello to Don. We'll say hello traditionally in most languages, you just do string concatenation, which works fine. We'll hit Enter and it says, hello Don. Now it works fine. Now notice something, there's a little squiggly here. It says convert concatenation to template. So you can actually use string interpolation in two templates. So I hit alt enter there and it gave me this pop up and I hit enter again and automatically converted this code for me. And this does the same exact thing. And now what this does is actually prints Hello Don. So it's actually one big string. And then we could actually have one here to say age, and we'll just say 32. And it would say, hello, hello Don, it'll name, your age is and we put this thing in here to age and it would print the age inside of here. And now one other thing that we can also do that's cool inside of these things is say something like this. So hello name, your age is, and your name is, we would say dollar sign. We put open curly braces 'cause now I need to call a property or method and that's a name dot. So name.length. And that's what this says is it would say, Hello, Don, your age is 32 and your name is what, before care actors log. So let's run that. So hello, Don, you're age 32 and your name is four characters long. So now instead of using a very complicated replacement or anything else like that, I can use a string interpolation and interpolate these variables. If you're not going to be calling into the object graph at all, so you're not doing this dot anything. Then you can just call dollar sign variable name. Otherwise you need to do dollar sign, open curly bracket, put your code and your expression inside here and close curly bracket and that's it. Last, but certainly not least is the Boolean value. And we could specify one like this. So we could say is blank. It would provide us a truer val false value, and we'll just initialize it to false. And so this just basically has two options and that's true or false when we go to the implementation. Again, everything in Kotlin is an object. And so there's some other built-in operators here. So we can do the inverse of that. So if we wanted to know, for example, let's just print this. Print ln is blank and then we can just do print ln is blank, and then not, and this will print the inverse of it. If we run it again, we see here in our output window, of course we had false and then we have true. So we initialize it to false and then true. We can also initialize it, of course, using other variables, such as a string, so it will say is blank. And then we can use a string. So we go blank, that is blank. So function is blank on a string. So this is a char sequence function that checks to see if they link the zero and return the Boolean value. And if it's zero, then it's going to return true. So we can print this off here. I'm just gonna copy this and then we could see the value true or false. So we'll run this and of course it's blank of course. If we change this to two first name, Don, and we run it, it's gonna end up false. Then it will say false, of course we did the inverse, which is not. And there's many other functions on this Kotlin object that you can also inspect that you can do, which is gonna be or, X or et cetera and a bunch of other ones inside there. You can check them out by navigating to the Boolean section there. But if you need something that represents a true or false condition, you'll want to use Boolean. Working with conditionals is a very common thing that you'll do in the Kotlin language. Let's assume that you're building an application which will track your calories. And let's assume we start with 2,500 calories and these would be the calories that we consumed already, or the user has consumed in their tracking for their diet purposes. Now let's assume that we had a requirement that says, if the calories that have been consumed are over 2000, then we have to print a message to the viewer or the end user to let them know that they've eaten enough, they've consumed all of their calories for a day. So, here we have see that they've consumed all their calories for a day. Now, if we run this, you're gonna see down here in the bottom window that we can see that they've consumed all their calories for the day down here at the bottom. Now, if we were to change this, let's say to 1900, we're gonna notice that the line of code here on line four does not get run because the calories are less than 2000. So that means we're not gonna print this line. So let's say for some reason, we wanted to check to see. Otherwise, if they haven't eaten all 2000, we can say something else like, okay, you still have some calories left. So I'll say print line. And if we were to run this, we would see now that you still have some calories left. So we know that again, this is a conditional, if conditional, and this is the else portion. So if there are over 2000 calories consumed, this line of code is gonna be printed. Otherwise, else we're going to print this. So any other types of, if it does not match this condition, so anything less than 2000 is gonna show up here. So we even just put this to 2001 and rerun this. You're gonna see that we have consumed all of our calories for the day. Now let's assume that you're working with someone and they decide, or you decide in your app that you would like to make sure that if they've consumed over 1500, then what you would like to do is give them a little bit of motivation and to let them know they had a little bit of room to have like a snack or something. And so you can say, if calories is less than, excuse me, is greater than 1500. So here, perhaps it's a 2000. So if the calories are greater than 1500, we can say print ln and say, you have a few more calories left to eat. And so if we run this again, what we're gonna see here is still the top line. You've consumed all your calories for the day. Let's go ahead and make an adjustment saying we've eaten 1750 and we were running our program and the program would now say, you have a few more calories to eat as we can see down here in the bottom. And then for some reason, we even set this to 1499, which means this condition will be false. And then it checks this condition, this condition will be false, which means anything in the else block without be printed. So let's run that and we can see that you still have some calories left, which is pretty cool. So you can keep adding on these various different else, ifs. So, if we wanted to add another one here, we can add another else, if as well. And we could say something else, like calories is greater than 1200 and perhaps we just wanna give another message here like this. And then if you were to run this and you were to set this at, let's say 1400, and we were to run this, you could see down here at the bottom, you have some room for snacks. So we can not keep adjusting this and play with this. And there's no limit to the amount of else ifs that you can throw in here. So if you have many conditions, so if you only have one condition, it may just equate to be something like this. It's very simple, your if or else. You do not have to have an else either. If you don't wanna have an else and you just need to perform some type of action only if one value is true here, then you go ahead and print this. So else is not required. The else ifs are not required if you're using an if statement. So again, just an if statement or you continually add else ifs and we can continue to add these if we wanted to. So we could add another one here and we could say, calories were greater than 1000, you can say, you have plenty of calories left, et cetera. And as we go down, so again, if we were to go to 1100, that line would now print, you have plenty of calories left. And if again, we dropped it down to let's say 800 and we ran it, which one you think would run here. It's actually going to be the bottom one here because the calories is not greater than 2000. It's not greater than 1500, it's not greater than 1200, it's not greater than 1000. So therefore the final conditions going to run. And this is a very simple, if else conditional that you can use. These are gonna be the most traditional type of if elses that you see in your programs. In Kotlin, you can also omit the closing parentheses and open parentheses of a condition if it's a one-liner. So let's say if the age is greater than 10, you can actually just write ln is greater than 10. So let's go and change it actually two 11 and we run it. We're gonna see that the output in a window down here as the age is greater than 10. Now, if for some reason we change this to nine, notice how the steel yet we don't get an error, but we don't see that the age is greater than 10 because a single line statement can now just be printed without parentheses. However, this should be often avoided as it can be confusing even if I divert to do something like this. It's hard just to type this. Just basically upon the indentation, you would think that hello there would not be printed, but however, hello there is not part of the if condition. The highlighted area you see here is part of the if condition. This print line, hello there is not part of the if condition. So if you know that you're gonna have code here, it can be very confusing. So it's usually often better to just go ahead and provide the braces as it's much more readable. And when you're writing code, you should be trying to write code that you can understand in the future. Now, one thing I did mention for a quick second was the word one-liner, or words one-liner. What that means is you can actually put this all in the same line here. Now, if I were to run this it'll compile and everything, and we'll just see the words, hello there. And that's because it'd be hello there, that's because the age is nine and we're looking for age greater than 10. So let's change this to 11. And then if we were to run this, this single line of code is actually pretty easy to read. So now, if you're just gonna write a single line, this is very readable and we know that this line below it, hello there is not going to get executed. So if you're just running a single line, it's very often you'll see this and you can remove the brackets as I've done here. And if again, we're gonna run this, we're gonna see the two things here, the age is grater than 10, you dropped this to nine and we're only gonna see the words hello there because if condition has not been met. Now, you can also take this a step further. If you know that your, if else condition might be very short, you can actually all do this on one line. So print line, and we could say the age is less than 10. And so we can actually have the entire if else condition right here on a line. And it's very small, it's very easy to read. And so if we're to run this again, we're now going to see down at the bottom, the age is less than 10 and then the words, hello there. If I change this to 17 and we run it again, we're not gonna see that the age is greater than 10. And of course, we're still gonna see the words hello there, because it's printing its own statement. So you can definitely create if else statements on a single line and it's advice to do it only when there are a single line. It's also good to mention if your else statement becomes very, very long and you have to continually scroll, and there's just a lot of text over here. So if I were to say, my content went way over here and in the editor, I had to kind of look over, you'll see this little gray line here. This gray line is kind of the recommended line break. So if you had a bunch of texts and I'm just gonna type a bunch of garbage in here. If I had this line and went beyond here, this recommended line is where your line breaks should be. So if it goes past that line, you should probably consider breaking this into a traditional if else statement, which we can just put brackets there and the IDE will apply them for us. Now it's much easier to read. It doesn't require that I do any horizontal scrolling and makes it easier to understand the code by just looking at it. A fundamental understanding of truth tables will help you understand what's going on inside of your if statement and if it will evaluate to true, or if it will evaluate the false and execute this line down here. So let's hop over and take a look at some simple truth tables. Here, I have created two truth tables, one on the left, which is the logic well conjunction and which we'll talk about first. And then we'll move on to the logical conjunction or. A logical conjunction and state that we have two variables, P and Q. And this is red on the light right hand side. P and Q, so you'd be at P the up arrow Q and it's red as P Q. And that's what it evaluates too. So if P is true and Q is true, then if we were to combine them, P and Q would be true. If we have a P that is true, but that is false, well, then that would equate to a false representation in this truth table. So if the first expression, so if we go back to our code, if our first expression was true, our second expression was false and we're using a logical and, this is an and operator that the Emerson Emerson means logical and, then the false will be executed because the second item here is false. We can move on to the third line. If the first item is false and the second item is true, it's still gonna be false because in order to get a true value, we need both of them to be true. So back to the code, this one at this point would be false and this one would be true. If we were to add these together, that would not equate to true. So we will be executing the else statement down here. And lastly, if both the items are false, then of course, if we logically and them together, we're going to get a false statement. So back in the code, if both of these are false up here, this is false and this statement is false, then of course, it's gonna execute down here. Now on the other side of the fence, we have a logical conjunction or. Now this is operated in code, let's go back to the code here. And we change a or two B with two lines. And so that's gonna be a logical or. So what this says is here is this statement has to be true or this statement has to be true. And if either one of these are true, if this one's true or this one's true, then it's okay to continue on and execute this line of code. However, both of them are false, well, then it's gonna execute this false statement down here in the else. And so let's take a look at them again. We're using P and Q and it's red as P or Q. And that's that little V thing there used to be a down arrow. And so if P is true or Q is true, then of course the resulting truth would be true. If P is true and Q is false, well, we're still saying, "Hey, either one of these is fine, "so let's go ahead and move forward." And that would be basically like saying if this is true, then okay, we can continue. Or if this is true, that's okay, let's continue. Same thing here, if the first item in the evaluation is false, so P is false and Q is true, then our logical or conjunction would equate to true. Then our logical or conjunction would equate to true. so this equates to false. So false, it's gonna go to the next item over here and it's going to return true. So it says, "Oh, I found a true here, "which means I'm going to execute this line here." And lastly, we have the false or false. So if both of them are false, back in the code. if both of them are false back in the code. which means they're both false means neither one of them are true, then it's gonna execute the else statement. There's one thing important to note is that these conditionals, when we use the logical operation or, or the logical operation and are known as short circuiting, which means that if this operation here is false, it's just gonna skip this together. It will happen is Kotlin will say, "Oh, you want me to add these together. "So I'm going to need this value." What it's gonna do is at runtime, it'll check say, "Hey, is this false? "Okay, I already know this is false, "which means this whole statement is gonna equate to false "because the first one's already false "and you're asking me to have both of them be true." So what you would then do is, Kotlin would then do, is say, "All right, well, I'm just gonna go and skip this. "I'm a short circuit this because I know that there's no way "and no reason for me to even run this code over here, "because I've already determined that this first piece "is false and we need both of them to be true "in order to continue." On the opposite side of the spectrum we have or, or this could be short-circuited here. So for example, let's assume that this was true. So we're basically saying if this is true, or this is true, print this statement right here. So what this would mean is Kotlin would short circuit. If this was true, it wouldn't even bother checking to see if this was true at all. So it would, short-circuit this entire operation. So, all right, this is true. All right, cool, I'm not even going to worry about executing the statement over here. However, if this was false right here, it would say, "Okay, this is false, so I need to go check the next one, "because I'm going to logically or them. "And I'll check to see if this one is true." And it says, "Oh, this one's true. "Okay, I'm going to use this." Now, this could continue on as well if you had a, for example, we could have another condition here, we might say, my age equals equals my name.length. And so we can actually have three conditions here. So this one right here might equate to false. This one might equate the true. And if it does equate the true it's going to skip the rest of the conditional here, because we've already found a true and Kotlin knows, "Hey, we're just trying to or all of these together." And so understanding basic truth tables is a humongous help in understanding of what's gonna happen in your conditional statements. In Kotlin, there are two types of equality, structural and referential. Let's assume that we have two string variables by the name of name one and name two, both containing different strings. We could compare them and see if they're the same by using the structural equivalent operator, which is the equals equal sign to say, "Hey, is name one equal to name two?" And if we were to run this, it would output to the terminal window saying that it's false, that named one does not equal name two. Now this is very similar to other languages such as Java's equals method, which you can also use. And you'll see this here. And if we run this, we'll also get false here as well. Notice this squiggly line here. If you put your cursor there and you hit alt enter, you'll get a pop-up. Otherwise you can just click on the little light bulb and say replace with equals equals. This is the more idiomatic way to do it in Kotlin. So this says, hey, is name one equal to name two. Now there's also, if we were to change this. So let's say that this was changed name two to Don. This would then evaluate the true because the structure of the contents are the same. So they are the same types, same types and the same contents. So structural equality is true. And so if we were turned this back to char, and we say, "Hey, I wanna see if these are different." We can use, what's known as structural inequality, and that's going to be with the exclamation and equal sign. Now, if we run this, we're going to see that we have a true, that Donn does not equal to char. Now, if I change the second one to Donn again, what do we think is gonna happen here? Donn does not equal Don. Well, that's gonna be false because these two are exactly the same. So that is the way that you can use structural equalities inside of Kotlin. You're going to get the first one, which is equals equals is the regular equal equality. And exclamation equals is inequality. The other type of equality checking is referential equalities. And we can determine those using a different operator. So let's assume that we have one variable that's equal to 12 named A and another variable that's equal to 12 is B. And since every object, everything in Kotlin is an object, these, you would think would be different objects. So if we were to run this, we're gonna use referential equality with the three equal signs, which is referential quality saying, hey is A equal to the same object as B, we were gonna get true. Which if you know about references and memory, this was not making any sense, except there is a caveat in Kotlin and that is number, characters and Boolean. So numbers and Booleans, all have a special internal representation and they're represented as primitives at runtime. But to users, they look like ordinary classes. Therefore the triples equals is the same effective thing as the double equals when we're using primitives. So let's get rid of these primitives here and let's go ahead and we're gonna jump ahead real quick, and we're gonna create a class. And this is a very simple class with a user who has a name that is a string. And let's set person A equal two person, and we'll give them the name of Don, and then person B will also have the same name, Don. And then what we'll do is we'll do referential, a referral equality check here, run the program to see if object A equals object B. And as it runs down here, we have false, which means that object A does not equal object B, meaning that it does not share the same object, it's not the same object whatsoever. We could also change these things around and do referential inequality. So we would do exclamation equal equals, that would say object A does not equal object B. So they're not pointing at the same place in memory. So for, to run this, you would see that we now get back true. Then we're getting back true because person A is a completely different object than person B, they're stored in completely different parts of memory. So for referential integrity, we have the triple equals, which is equality. And then we have exclamation equals equals, which is inequality. One of the goals of Kotlin's type system is to eliminate no reference exceptions. So if you two were to take this existing name variable and set it equal to null, you would notice that you would not be allowed to do that. And the reason being is this string is known as a non null reference. If you wanted to create a nullable reference, you would still say string, but you would add the question mark at the end. This would allow the name to be reassigned to null. The same goes for other types as well, such as age. We might say, this is int, we've got 32. However, if we tried to assign age to null, this would not be allowed either. So anytime we would like to make maybe age or any other type, we need to remember to add the question Mark at the end, which then would allow us to set the type to null. Even if we had a simple class such as a person class, we could say var person equals person, that would work fine. But if we were to reassign person to a null, this would not be allowed because we are guaranteeing that this is a non-nullable reference. However, if we did want a null reference, we could say person two, we would need to provide the type here 'cause it's going to be a nullable type. And then we could say equals person Fu. Now I could set person two equal to null when working with non-nullable references as the first name variable here. If we wanted to see the length of the first name, we would simply say, first name.length. This would give us the length. There's no need to check to see a first name is null because we're using a non-nullable reference and Kotlin guarantees that that will not be null. When we were using a non-nullable reference if we attempt to do the same thing and we'd get the last name.length, you'll notice that we have a red squiggly here. So what we have to do is introduce what's known as a safe call. This basically says, if the last name is null, then go ahead and make this null. Otherwise give the length. So this variable could be equivalent to one of two things, either null or the number six, because there's six characters in the word Felker. So if the last name is null, it would return null. Otherwise it would return length. You could do the same thing with an if statement as well in one line. So you can say if last name does not equal null, then you could say last name.length else will say zero. Now what this is doing is saying last name. We're checking to see if the last name is not null. They're gonna get the length. Now notice how I did not use the safe call operator here. The reason is, is Kotlin recognizes in this if statement, we are checking to see if last name is null, therefore I don't have to check for null here because it was already taken care of in this step. However, if for some reason the last name is null, then we'll just go ahead and return a value of zero. So either way, the answer here would be we're either going to get six or we're going to get zero. Now of course you could change this to null if you would like, though to essentially do the same thing as the above, but most likely you probably want some type of value in your code so you can do this with a single line if. And you can also chain these safe calls together. So let's say that we wanted the first two letters of the last name, and then we wanted the length of the last name. Notice how we would need. So we'll say link four, so last name says it needs a safe call and notice now the next one needs the safe call. So what this one will do is says, hey, if last name is null, then just go ahead and return null, if last name's not null, go ahead and pull the sub string off of that. So grab the first two characters. And if for some reason that's null, go ahead and return null. Otherwise go ahead and return length. Now you may have another language has seen something very similar to this. If last name does not equal null and last name.length is greater than one, then you might wanna continue off there then you could have your statement to say that your length or whatever is, to check to make sure it's not null. So this would be very common in our languages such as Java, but here you can start chaining these calls together. You could even take this even further by saying last name.sub string would say 05. So we get the first five characters, then we could say something different, like drop the last two characters. Then we could get another sub string of those and then we would say, let's say we want the second one to the third one and then we would say length. And so we could say length five equals this. Now, because this could be null all the way across, we would need to use safe calls. So every time all the way across we need to use these safe calls, meaning that if any of these were to return null length five will be null. Otherwise it will be the length when working with nullable types, it's often to wanna retrieve a value. So let's say the length again of a certain, maybe the last name string, you wanna get the length of that. And so the need to perform some type of safe operation to get that value. However, this is going to leave us in a certain situation where we could either have null or the length of the string, last name which is six, so we could get either one. Let's say for whatever reason, we determined that we know we wanted a non-null reference. So in that case, we're going to get an error because last name dot length is going to return us a null. So how would we fix that? You could fix that with a simple if statement. So what we could do is we could just check to see if the last name does not equal null, then give us a length, otherwise give us zero. And we can get rid of the safe call here because again, this null has already been checked. So what this will return is a non-null reference of length, either the length of the actual string variable, otherwise zero. Now this can also be short-circuited as well. So we'll say linked to int, and this can be short circuit, so we're gonna use in what's known as the Elvis operator. And Elvis operator looks like this, is a question mark, colon and then a value. And what this means is the Elvis operator means is, if anything on the left-hand side of this Elvis operator is null, then return this value. So if last name is null, then return it. We could even go a step further and change some things together. So we could actually say 0,2 and we could do multiple safe calls in order. So if last name is no, or this sub string is null, then return zero, otherwise return us the length here. And this will give us a non-Konoll reference. Now you may be wondering why is this called the Elvis operator? Well, if we turn it sideways and zoom in and have this image here, you can see that it looks like Elvis Presley's hair, and that's why it's called the Elvis operator. To create a function in Kotlin, what you'll need to do is use the keyword fun and then the name of your function. So we can create one called hello world, and you'll have open and closed parentheses and then open and close brackets here will give you a function that you can call. Now, if we were to call hello world from somewhere, to call that, again, our main function is going to be called because we have a little run button here. If we want to execute what's whatever is inside of the hello world function, we would need to call the hello world function. And we do that by just typing the word, hello world, the name of the function here with the open and closed parentheses. Now, if I were to run this, you would see that nothing is going to happen because inside of this function, we're not really telling Kotlin to do anything at all. You can see it the program exited. If we want to do something in here, what we can say is we say print line. And what we could say is, hello world inside of here with double quotes and then ups here when we've now run it, we'll see Kotlin will compile it and we will print hello world right here to the screen. Now we could change his function name to something else. So let's call it something fancy like purple cow. So we have purple cow here and I'm gonna go ahead and copy and paste that name there, purple cow. Anytime I call purple cow, it's going to print hello world. Again, we'll just double check that here to make sure. And there we go, we see hello world here. Now the cool thing about functions is you can call them over and over. So I'm gonna copy this and go to a new line, I'm gonna paste it again and again and again. So if you're wondering, what's gonna happen is due to the procedural nature of Kotlin, it's going to execute this code, which means it's going to walk into this function and it's going to run whatever inside of here. Now it's going to, and then after that, it's gonna come into this function and call into whatever is inside of here. So basically it's gonna call this purple cow function one, two, three, four times. So we should see print line of hello world four times down in here in our run window, which we do. We see hello world. Now the cool thing about functions is that we can actually change what's inside of them and it will change anytime you call it. So if I realize I have in my application purple cow called four times, and I wanna change this to say, "Oops, you know what, this should actually say, "hello purple cow." I can just change here. Now when I run it, we should see down here, hello purple cow four times. So we'll see hello world purple cow. There we go, it shows up four times. Now I can also do other things inside of this function. So a function is just a place where we can put other code that we would like to call it over and over and over. So it's kind of like a reusable piece of application code that you can use over. So if I were to do something else, I could say per line, hello there. And now what we're going to see is hello purple cow. And then it's going to print hello there, but it's gonna print that four times. So if we run this here, you're going to see that it's going to call hello purple cow, hello there, hello purple cow. Hello there because what's happening is each time this line of purple cow is called, two things are being executed inside of here. Now functions can also call other functions. So it was just kind of makes sense because inside of the main function here, we're calling another function called purple cow, but also inside a purple cow, we could also call another functional call moo. And maybe this function might just say moo and we'll call function called moo. And inside of here, we'll do print ln moo. Now what will happen is when purple cow is called, we'll see, hello purple cow and then we're gonna see print line is gonna be called hello there it's gonna be called. So hello purple cow, hello there. And then the purple cow function is going to call the moo function and the moo function is going to say moo. And so if we were to run this year, you would see that's going to call. Here we go, hello purple cow, hello there moo. Hello, purple cow, hello there moo. So now I can actually change this around if I decided, "Hey, you know what, I wanna move this up here "and I wanted to change this to say, hello pink cow." Or let's do blue. Let's do hello blue cow and we'll do moo. Now, if I hit run again, we're gonna see what do you think was gonna happen? We're gonna see hello purple cow, moo, hello blue cow, moo. And so we just see move between each one of these because that's all that moo is doing. And then we could add something to this if we want. Of course, to say moomoo Buckaroo. And if I were to run this again, we would see all different types of stuff here. So it kinda makes sense. Hello purple cow, moo, moo moo Buckaroo. Hello, blue cow, moo, moo moo Buckaroo. Say that 10 times fast. So as you can see, these are just very simple functions, but the functions allow you to reuse code. Now I can also come up here and say, I can say I wanna do moo right here, and I don't really wanna have it here. So I can kind of use these reusable chunks here. And this case, we're gonna say purple cow once, we'll do the moo routine and then will just print purple cow. And so, like I said, each one of these functions allows us to have reusable chunks of code. So we started off with hello, purple cow, hello, blue cow. And then we did the moo, moo Buckaroo, and then it went back and executed the purple cow function three or four times. So this is how you create a very simple function. This function has, as you currently see, no return type specified. And because we don't have any returns type specified, what is actually returned is a unit type. And we'll talk about the hair in a second, but if we leave it off there by default, any function that does not specify a particular return type will return a unit. So we'll get to that in a second. By default, all functions in Kotlin have a returned type of units. So if we create a function called say hi, that's just going to print out some text to the screen, such as hello world and we call it here, it's actually, it doesn't have a return type. And then to specify a return type in Kotlin's functions, you're just going to, after the parentheses here, you'll put a colon and then the type that this function is going to return. So if it returns something back, you could say it's gonna return back a unit. So you'll notice at this point in time, I don't have any type of statement to tell it to return a unit or anything like that. I could do this, and it would work just fine. But by default all functions that don't have a return type return unit, which if you see the IDE IntelliJ is saying that the unit is it redundant so I can alt enter, or I can click on this little light bulb and say, remove the explicit type declaration. So I've done that now to prove that this does return a unit, I can actually just create a variable and I'm gonna print this variable to the screen. So whatever X is here, because again, say hi is going to return something. And because this function does not have a return type specified by default, it's actually just going to be a unit. We'll just leave that off to show you that. And since it's returning something, I can set it equal to a value. So this is also valid to call this just like this and have it execute the function. I don't have to do anything with return type, but if I would like to, I could actually take that stuff into a variable as I'm doing here into the say at two variable X and I'm a print to the screen. Now, once we run this here, say high is gonna execute and return a unit and you can see it printed the hello world text and then it printed the type which is Kotlin.unit, so that is a unit type. And so again, if I just specify it here, units. Again, I'm not having to specify a return statement because just by default, if it's a function like this, it will return a unit. And there we go, as you can see it returns a unit here. Now let's say for whatever reason, we want it to return a string though. And so you'll see here, as soon as I did that, got a squiggly line here. Now what that's saying is we haven't returned any string here. So let's say, I wanna return something. So let's say, hello, is this return the word hello world. So instead of printing it, let's just return it here. And it's going to return the value hello world. And then when we print it to the screen, we can say hello world. And did go ahead and print to the screen because what happened here is these say hi function is now returning hello world. We're gonna stuff that value into this variable X and then we're gonna print the variable X to the screen with the print ln statement. So now something interesting happens. You may be thinking, well, let's just call say hi a few times here and let's see what happens. And if I run this, you'll notice that we're just going to get on the screen hello world one time. And the reason why we're only getting hello world to the screen one time is because remember, say hi is returning something, it's returning a string. So it's not doing anything in here, but saying, hey, I'm gonna have this value and I'm going to return hello world. And at that point you can do whatever you want with it. And in that case, we are just assigning it to a variable X and printing it to the screen. Now, these last three, aren't doing anything. It's calling this and it's returning hello world, but we're not doing anything with the results. So if I did val Y equals this, I did val X, (indistinct) would have X, Z equals this. I'm val A equals this, those variables would then be set and I could actually print them to the screen as well. So I'll change these from Y to Z to A. And if we'd run this again, you're gonna see that all the values are running and printed to the screen. So hello world, hello world, hello world, because we had to actually do something with those return values. And if I get rid of this right here, we're gonna see that we have an error on the screen, simply because those variables have not been declared. So that's just not gonna work, it won't even compile. So this is a return type. If we wanna change the return type to something else, we can say int, so it's gonna return int. And by default, this will not work. You'll say, hey, there's a problem here. And so I can return the value of 32 or something like that. And if we run this again, we can then execute it. Now, of course, because this is a function, We can say something, we could put some if statements inside of here or whatever we wanted to, even if we wanted to return it to the string, but we only had a integer in here. I could say dot two string and that would then print 32 as a string to the screen. So now whatever return types are, which could be any of the built-in types that you've already learned could be here. So it could be a Boolean value, it could be a long, it could be a double, if it can be any float, whatever you're working with at that point in time, you'll need to return that. So in this case, I'd say return true, and it would return that value. So you need to return whatever the value is. And of course, as we know, if we don't return anything at all, it's going to return a unit, which even if the function does nothing whatsoever, it's still going to return a unit, which then we print to the screen here. Functions instead of Kotlin are first class. So if we have a function, say hi, we might have something that look like this. And inside of here, when I say hello. And if we were to call this function, of course, we just call it in the main function and it's just going to print hello to the screen. However, one a bit are cool things that you can also do inside of Kotlin with functions since they are first-class citizens and technically objects, which we will get to is you create another function inside of another function. So we can say bye and that would look something like this. So now I'm creating a function inside of another function. And this one just gonna be bye. Now, if I wanna call this function, if I go up here and I try to say, say bye, you'll notice I can't call this up here because this function is not found. It's because the scope of this function is within say hi. So the only place I can call say bye, is it within the say hi function, so this won't work. So if I wanna call it, I can come in here and call it. But if I try it right here, you'll notice again, it's red, which means it can't be found and the reason why Kotlin can't find it is because it has not been declared yet. So by the time the code gets here, you have no idea what say bye means because it's been declared below. So we can go underneath the function and call it. And so say bye, and if we were to run this now, what we see to say hi and then bye. And if we run it, we would see hello and bye. Now, of course, we should probably rename this to something else to say hi and bye. And that was a simple rename, which is done with a short cut key or you could just do refactor rename. So I used the shift F six here on a Mac. So shifted F six giving the highlight to change it. So say, hi and bye and it will say hello. And then it will say goodbye or just bye. And then that function inside of their head of function. You can also put another function inside of something in here. So you could say move like a cow, it would say print ln. And this could say moo. Now of course, I need a call moon after I've declared it. So at this point, I'm gonna call say, hi and bye and then print hello, this function will be declared and then we'll call say bye which then we'll hop into here. It'll say bye, we'll declare moo. Then moo will be called, which will print inside of here. So all different kinds of stuff happening. We're going here to here to here kind of jumping around all over. And if we were to run this again, we're going to see hello, bye, moo as we see on the helmet here. Now there are again, functions inside of functions. This can get really squarely looking and look real nasty after a period of time. So I advise that you be very careful with declaring functions inside of functions and make sure that's the proper right thing to do, because it can make the code very hard to read. So unless you have a really good idea for making a function inside of a function, then you might wanna steer clear of it until you have a good grasp on when you might want to do it, which there are various different implementations that are beyond the scope of this video. Functions in Kotlin typically look something like this and this function called say hi, what we'll actually have is a print line statement that says hi. And so if I were to call this from the main function, we're gonna go ahead and see high printed down here to the output screen says hi. Now, unfortunately, with a lot of languages, this bracket and this bracket is kind of verbose for just a simple statement. Now, of course, this function could have multiple different things in here, but let's just assume it did one thing for now. So Kotlin actually has a concept known as a single expression function. So we could do this here, say hi is equivalent to what we had before. What this means is I have a function, it's name is say hi, it doesn't have any parameters. And it's body of the function is the equivalent to this stuff over here, which is on the right hand side of the equals sign. So inside the body of the function is just gonna be print line. So if I run this, we'll see the same exact thing here. So we can actually see hi. Since Kotlin supports functions inside of functions because functions are actually objects, we can actually declare a function inside of another function, which you could decide to do or not and we'd say do work. Let's say we had a variable peer called age equals 32. And then it said, if age is less than 32, excuse me, less than 21, we would go ahead and print out that the whatever person or whoever is less than at 21, and otherwise they are say eligible for whatever reason, maybe have some reason someone has to be over 21. So now if we were to call do work out or try to of course, type a here do work has not been declared yet so I can't call it, so I'm going to call it from down here. And if I run this, we're going to see say hi and then we'll see eligible because the person is eligible. Let's set this to 12 and run it. And you're going to see that it says less than 21. Now we can also perform, this is kind of as we know multi-line statement. But since we can use single line if statements, we can also turn this into a single line expression. So what I'm gonna do is I'm gonna get rid of these brackets, basically all of them. And we'll do is kind of line this stuff up here and turn this into a single line expression. And so again, the age is 12 if it's less than 21, it'll print line here, else that'll print this other thing. If we run it, we can actually, we see less than 21. And of course, if I changed it to 32, run it again, you'll see that the output is eligible, which is the other part of the statement here. So you can clean up a lot of your code by using single line expressions. There is a heuristic that you should follow though. Let's close this window over here and you'll notice the right-hand margin with this little gray line here. If you find your code approaching this over here. So let's just go ahead and type eligible a couple of times, if for whatever reason eligible, you've typed a bunch of stuff here, it goes beyond the right margin, it's usually advisable that you'd turn this instead of this into an old fashioned multiline function just so that it's more readable. So here you'd wanna go ahead and of course, add your brackets. And you'd kind of do this thing right here. And as we've kind of seen before, we'll put this down here. Oops, we'll clean this up a little bit, so there we go. So now if the age is less than 21, we're gonna print line. Otherwise we're gonna print eligible, eligible, eligible, eligible. Again, now the reason is because the right-hand margin, it's just a common coding practice. If your code goes beyond that right-hand margin, you should figure out a way to break into a new line. And usually if you're using a single line expression, you'll wanna go ahead and that's usually an indicator that you should probably use a regular function body instead of a single line expression. Functions in Kotlin without any parameters are going to look just like this with an open and close parentheses. So the parameters are gonna be in between the parentheses here and there are no parameters here. So if we were to call do work, of course, nothing would happen. We could say print the line work is happening. And then if we were to run it, of course we'll do that. Now, parameter is something that we can parse into a function to do work with it. We can perform some type of value. So what I would like to do maybe is I would like to provide some type of value here, I wanna type in the value of 32 into this function. So what I'll do is take the value 32 and say, "Hey, do work, you need to do something with this 32 value." But in order to do that, I need to provide a parameter inside of the function declaration. And so to do that, what I'm going to do is type the word age and its type. So here age is gonna be an integer and its value up here is gonna be 32. So now what I can do is I can say age is, and of course, when you string interpolation here and say age is age and whatever. Let's just do the reads a little better. You are blank. And so let's say you are 32 in this case so far. To run this, we would see that do work functions says you are 32, which is great. Now if I call this function with another value, let's say 12, we're gonna see two different values returned here, you are 32 and you are 12. So what the parameter allows you to do is allows you to parse in values to your function. So you say you are 32. Now I could also provide other additional values here so I can put name and access can be a string. So I need to provide the variable argument name and the argument type, which is string for both of them. So again, this is the argument name, argument type comma. So I need another one in there. Argument name, argument type. Now you'll see that we have a red squiggly up here, that is because IntelliJ says, "Hey, you're missing a additional value." So I'm gonna type in Donn and instead of saying, I can say, string interpolation again, name, comma, and we'll have proper punctuation here, period. And if we hit run here, what we'll see is 32 and Donn says, Don, you are 32 now. If I to change their own, I could say something else like 12 and I could put Jonas, and Jonas might be 12 for whatever reason. And if we print that out here, well, I'd have you're 12. Now the cool thing you can do with this thing inside of the do work function is now you can start working with these values inside of here. So maybe you might have an if statement. If the age is less than 21, you might wanna say something different, like I wanna cut this out here and we'll say the name, you are not old enough. Now, for whatever reason, perhaps you have some logic that states that for someone to use this application, they have to be over the age of 21 for whatever reason. And we wanna put it you're not enough. You are blank, let's say age. As I say, Don, you are say far too old at the ripe age of, and then we'll put the age of 32. And so now as I run this, we'll get two different values that we're we're working with here. So we'll see the first one is Donn and the next one is Jonah. So Donn far too old at the ripe age of 32, Jonas, you are not old enough, you are 12. And so I can continue down this road here. Now, if I wanted to start having, if I call this function all over the place, let's say I have a 71 year old and her name is Evelyn and Evelyn's using the app too and she types in her age and we run it. We'll see now that Evelyn also has an entry, Evelyn far too old at the ripe age of 71. Sorry, Evelyn, if you're really not 71. So we can actually start various different values in here that can be any different types here. You can provide one that's a Boolean value. So you could say, yeah is happy and you could tap in a Boolean value. And of course, because everybody's happy, we'll just put true here. And we'll say true and you see IntelliJ is giving us some nice little hints here of what the actual (indistinct) this is not something I typed here is happy or age or name. That's IntelliJ saying, "Hey, by the way, "the name of this parameter is happy." And then we've put true here because, Evelyn, everybody's happy. Then we can hit run. And of course, we're gonna run it again. Donn you're far too old age, ripe 32. You notice, hey, we didn't do anything with that value. So we, of course, we might need to do something with it, which has says you are happy. And then we could put something like this. We could actually put some other type of code in here. We'll say, boom is happy and then we'll run it again. And again, of course, now we're gonna see each one of them's for Don, Jonas and Evelyn, you are happy, true, you are happy, true, you're happy, true. For some reason today I was not happy, maybe I stubbed my toe or to run it again. We would see that Donn is not happy because he stubbed his toe. Jonas and Evelyn though are still happy and so forth. So this is how we can start adding in various different parameters. Again, if you need another one, you're gonna put a comma in here and you're going to add another one. So we could say foo is going to be of type long, for whatever reason, you may need some different types here. And of course, IntelliJ is gonna say, hey, that's not gonna work 'cause you need to provide that value up top. So this how you provide arguments to a function. Let's assume we have a function called print and user info. It takes a bunch of arguments such as the first name, last name, age, if the user sunburned, if they like movies and if they love popcorn. And if you would like to do something with this, and perhaps we just want to print it to the screen, we could provide some implementation that looks like this, that uses string literals and prints it to the screen. Fair enough. Now, if we wanna call it, we're going to call it like this. We'll say print user info. And then we have to start providing all of the information such as Donn Felker and then we have to provide the age. And then we say, is he sunburned? No, he's not sunburned. He loves movies or likes movies, true and he loves popcorn, of course, why not? So this now very simple, we've covered this before, but now we have these nice little IDE hints in here that say, Hey, this is what this argument is. It's his sunburn. And this has works great except until it goes outside of the IDE because IntelliJ and other IDEs that are from IntelliJ brains do this for us. But once we get online or into another IDE, we don't get the same feature. So let's hop over into another IDE. Okay, we're inside of visual studio code. I'm gonna paste the code in here and you'll notice that we don't get those nice little previews like we see over here. So we have first name, last name, age, sunburned, et cetera. If we go back to visual studio code, we don't have those values over here. That's a feature of the IntelliJ IDE. So at this point when we're reading this code, and this will be the case, if you're doing a poll request or viewing the source code online through a web browser, you're not gonna understand what this false even means, especially a couple of months down the road. You're not gonna understand what true means. True here, we have no idea what print user info is. And most likely this method is somewhere perhaps in another file, perhaps further down where I can't see it unless I have to scroll for it and find it. And it just makes it more difficult and I have to do a lot of context switching to find it. Now, the interesting thing here is that we can fix this inside of Kotlin. Let me go back to IntelliJ and what we can do is actually provide the name parameters here. So I'm gonna type first name. So this is what is known as a named parameter. I'm using the name of the parameter and at the call site, I'm actually providing the name here. So is sunburned, that's false. And then we could say it likes movies, true and loves popcorn is true. Now, if I actually, I'm gonna copy and paste this code again, go back to visual studio code, delete this code here and paste it. You'll see we actually get those name parameters over, By default, Kotilin will still compile those code. So back inside of IntelliJ, it will still compile this code right here. And if we were to run it, we would see this as Donn Felker is of age 32 sunburned, false, likes movies, true, loves popcorn, true. So the things all run in, which is really nice. Now the other great thing about named parameters is they're also positional based. So let's get rid of this one down here so you can see what I'm talking about. We get a little squiggly here. It says mixing named and positioned arguments is not allowed, meaning that we can't... We're using a positional based argument right now. This is a positional based argument, it's the last one here in the list of arguments. I could also undo this and say, hey, let's get rid of the age one here. and I'd get the same thing here and say, hey, this is basically the third item in the argument list. Kotlin says, "I don't know what you're trying to do. "You wanna use name parameters, but now you're using one "that's a positional based, decide what you wanna do." But it won't compile. So if you're gonna use one name, you need to provide the name for all of them, which is much more readable. Now because we're using name parameters, I can actually move these around, which is very cool. So the print user info method takes a first name, last name, age, those are the parameter order. So in traditional languages that you're used to working with otherwise, you always have to provide the values in that order. However, with Kotlin you can provide a named argument. So it says, hey, age is 32, Kotlin will know to map this value here over into this age parameter. Now, if I wanna move his sunburned maybe a little bit closer, 'cause for whatever reason in my program, it makes more sense, I can do that here in Cottonwood note to map this parameter value, which is second the list in the positional sense, map it over here because we're using the actual named parameter. So we're using the name parameter to map it over. And that's how you can use name parameters in Kotlin. In Kotlin, there's a concept known as a default argument. Now, every time I call this print user function. So let's say I had to call it a few times, I have to change this from every time of course, these values are gonna change to some other types of thing and it's gonna say 31 to 37 and 12 and all these different types of things. And this person is sunburned, et cetera. But perhaps the majority of the time, I always know that these people are not sunburned and maybe because these people are my family, they like movies and I know that they love popcorn, but having to repeat these things many times as troublesome. So we can actually use the power of default arguments. And to do so we can specify a default value for an argument. So it will say the user loves popcorn. So we're gonna set it to true here. Now, if Love's popcorn is not provided, we can actually remove it from this call site, it will just default to true. So I can actually remove it from these other ones as well. And I can provide a default argument for likes movies. And we're gonna default that the true, because everybody should like movies, I think. And then again, I can remove this from the call site. And because I'm removing it, the default value of likes movies is going to be true. And of course, it's not often that everyone is sunburned. So let's go ahead and set this to false. And it's pretty easy to do, so I'll say false. Now we can go ahead and remove this from here. And now this cleans up the call side of these three functions quite a bit. We can actually see the only relevant information that is changed in each print user info function. And if we were to run these here, let's run it. You're gonna see it's printed three times and we see sunburned, false, likes movies, true, loves popcorn, true. Now, if for some reason that I know that this time, perhaps I've just eaten a lot popcorn and I'm just not really fond of it lately, I'm just gonna say it loves popcorn, false. And maybe I know that Bob this week is sunburned 'cause he just got back from the beach here over in New Jersey and the Jersey shore. And perhaps Sarah, for whatever reason just does not like movies and we're gonna put false there. So actually Bob is sunburned. So make that true. So now what will happen is when we call the print and user function, all these values are set. Now loves popcorn, which is traditionally true, is gonna be set to false for this function call. For the next function call print user info, it's going to be the true cause I haven't provided it up here because it's using the default value. However I'm saying, hey, it is sunburned. This time don't use the false value, use true 'cause Bob has actually sunburned. And then finally we have the print user function down here at the bottom for Sarah. And she actually doesn't like movies, so we're not going to default that to true, we're gonna set it to false. And if we run this again, we're going to see that these values actually render correctly here. So Donn loves popcorn is false. The Bob Felker here is a sunburned, true and Sarah Felker likes movies, false. So these are all of their default values that you can have inside of Kotlin. Now, if there is a string, for example, we could say that a string, let's make a string here at the end, we'll say favorite color. And it could be string and would've say that's going to be blue. And if you provide that favorite color, then up here, you can override it, otherwise it will just default to blue. So you can do any of these things. Could be a built-in primitive type, could be a class. And then like that you can set to a default value. Now, traditionally the default arguments are usually placed at the end of the method call. Now this is not a steadfast rule. You can actually place them in the middle. So if you'd like to say, well, the last name is actually supposed to be defaulting to Smith because we are working with the Smith family. But whatever reason we realized we gave Sarah the wrong last name. Now, if we were to run that again, we could run it and we'd see Bob Felker, Donn Felker and Sarah Smith here. So we've used a default argument right inside of the middle of the call site here. So right in the middle of the definition of the method. Now, but age has not been specified. So you do need to provide that. So if we do leave off age, we're gonna get an error here and we'll see that no value parse for parameter age. And because we're using named values, I can do that up here, say, our age is 32. And I can move these around anywhere that I would like, because we are using the name parameters. And that's how you can use default arguments parameters inside of Kotlin. A common pattern in Kotlin is once your column length for your code exceeds the right-hand margin length, which is his right hand line, it's common to start placing values on the next line, such as we're doing here, makes it much more easy to read. Now this can get quite old pretty fast and can become very slow and error prone. Now, thankfully, if we're using an IDE, such as IntelliJ, Android Studio or anything like that, this is built into the platform. So put your cursor on the left-hand side of the open parentheses at Alt and enter, and you can say put arguments on separate lines and now it'll put it on separate lines, which is great. So it saves you a lot of time really quickly. So anytime you wanna do that, you can use that little shortcut. Now we do get these nice names here that are built in from the platform. Again, this is the IDE giving us these names. So if we copy these into another IDE, such as Visual Studio Code, we don't get those values back. So how do we add those named values of actual those argument names? You can go back to IntelliJ, Android Studio or whatever, and start adding them by hand. Again, this is gonna take a lot of time if you're doing this over and over and over. Thankfully again, place your cursor on the left-hand side of the parentheses and select the add names to call arguments and boom, all of a sudden you have now cleaned up your code by placing each of the arguments on a separate line, making it much more readable, as well as adding the names of the arguments to each of the parameters, which makes it even more readable and saves you time at the end of the day. Let's assume you have a function called print book info that prints the title of a book and the author. Here, we have green eggs and ham, and if we were to run it, we would see that it's just going to print green eggs and ham, the author is Dr. Seuss. Now, however, if we had the instance where we knew that we needed to add an additional author, because sometimes books have coauthors, we would have to maybe create an overload of this function. And we'd say this would be author two. This would be author an author two and then perhaps we might change it to say authors, and then we'd have another comment with another string interpolation here. It would for author two. Now, if the book has three authors, we would have to create another function and four and five and six, and you can see where this is going. So thankfully Kotlin has a way for us to do that without having to write extra code really. And so what we can do is take this last parameter and turn this into a var arg. And that's a keyword var arg, which means variable argument. And then I'm just gonna change this to authors, which means that this value could take zero to many values. So there could be many authors here. And since this is going to be an array, if we were to print this now, let's go ahead and just add another person in here. So maybe I was the coauthor, which we know I'm not, but this is hypothetical. We'll run this here and you can see that we actually have open bracket and it's starting to print the information. This is now an array. And so this is printing the information about the array. If we actually wanna print the contents of the array, we're gonna hop into a little bit of an advanced concept here and we'll use a lambda expression and we'll print it to the screen. And it is the key word at the front line. And if we're to run it now, we would see also this array value here, which we can get rid of and clean it up a little bit and run it again. And you would see that we have green eggs and ham and the author, we should say authors would be Dr. Seuss and Donn Felker as it's now rendering here. We could also take this and add additional ones. So if we had Jane Doe, Jon Doe, and we had all kinds of other ones, we could keep adding them in there. Now the other cool thing is too, we could also have the situation where for whatever reason, this book just doesn't have an author and it's unknown. We could run this as well. And we would see that there's no values returned. So if you need to have a function that takes in multiple inputs of that particular type, you wanna make sure that the var arg is the last argument in the function, slap on the var arg keyword and then you can pars multiple values in so such as we're doing here. So like X, Y, and Z, and then all these values would then be stuffed into this author's array and you could loop over it using either a lambda expression or any other array manipulation or iteration techniques. Let's assume we had a function called print user info and it just printed a name. So very simple, we're just gonna take the name and print it. And it says name Donn Felker. Now let's say for whatever reason, I also need to have it take sometimes it only needs the name, sometimes it only needs the age. So I'll say, you know what, I actually kind of need another print user info function. And this one is going to have age and this one is gonna be int. And so at this time I can actually say, all right, well, I'm gonna call this new one, it's gonna be a new user. And this one's called Jane Doe and I'm gonna make her age of 37. So now this one print user info is going to, if we go to the definition of it, it's gonna go to this function. And this one is gonna go to this function. This is called function overloading, we're overloading the term print user info. So has two different has the same name, but it's taking different arguments here. Now, if I were to remove this argument, you'd see we get some errors here because we had the exact same definition here. This is the exact same function name and function parameters right here that are right here and that's going to be a problem. So because we have a different parameter here age, it's now considered a different signature to Kotlin and say, all right you can overload that function and then of course, we're probably going to have some information here that's a little bit different. Now, again, we could say, well, now I also want their favorite color to be inside of here. So I'd say fave color. And that might just be a string for simplicity. Now I have three overloads. So what we could do here is I say, print user info. You see, I have three different options. So one takes a string, one takes integer, a string and an integer, and one takes string integer and favorite color. And so of course, then we'd want to change this. So it said feed color. And this would say using string interpolation again, their favorite color. So then I could print this user info. And I would say Frank Bendo, and we would say that his age is 22 and his favorite color is purple. And at that point in time, if we were to run these, each one of these are going to run a different function. So the top ones are in the top one here, the second one's going to run this one here, and the third one's going to run this one here. Now, if you're already familiar with Kotlin and also it's named parameters and his default values, well, then now I'm sure you can already imagine that well, we could actually kind of clean this up a little bit. And to clean this up, you can actually kind of get right rid of this and say, hey, you know what, well, age is going to be an integer. And if they don't provide it, I want it to default to zero. And a favorite color is going to be a string, of course. And if they don't provide it will default to purple. And so we'll do that. Actually no, we can actually default it to an empty string if you'd like, and then what will allow us to do is get rid of this, see how we have this here. It says, hey, there's a conflicting overload because we have this up here with the same values. Now, if I get rid of this, all of these still work because we're using the name parameters. Now, of course I would wanna say age like this and fave color, so we can actually render the stuff and it would be there. So if I don't provide a favorite color, just gonna be blank, if I don't provide an age, it'll be zero and I've kind of cleaned it up. And I still have this function overload options here, which I can say, print, user info. And then it's gonna give me the option of, hey, don't use any default values. And that's how you can overload a function in Kotlin. In Kotlin, you can create a class by using the class keyword and give it a name such as user we'll do open and close parentheses. Now, if you're not familiar with what a class is, a class is a reusable templates. So we could actually have various different variables here. So I say variable user equals user, and then I could have another one. So var friend equals user, and there's another own different instances. So friend is a user and this user object is a user. They're two different instances stored in memory. Now this class, this template per se is not doing anything, it doesn't do anything for us right now. So what we can do is actually define a couple of fields here. So we'll say a couple properties. We'll say field, call this one first name and this one's gonna be a string, we'll just initialize it to an empty string, var last name. And we'll go ahead and initialize this one as well to empty string, and now we have first name and last name. So if on the user I would like to set that, I can say user dot and that will allow me to access the two properties last name and first name. So I'll set the first name to Donn and user.last name to Felker. Now let's say we have another user. Let's say it's friend, of course, this one also has a first and last name. We'll set the first name here to Jane and the friend.last name equal to Doe. So we have two different instances. Just because I've set the last name Doe here does not mean it overwrites here. These are two different things in memory. Now class again, it's kind of a template. So you can actually, it can provide functionality as well. So we can actually put functions inside of this user class that are scoped to the user class. So what I might wanna say is print full name, and then we'll just be a regular function and it's not gonna do anything, but it will print line to the screen and or to the output, which would be the first name and the last name. We can have another one in here that says print with a prefix, which then would maybe taken a prefix of some sort. And we could just call that prefix and that would be a string. And then when we call this function, we can do the same thing here. We could just say prefix and then you have perhaps a last name. Now, the way we would use this is up here. We might just call user.print full name. And if we go down here and do the same thing with friend, friend.print full name, we'll actually see two different pieces of output. So let's run this real quick and what's gonna happen is we'll have two things printed to the screen. One is Donn Felker and that's called by the print full name function here. The next one is Jane Doe and that's called from the print full name function as well here. Again, there are two different instances. The contents of the objects, which are basically instances of the class are being printed to the screen with the print line function. Now we do have the prefix, so we'll get to that in a second, but let's also do something different here. So let's say we have a function called update name and this update name takes in a new name and it's a string. And what this is going to do is actually update their first name. And so we'll say first name and we're set to new name. And then what we'll have here is a way to update their name. So let's come back up here and say, you know what, maybe I messed up when I update their name and this is hypothetical and what I call this one, Bob. And then at the same time, I'd like to user.print full name. And so if we run this again, what we'll see here is that the first object is Donn Felker, then we update the called the update name function, which goes down here, sets the name to the new name. And then we call print full name again that says Bob Belker. And then we go into the next user and this following the line, sequentially procedurally, we see Jane Doe here. Now the same thing we can do say user.print with prefix. And maybe I'll say Mr. And then in this one, I might actually say friend.print with prefix, and this one might be Ms. And if we were to run this now, what we're going to see is of course, Donn Felker, Bob Felker, and it will say Mr. Felker, and then we'll see Jane Doe in Ms. Doe here, because we're actually just printing with the values in here. So now this class is basically a template that we can use inside of our application. Now everything is stored inside the same file here. Now, what you can do is you don't have to have this in the same file at all. So what we can do is I'm gonna open up the project, went over here and you see we just have our main file. What we can actually do is create a file manually, like say new Kotlin and I can say, user.kt. And then what I can do is I can go over here and I can cut and paste this code into user.kt. If we go back to the main, you're gonna see that everything still works because we're in the same package. So it knows that it's going to look in the same package and it found the user class. Now this allows us to start cleaning up the code a lot because basically have a template called the user class here in a different file to do that for me. Now, what I'm gonna do, actually, I'm gonna undo this. I'm gonna go back to main here, I'm gonna undo this as well and come here and get rid of that Kotlin class. So we're gonna go back to how we were before. There's actually a shortcut that you can use. When you put your cursor over class, the class name here, you'll see this little bulb. You can click on it and you can say, create test, rename the file or you say move user to a separate file. Say, yeah, I'd like to do that. So right out of the box, by default, we were given this nice little thing that allows us to create a file. And what that did is it created the file force and moved all the contents of that user class over there for us. So let's go a little bit further here and make one more method. And this method might print the length of the first names. We say, function, first name length. And what we'll do is we're gonna print something to the screen and then what we're gonna do here is just take the first name as the first name.length. And that's gonna print the length of the first name. So if we, again, this is kind of like a template. It's a class. Go back to main Katie and let's print the length of the friend's first name. Friend.print, excuse me, length, first name length. There we go. And if we run this now, Kotlin will find a user class included here for so we're running what that user class, run the existing code, which we've seen already, Mr. Felker, Bob Felker, et cetera. And then we're gonna call the friend up first name length, and it's going to return four because that's the number of characters in the first name, which is Jane. So this is a very simple class, but this is how you can create a class here in Kotlin. Let's assume that I have a class called user with first and last name and a couple of functions inside of it. If I'd like to use a constructor, what I can actually do is use the keyword constructor. Now I can actually bring these properties into the constructor. So let's actually get rid of these and actually actually say first name and I can say, this is a string and last name. This is a string. Now, usually you wanna default everything to vals because it's going to help you with immutability and help you not make as many mistakes. But we can try that here, but we noticed that there's actually a problem here. So we have the update name method, actually updates the first name. And if it's a val, we can't do that. So let's just change these to vars. so the properties that can be mutated, which means they can be changed. So now we have a constructor for our user class, which takes in the first name and last name. And if we go back to our main file, we're actually using it. You can actually see a couple of things here. First we have in the user class, you can actually see when we type user, open and close parentheses. We have a string for first name and a last name as well. So we put Donn and Felker. Now, if I would like to rewrite these, I could easily do that with this property right here on line six and seven, I can change these to perhaps it should have been Jason Smith. I can reset those here so I can actually change those or I can actually just get rid of them 'cause I don't need them anymore. And then here I'd actually do Jane changes to Doe, I can get rid of the property setting here. And if I to run this again, you'll notice that we're gonna get the same result that we got before, this time we've just used a constructor with the parameters and properties inside of here. So these parameters are turned into properties, which you can use inside of your class. So you can see that down here. Now you can also, as you can probably imagine, put your cursor here and you can add the names to the call arguments. So you can actually see that that's actually the first name and that's actually the last name. So you can actually see what these values are as you parse them in. Furthermore, in the user class, if the constructor does not have any modifiers on it, such as the internal keyword, which we'll cover later, you can actually, so if it doesn't have any of these annotations or modifiers, you can actually get rid of the constructor word and this will be the primary constructor. So now we've actually cleaned up the code a little bit more. We have the user class, which takes in a first name and a last name. So we can see, I hear to a first name, last name works just fine. We don't have to have the constructor keyword on there though if you could still use if you'd like to, but it's very common if you have a primary constructor and that's it, you just remove the constructor word. Now, if you wanted to have perhaps this to only be for internal and you wanna have constructor, and you tried to get rid of the constructor word, you would see that this is not going to work, use constructor keyword after any modifiers of a primary constructor. So we need to put constructor here if we were to restrict the visibility of the user classes constructor. But we're not doing that here. So this is a basic class with a primary constructor. One of the additional things you can do instead of a constructor is also set default value. So we could set this to be a blank string. And perhaps we knew we were always gonna be working with the Smith family for whatever reason, we could just say this is Smith. And so if I'd like to back inside of our main file, I can actually get rid of the last name. If I didn't want that there fall into override a here to be Doe, I could do that. And so now this is just gonna say Donn Smith and Mr. Smith and Jane Doe and Ms. Doe, because we're using the default values inside of this primary constructor. Class can have more than one constructor. This is a primary constructor up top here. We can also have a secondary constructor. To create a secondary constructor, you can use the constructor keyword, and then you wanna parse in some additional values. But first let's get that set up. Let's assume that this user can be of a particular type. And so we'll say is platinum and be perhaps a platinum user for a particular, you could say it's a financial institution. And so we wanna create another constructor. So sometimes we wanna be able to provide, instead of the first name and the last name, we already know by default, they're not platinum. So what we can do is we can call this constructor here. Now this constructor will take the first and last name, but then what we need to do is call into the original version. So we're say the first name of parse it in. So this first name value is coming from here. And then the last name and they'll say is platinum, we're gonna say false. Now, we can to see here is we no longer have any red squigglies. What this means is I can use this new constructor. Now I don't have to provide a body for this here. But if I would like to, I can also provide a body, may be able to do something in here. Like print line you could say is platinum is false by default. So if I use this one, if I use this constructor, this line of code will get run. So let's go back to our example and let's change this one here. So by default, both of these are going to call into this here. Now, of course we have not provided a last name, so let's provide a last name here and that's going to be Felker. And then it'll say, it's platinum is true. Now I'm a platinum user, but in this user down here is not a platinum user. So now if we were to run this on the second call down here, we're gonna see is platinum is false by default. That's because when we go here, I'll go to the implementation, it's using the secondary constructor. Now we can even go even further and say, if we want another constructor, so multiple constructors who can keep going to here. So I might say first name, and then might just skip that and say, all right, this which means, hey, call another one of these instructors. And this one is going to call in first name and this time, I don't know what the last name is going to do. So I'm gonna call and perhaps I'll watch. Just for completion sake, we'll say hey in the third constructor. And if I go back to the screen here and let's go ahead and create another one, var we say it's your cousin, and I'll say to user, it we'll say, first name is Nick. And then we'll say, we're not gonna do anything here because if we look at this implementation, it's using the third constructor here, which is gonna say, hey, I'm in the third constructor, but look, something else is going to happen. And what's gonna happen is when this third is called, the word Nick's gonna put it into the first name and then it's gonna call into this. It's gonna parse this first name value into this. And it's gonna give an unknown last name. Now this is actually gonna call this constructor, which is going to say, boom, all right, now it's unknown. And then it's going to print as platinum by default. So let's actually see that run. And then all the values of course are then parse d into the root constructor here, and then we can actually see everything. So if we run this now, what you'll notice is something interesting is going to happen here. Now there's a lot of stuff going on here. So let's this up a bit. So let's go ahead and just kind of get rid of all this stuff here. We just wanna see when one of the constructors is called. And so we have have those methods inside of there that's going to call line when the constructor is called. So if we run it now, we'll see is platinum is false by default. And we see the same thing again and then I'm in a third constructor. So if we were to go to this first one here, this first one uses the primary constructor. Nothing happens here, we just set up an object and that's the end. The second one uses a constructor here that uses the first name and the last name and then defaults to platinum field is false. And it says it is platinum is false by default. Now the third one is interesting 'cause the third one will say, hey, the first name is Nick. It calls into the other constructor here. So it'll say the first name was Nick, last name is unknown. And then what happens is this construct will call into the root constructor. So it's gonna go this executes first, this line then this line is gonna execute, which calls in up to the primary instructor. After that is complete, which is this line here, which calls the primary constructor up here, then it will procedurally call this one. And then now that this whole method is done running which is right here, then it will run this. So what we're seeing here is this is being called first because again, this gets called, which goes into here, nothing happens up here, that's complete. Then it runs this whatever's in this constructor, which is this call here. It says, okay, that's done now. Now run finally what's in your constructor. And so these three things are happening. So if we really wanna make it simple, let's go ahead and say so third, just to really make it kind of obvious, second and there we go. I'm gonna go back back and we're going to just comment out this one. And what that'll do is we're just gonna run this one here and we see second then third. So first thing that happened is it printed second because it hit this constructor first from kind of walking this call chain and then it went to the third one. Now we could actually get around this here and say, hey, this one here is, we'll just say is platinum is false. And we could even say true if we want, it doesn't really matter, but let's just leave it false, let's do a true actually. So if we call this and we parse in a first name or you an unknown last name and they're a platinum user. Now when we run this, you're just gonna see third being printed to the screen because it's only calling the third instructor 'cause by default, what happens is this construct will be instantiated in turn it takes its first name value and calls the primary constructor. So this are here's the primary constructor because it has three parameters. If we then took this one out, last one, it's now going to call the second constructor. So let's assume this wasn't here, so let's comment it out. That's not going to run. Now, you'll see that we don't have a constructor that matches the signature, which is a string and another string. We have a constructor with two strings and a Boolean. So that's why we're seeing multiple different versions here. And again, if we can come in here this time we run it, we're gonna see second and then third. Now you can have multiple different constructors inside of here. So you can keep creating different types of constructors. You could set them platinum, et cetera. Now, of course, some of these things may not even be need to happen. So we can say, hey, perhaps we don't even need this, we can use some default parameters here as well. So if the last name is not known, then it should use unknown. And then if it's by default, we're gonna set this to false. So now if I come back here, I can still do this exact same thing. Even this would work and still gonna work because it's using default parameters and the named arguments, the default argument values. And so then we can see everything how it should be. So each of the objects is going to be created, number is going to be allocated, et cetera. And then we kind of get all that safety right here and the various different constructors. However, if you do need different constructors for whatever reason, or you need to perform some type of different values, you can call it from functions to do different, run all different types of code in here and run different code in here, you can do that, just provide a different constructor using the constructor keyword and you're off to the races. Now, additionally, you can also restrict some of the constructors using modifiers, such as protected, or you could call internal, et cetera. So you can actually lock down who can call into a constructor based upon a modifier. We have a user class and instead of a user class, you can also have what are known as initializer blocks. And these initializer blocks are created with the init keyword and open and closed curly brace. So this would be perhaps somewhere where you could perform some type of initialization for your class. Initializer blocks are called after the primary constructor has been invoked. So again, this is the primary constructor up here. So after the primary constructors have been invoked, the initializer block will be called. So something interesting that we can do is we can just go back to our main file here and we'll create an instance of our user class. And we'll just say Donn Felker here for the first and the last name and we're gonna run it. Now we're not putting it into the screen here, but notice how we do see hello one is output down here in the run window. The reason for that is because in the user class, inside the initializer block, I'm printing out hello one. Now there's also something interesting you can do with initializer blocks. Is where you can have multiple initializer blocks and this initializer blocks will then operate in the order in which they are defined within the class. So what will happen is the primary constructor will be called first and last name, and then we'll get hello one called and then hello two. So if we go back and rerun this here, what we're going to see instead of hello one, we'll see hello one and hello two as we see down here. If we go back to the user class just to demonstrate this, when we move this below hello two and we rerun this, what will the notice is we'll see hello two show up before hello one. Now one last thing here too, just to demonstrate that the initializer and blocks are called right after the primary constructor, let's go ahead and create another constructor that just perhaps provides the first name, which is a string and then it's just gonna default the last name for us. So we're gonna call into this parsing the first name and then the last name we'll just default to Felker here. And if I go back to this here, I'll get rid of my last name, which will then use that second constructor here, which we can see here. Now what's gonna happen is we can actually provide a block here so we can do something after this constructor's call. So let's do that here, say constructor, second constructor call so we'll say here. So our expected output at this time, when we create a new class, you might think would be okay, we're gonna call the second constructor called. And then of course it's going to be the primary constructor and then we'll get basically hello one, hello two because that's the order to show up in. However, what you'll notice if we run this, that's not the case. And as you can see here, it's actually a little backwards. We have hello one, hello two, and then second constructor called. So it seems kind of backwards. But when you think about the execution of the actual class, this here is called immediately. So as soon as this constructor is called, immediately those values are parse d into the primary constructor. And then after the primary constructor is called, the initializer blocks are then called. After those are called, then execution will return to inside of whatever constructor you're in, which is the second one here into that block there and you'll execute that, which is why we see hello one, hello two and then finally the second constructor is called. Now you can use the initializer blocks to perform some type of initialization for your class, perhaps some basic setup of the class and initialization type of tasks are a great place to put it inside the init block. In classes, you can also create properties in line here. So I will type a property called full name, and then I can initialize it with the values that are being provided to me through the constructor. So we can use the first name property, and I can use the last name property to create a full name here. Now, if I go back to the main class and I decide I would like to print that, I could say print ln and would say user.full name, and you see that I have access here too because it's in the same package and so I have access. If I were to run this, we would just see that Donn Felker is printed. Now you also see that we have an init block that's been printed is here as well. So we have init blocks. And one of the nice too is we can also, if let's go back to the main class, we can also change this value. So since this is a property, it means it's mutable. And we've of course, because we set it to be a var type here. And so what I can do here say user.full name, and I can actually reassign this volume, say hello world. And it's gonna completely overwrite that value. And then what I'll do here is I will say print line, and then we'll put user.full name again. And when we run it this time, what we're going to see is the init block then we will see Donn Felker and then we'll see hello world. Now back inside of the class, there's an interesting thing that we can do as well. Inside of the init block, I have access to this property. So if I were to type something inside of here for the init block, perhaps I just wanted to print something to the screen. I'd say this class is for user and I could say full name. Now what you're gonna notice is I get a red squiggly here. And what that red squiggly is telling me is that it's not initialized, but as we can tell it is initialized, but here's what we need to do. We need to actually take that and move the initialization up a little bit higher. Now based upon the file format and how everything's organized, the init block can now see the full name is there. If I come back and run this, we're gonna see that the unit block runs. It says, hey, I'm in the init block and this is class, or this is class four, user Donn Felker. That's really bad English. So this is the class for user Donn Felker. Of course we rerun that cause we don't want typos. We'll see, this is the class for user Donn Felker. And of course here we're gonna print the full name shows Donn Felker. Next we reassign it and then it shows full name. Now each one of these, you can have multiple of these if you'd want. They don't have to be initialized from a particular instance. We could actually just set it to something like this. Say it's zero. It could be of any type that you want your proper to be. And I can actually come here and say user.age equals let's say 22, which I'm not 22, but that would be fun. And then I can then print it or do something with it at that point in time. So that's how you can work with properties using accessories and mutators inside of Kotlin. You can also define read-only properties inside of your class. So you might wanna know the full name length, and decide to store that as an integer inside of your class. And so you can say full name.length. Now we'll have a full name length read only property that we can use inside of our main.kt file. So if we say user.full name length, we can actually get access to it. We can print it to the screen. However, if we attempt to reassign it to 12, we'll get a little error message saying that the val cannot be reassigned. So these values have been assigned based upon the other properties. You could also perform this off of a property directly off of the constructor. You can also decide to set this initially such as 12 or something like this. And if you'd like, you can provide the type as well. And let's assume inside of your user class, you wanted to decide that anytime someone requested the full name property, you wanted to make sure it was prefixed with the word name. We can do that very easily by providing an override of the accessor for the full name property. Now, what we can do here is we can actually type in name and then what we can actually type in here is the word field. And what the word field means is this field right here, whatever is stored in this property, go ahead and we're gonna use that right here. So anytime someone calls a get on this, which is by default, anytime we call something like this, it's actually calling the get, it's going to return name:field. So in this instance, if we were to run this, we're gonna see something like John Franks. But instead of that, we'll see name: John Franks. Now, if I were to leave this off or comment that out where to rerun this again, we would see that the name is just John Franks, because it's just printing out the field, which is been stored by default as initialized with the first and last name. Now here, I was able to override the get implementation so I can say, yeah, anytime someone requests the first or last name, then we're going to go ahead and say name: John Frank. Now it'd probably make more sense if we just said full name so it wouldn't match the variable name there. And then once we run it with the full name, John Franks. One important thing to note, this field is actually the backing field for this full name property. So this is what contains the contents of the full name property. So this is known as the backing field. Now I can also override the setter, also known as the mutator. I could say set. And by default, the value that's parse d in to the full name when it's changed is gonna be called value. So if I were to say something like this user.full name equals Jon, like this was a school lowercase, jon without the H, then what would happen is this Jon would be shoved into full name, which then would call this code block here and what would be inside of this value variable would be these three letters, J-O-N. Now I can actually do some stuff, anything I'd like to do in here. So I can actually say if value starts with, maybe I wanna know if it starts with something. I say, hey, if it starts with Jon, J-O-N, then what I want this field to be equal to is going to be Jon Doe. So if anyone says, sets us to Jon and then our to print line anything else for user's full name, it should now say Jon Doe. So if we've run this, we'll have in here is we'll see John Franks, of course, we're printing that the first time we've reset this to say, hey, the full name is actually Jon, which we know is not the real case, but if we look at the mutator, the setter here, we're saying, hey, if the first name is Jon, then go ahead and actually set the backing field to actually equal Jon Doe, for whatever reason, maybe we have some business requirement that states that we need to do that. And then once I print the full name, of course, it's going to say full name and then print that backing field, which has now been turned to Jon Doe. Now we do have a problem here actually, if I were to attempt to reassign full name to something else, such as let's say Jane Sparks. And if I were to try to print that to the screen as well, what we would end up seeing immediately is something we probably wouldn't expect, which is we're still gonna see Jon Doe printed. But hold on, I set it to Jane Sparks, why isn't that working? The reason that it's not working is because the full name property we have overrided the setter, we're checking the value that was set to that backfield. If it started with Jon Doe and then we set at the Jon Doe, but what we didn't take into account is, hey, what about the else condition here? So we needed to do an else condition. If this value starts with this, it starts with Jon and set the Jon Doe otherwise set the field equal to the value that was parse d in. So now if we come back here and we were to run this, we'll now see that we have John Franks, Jon Doe and Jane Sparks that are returned. So you wanna make sure that you handle all of the various different types of scenarios you can have. This could very well be a valid scenario for your application. You may only wanna decide that only you can change that value to certain values or there's some type of logic for the setter that you need to adhere to. However, if you need an else condition, be sure to also implement that so that your property does act like a normal property, which can have the properties red and have them set. Let's assume that you wanna have multiple properties in a class, you can easily do that. So of course we have these properties up here, but let's assume that you also want another property in your class called age, and you're gonna default that to zero and then perhaps you have a favorite color and that's a string, and you're gonna default that to blue. So you can have multiple of these inside of your application and it can easily be accessed and modified through the property accessor's and mutators, which are default. So say first name, age X, age equals 30. And I can say, use the user that favorite color equals green. And so these are all possible. You can have as many these properties as you would like inside of your class, just like functions. You can have more functions and new, like you say, say hi, and this could just be a simple little print line that just says hi. And so of course we could say, user.say hi and that would print to the screen. I could come in here and change that of course, to hi, full name and then it would say, hi, Donn Felker in this case. So you can easily put multiple properties here wanting to add another one, we can do that here, et cetera, et cetera, et cetera, and put as many there as you'd like. You can also add functions to your classes and you just go inside of your class definition and start typing your function name. So we'll create a function called full name, which will return a string. And then inside of here, we'll just use some string interpolation, we'll say first name and then we'll say last name with the dollar bracket syntax there and it will return us the full name of the user. Now you may be noticing I'm not using the brackets around here. If I need to use the brackets, only time we would is if we're using some property off of this. So maybe I wanted to get the length and you'll notice how IntelliJ added those brackets there for me. Anytime I'm doing anything off of this main expression here, it's going to require we put it in brackets. Now, if I do remove this, I could still have brackets here. This will compile and this'll work. But notice there's a little gray squiggly here, it's kind of hard to see. If I put that there, I'll get a little light bulb and click on it or hit alt+enter, and it will see remove curly braces. And if I do that, it'll just automatically remove them because we don't need them here. Now this could also be cleaned up a little bit further as well. So we could kind of turn us into a single line function really easily, and we can just click and get rid of this and make it equal that. And we can make it a Parry, small, succinct little function. Now, to call this, we would go back to our main file here and we say user.full name. So we'd actually see that if they function here. And of course, if we now run it, we will see that we have the name, first name John, last name, Franks, and it just puts out John Franks. And again, if we wanted to again, do something different, we could do dot length. And for whatever reason it would say, John, and then the length of the actual last name, which we'd see here, as soon as it compiles. John six, because the word Franks has six characters in it. And so you can continually keep adding different functions here. So you could even add a different function here. say, full name length, and this one could be very similar. Again, this one might return it int this time and we could either do it on a single line expression, or multiline like we're doing here. And we could do something even call it one function call another function and say, let me see you get the length of that. And then we can also print this here and I'll go ahead and go to full name length. And when we this again, we're going to see that we get the John six again, 'cause we did the full name. Let's fix that real fast 'cause we don't want the length anymore on the last name. We just want it to say John Franks as the actual output. And then we want to actually show the length of the name. And so we're doing that here. So we say John Franks and then the full name length is 11. So we have one function here, it's calling another function. We don't have to do this. I could replicate this functionality down here by doing this, I'm gonna comment this out. And I could say return this dot length and would give us the same exact thing. But at this point we have some code duplication and that's just usually not a good thing because if for some reason I decide that I want the full name to have a hyphen in it, well, now this length here is not going to be the same as a full name length. That kind of depends on what kind of app you're building and if you need that functionality. So I usually recommend, okay, if you already have a function that provides that value, just go ahead and call that function and then it will be updated. So in this case, it's gonna add us two additional characters here because we have an additional space and a hyphen. So instead of the 11, we should get back a 13 as we have here. And that's how you add just very basic functions to your actual class. And you can continue adding as many as you like inside of the class body here. They can reference the members, they can perform different operations, they can even alter the members as well. So if we wanted to do that, we'd say fun, update full name for whatever reason with suffix. And we might have parse d in a suffix of some sort and that suffix might be a string and we might do something like this. first name equals, let's say suffix. And then we would say first name. And then what we would do here is we'd go back to this area here on our main file. And we wanna say user to update full name of suffix. And it will say Mr. And then as it prints, it should say, Mr. John, which sounds a little weird, but that's how it's gonna look. And we see Mr. John down here and the output, and that's how you can add and continually add additional functions inside of your class. To create a companion object, what you'll do is inside of your clash you'll type companion object, and then you can start typing the name of your function. So in this case, it's create a function called create user. It will return a new user. In order to do that, we actually need the first name and a last name. So we'll need that provided to us. That's not what we want first name, and then we'll have last name. And then of course, what we'll do is we'll have this return a new user. And it's very simple, just kind of knew what the user with the first name and the last name. Now, companion object is a function or property that's going to be tied directly to the class or rather than it's exact instances. And we it's very much like a static method that you would see inside of something like Java and a companion object is a Singleton, so meaning that is tied to the actual class itself. So what does that mean? Let's go take a look at this implementation here. I can do this, say user.create user Fu and then we'll say bar, and I'll say val user. And then what we can do here is I'm just print line the user, they'll say print the user. And what we've done here is we've actually created a basically static type of method attached to the user class. So now you notice how I didn't create an instance of user, I just called a method that was directly on the user class, and that is going to be the create user method. So see, create user and we can treat this like a factory to create objects for us. Now we could go a little bit further too, as well. And let's say that we had maybe we wanted to create, a way to create a bunch of users and I say, create users. And then what we can do inside of here to say, how many users we would like to create and maybe parse into value. What we're gonna do is return a list of users. So in order to do that, let's go ahead and create a quick class level Singleton variable. So it will say foul users equals a mutable list of users. Now we'll be able to access that via the class. And what we can say here is for I in zero to the count, which meaning from count from zero up to the number provided count here. We want to add an item to the users list, and we'll go ahead and say user, and we'll just kind of do something first name, and then we'll provide some type of value such as the index here. And then we'll do the same thing here, last name, and then we'll do something like the index as well. And as you can see here, it says we can remove the curly braces. So we can do that here and here, because we're just doing a very simple expression. And then of course we'll return the users, which is a steep to member variable. So let's go take a look at how we would use this. So let's go over here, val users equals, and we could say user dot create users. So maybe you wanna have this to create a number of users or objects or one out of a type. And then we can say print line and they'll actually print those users out. And when we run this, what we're gonna notice is that we see all of them printed on one line. And the reason for that is this is basically Kotlin's way of showing us, hey, this looks like some type of collection because we have a square bracket and a closed square bracket up here, open and close square brackets. And this is the first element inside of our lists. So zero one, two, three, four, five. So actually, if we're to make this a little bit easier to read, we can actually use one of the collection helper methods called Foreach. And it basically iterates over each item. And then inside of there, we can off the item, which there'll be, it's called it by default. And if we run this, we'll see that we actually have them printed on all on each individual line. But now the interesting thing is we said, let's create five users, one, two, three, four, five, six. Now the reason why we have six is because this range is starting from zero. So we could change this to count, to count minus one, or we could just kind of make it simple saying, hey, go from one up to the count. So now if we run this again, we'll see that we only have one, two, three, four, five. Now we could count this up to 15, which is great. And we can go in and create 15 different users. And it's all pretty simple to do. Now also, the cool thing is here as well, let's go ahead and comment this out, is that we've not created our users, but perhaps we could say users too. So kind of new variable here, I'll say user.users. Remember this is a property, the Singleton property that's on the user class. So this method create users actually modified this list here by adding to it. Now, again, back here, we're not doing anything with the users, we're just kind of leaving it there. So technically I could just kind of get rid of it if I wanted to, and then I could say users, and then I could say print line. Let's actually go ahead and copy this up here. To make it easier, I'll say users two. And of course that's not there. So I'll say users two print line. And if we run this, what we'll notice is that the user's object of course, was populated via the create user. So now, if I were to come up here and say zero, so don't create any users, it's gonna to see, like, we have an empty basic list. We see foo bar, that's this one up here. So if we were to get rid of this guy up here, we wouldn't have that. And we're not gonna have anything output to the output of the window because we don't have it there. So that's how you would build a companion object inside of your Kotlin class. You use the companion object, keywords, open and closed curly braces, and any functions and properties that are inside of here are class level and it's basically Singleton scoped. Let's say that you would like to keep track of a user's favorite food, or even just a favorite food for your application. Let's assume that it has some ingredients and those ingredients are a mutable list of string. And let's also assume that there is a name of a favorite food here, and I should make this one mutable as well, and this one's gonna be a string and we can go ahead and default this to a known because maybe you wanna change it later. Now with a Singleton and software development, we only want one instance of this to be created at any time. So if I have one favorite food, I can't have multiple. I mean, I could have multiple favorite foods, but let's say my most favorite food in the whole world is X, Y, and Z. I only want one instance of this to be created. But however, if I were to set this up, I say, favorite food. I can create one instance of it here, I can create another instance here. And how would we do that now in utterly. So, as you can see, I have multiple of these instances, and I only wanna be able to restrict the user from creating basically one instance of this. So how would I go about doing that? Well in other languages, what you would do is you would mark the constructor private and it's a private constructor. And now of course, we're not gonna have the ability to create this. But now what we need is some type of helper method to create this. So maybe we have a companion object and this companion object has a new method called instance. And this instance just basically create a favorite food for us, so it's basically a private method that creates a favorite food for us. But now even then, we're gonna keep getting new instances here. So even if we did this, let's do those things. We could say favorite food at instance, we're still gonna get multiple different instances of this food. So what we'd have to do is inside of this, we have to kind of lock this down and say, all right, it's gonna return a favorite food. And then inside of here, we're gonna have to possibly need a let me say vowel instance equals favorite food. And we start getting a lot into a lot of code here to kind of start managing this. You can kind of start to see that we're gonna start getting out of control, we need to start handling, create the new instance here, et cetera. Now, thankfully in Kotlin, this is a lot easier. We don't even need to do all this. This is already handled for us. And to create a Singleton in Kotlin, it's actually really easy. So I'm gonna delete this private constructor. All we have to do is change this class to object. Now time, we only have one instance of favorite food at any time. So now if I go favorite food, so just give me a favorite food.name, excuse me, I could say print line of this, favorite food.name and we're gonna see unknown. So if we run this, we'll see unknown. Of course, this will say unknown. Now, if I were to change this, I'd say favorite food.name equals maybe you love. I know I do, I love watermelon. So I'd say watermelon and our to run it again. We could see still unknown because we didn't print it here. So let's print it again. If I print it now, it'll say watermelon. So I've just only have one instance of this at all. So now even if I were to create a function, so I'll say fun, let's just call it do stuff. And then do stuff is going to do something. And so I'm gonna say, favorite food.name is maybe it's chicken now. Now, if I come back up here, I'm just gonna say, call, do stuff. Oops. Say, do stuff. Actually we need to define it before I call it. So I'm actually gonna take this and move this outside of our method here. So there we go, let's say, do stuff. It's gonna call do stuff and then we'll say print line. I want you to print my favorite food.name again. So what we should see here is we should see unknown then we should print watermelon. And then by the time we get here, it should say chicken. So even though we're calling instead of the do stuff method, we're calling favorite food instead of a different scope. Right now we're in a main function scope of here, we're in the do stuff method scope. What we're gonna see here is that this is treated as a singleton because there's only one instance of it lying around. So once we run it, we should see unknown watermelon and chicken as we do here. So the same thing can go inside of here. For example, if we wanted to, let's do this right here. So favorite food.ingredients.add let's just say salt, everyone loves salt. And then I can actually print here. So print line, how many ingredients are here, I'll just print the first one's a favorite food.ingredients.first. It'll print the first one here and then, and maybe instead of do so let's actually run that to see what that looks like. So we'll see that the salt is the first ingredient that's printed there. Now inside of do stuff, again just to demonstrate this, let's just go ahead and say favorite food.ingredients.clear. So we're gonna clear out that list and now I wanna print this. So kind of put that, so it's a little more obvious. And there we go, kind of print break them apart. If you run this again, what we'll see here now is that the ingredients have been cleared and we'll see that this list is empty. So now we get an exception here on main line 14, because I'm calling first. So what I can do here, something different like first or null and that would actually give me a null value sheets should print null there, which it does. So we could say print null. And so there's nothing in that list at this point in time. So this is one way that you can create a singleton inside of Kotlin very easily. So you're just gonna replace the class keyword with object. And of course, you can also put methods inside of here as well. So you can say fun, you can say number of ingredients, and this can be a very simple method that kind of just returns the ingredients.size. And of course, we could just call that from up here as well. So print line, we can say, favorite food.number of ingredients. And if we run that we should see zero at the end. Oops, it's asking us for a return type that I did not specify, so I wanna say int. So I run the (indistinct) say zero. If we come back up top here where I added an ingredient, we should see one right after we see salt. So we'll see one then chicken, of course it was cleared out inside of the do stuff method, the ingredients were. And then of course we printed the number of ingredients too. So we have a singleton, we can have properties. We can have various different items in here. We can have functions, but this is now traded as a singleton. I can not create multiple instances of it. And if I were to print off, if I have two, print ln, favorite food equals equals favorite food. What do we think would happen here, is this equal to itself, is this the same instance? Let's take a look here and we'll see down here, we have a true, so these are actually the same exact objects that are stored in memory. And that's how you can create a singleton in Kotlin. To create a constant inside of Kotlin, there are a few ways to do it. The first is a local constant. Now the is actually let me say, there's a const keyword for helping to find a constant. But what you'll see here is the val is only allowed on top level const, excuse me. If we get rid of file here, we'll see that const is not applicable for a member function or properties in that case. So we'll need to get rid of that. Now, constants are usually defined in uppercase and separated with underscores is kind of the defacto standard for many JVM based languages. But let's say that we have a constant, that's a max age of 18. Perhaps this is a application for a children's application and we want the max age to be 18 for whatever reason. Now I can use this inside of here, I can say max age and I can do something with it and so forth. It's a val, it can't be changed, so it's a mutable. Now, if I'm back inside of the main function, I can still access this though. So let's say view user. If I still want it to be locked down, I would have to actually provide some level of modifier on it to see 'cause we can access max age right here. If I didn't wanna access max age, what I would need to do is throw a visibility modifier on there saying, hey, this is private, which is private to just the scope of this class. Now main, you'll see here, we don't have access to. So can I access max age, it's private inside of user. So that's the first way that you can do it. Now you can also create this again is all is scoped to the instance of the class. So if we were to see max age over here, let's say user.max age, this max age is scoped the instance of this user here. So it's instance of this user. So if you want it to be a more appropriate constant, as you see in our languages, you would think to do this, but what we'd actually need here is a companion object inside the companion object. We say, we want to say const, val max age equals 18. Now inside of here again, we can access max age very easily inside of our actual class. It's local to the class we have access to it. But however, if we say user max age, you'll notice that we don't have a max age on user. But we can access it because it's inside of a companion object, it's scoped to the actual to be a Singleton as part of the user class here, it's kind of a Singleton scoped for the value. So it user.max age. And there we go, now we can actually view the max age, which means that we can use it outside in various different locations. So it's basically almost users almost treated as some type of namespace. Almost, you could see it as that way for max age. So again, we have the local type we have inside of the companion object. Now also a lot of times you'll notice an applications, many people prefer to put their constants and maybe to a new file, they'll call it constants. constants.kt. And then we'll do is we'll actually create an object and they'll call it constants. And you remember when we use the object keyword, that's a Singleton. And so what we can say inside of here there is const val max age equals 18. And then we might want to say, const, val, whatever minimum age requirement is five, for whatever reason. And then back inside of our main class, we can then use that as we've seen in other languages. So constants.max age, we have constant.mean age that we can also access as well. Now, these all don't have to be inside of objects or classes or anything that can be also at a top level. So it can just kind of be floating on their own in their own files. So maybe we wanna have implementation that looks like this. Say, const val max age equals 18. Now this could be referenced over here. There's max ages in the root of the file. I can be over here instead of user. I could say something like this, max age. Now this is again, when we go to implementation, it's going to this root level file over here. Now, which one should you use? I prefer to keep any of the constants that are applicable to the user inside of the user class. So I would have a companion object for me, and I would put const val max age, if this was only pertinent to the user. If I have a constant that is going to be shared across many different parts of the application, at that time, I might put a create a constants file where it's an object and I call it constants. And then I have const val, et cetera, ABC, whatever the value is, and it could be food or whatever. And then I can access that from anywhere inside of my application using the constants as kind of a default standard there. So that's the way that I would do it. If you're going to need constants, I prefer to scope them to the class that they have. Again, if I don't need this constant on the outside, I don't need it visible on the outside of this, then what you can also do is just go ahead and you don't even have to use the companion object if you don't want to and you can just mark this as a private and that can be just stored as a max age. Now, what you so think about it as if you were on too, this is going to be stored around as a class, part of the class as well. So if you don't want that as part of the class, you can just put it inside the companion object and you can mark this as private inside the companion object. It will still be visible inside of your class. So max age here, but if we go back to the main implementation and we say user, and we create a new user again, and I try to access that user max age will notice that it's not available because I've marked it as private. So now if I remove private and I go back back to here, let's see we have access user.max age. If we go here, private user.max age is not, we can't even access it inside of here, even though it's scoped to the class V in the companion object, I don't have access because we've slapped the private modifier on there. So again, just to recap, if you wanna have your constants, I prefer to keep them scoped locally to the class that they belong to. If they can be accessed and are usable by many classes and have kind of cutting concerns, I will then usually put them in some type of constants file. I rarely will ever use them at a root level, such as this const val foo or whatever equals var. These are something things that I don't do, some of them, because these are kind of just randomly floating around in some unknown space and don't seem to be scoped too well. I have to keep things organized in my application. So it depends on what your use cases, but that's how you can create a constant in Kotlin. Let's say you're building a wizard to create a new user through some type of screen. And you know that you want a to store the favorite city of a particular user in a variable. And so you could say it's a string. Now, by default, we can't just leave this uninitialized, we're gonna get an error here that says the property must be initialized. And so we could say empty string, now that would work. So what we could say here is when we print this to the screen, we don't wanna say, so say Donn Felker's favorite city is, and then we'd print the favorite city so forth. We can print the favorite city. So let's go back to the main file here. And inside of here, what we can say is user, we'll create the new user and it will print the user to the screen. And when we print it, we'll see Donn Felker's favorite city is blank, but now, however, this isn't really useful. Kotlin does provide us with the ability to say like, hey, I know I'm gonna need a variable and I know that I need it populated, but I kind of know that it's not gonna be populated just yet, so just go ahead and trust me, I'm going to initialize this thing, but I'm not gonna do it in right away. So basically as soon as it's variable, it's properties declared, it's not ready to use. And I'm telling Kotlin, hey, that's okay, because I'm going to initialize it later at some point. But let's assume for some reason that I don't initialize it. So what's gonna happen here when we call the two string method, let's go back to the main file. So we haven't told Kotlin about the favorite city at all. Now, if we run this again, what we're going to notice is we're going to get Kotlin blows up here and throws an uninitialized property access exception. And it says that the late in it property from my favorite city has not been initialized. So Kotlin's basically letting us know like, hey, you told me that you were going to initialize favorite city at some point before you used it. However, you tried to use it down here on user line eight, which were on here. You used it down here and it wasn't initialized. We're not allowing that to happen, we're gonna blow up, and Kotlin throws the uninitialized property access exceptions. So now the nice thing here is easy to fix. So this allows me some kind of some safety to know that my application is working correctly, that I've actually initialized this because I do need a favorite city, I do need to populate it, and I want it to blow up if it doesn't. So now it's that user, that favorite city. And I could say, let's go ahead and go with it, Newark is a great place, so Newark and the wide run. And that's not really my favorite city, but we have Donn Felker's favorite city is Newark. Now we don't get a blowing up anymore. So I could even set this to an empty string. That's fine as well. But not at least that's been initialized, I've told Kotlin like, hey, I could say Minneapolis, which is one of my favorite cities and we'll print off here. So we'll see Minneapolis and we'll see it's populated. So that's one way that you can do it with a late initialized property inside of your application. This could be a string, it could be another object, it could be any of that nature. You're basically telling Kotlin, hey, I'm going to need some type of variable that I don't know what it is yet, but I do need it populated before I can use it. So I'm not gonna initialize it here, it's just gonna be initialized later, so late init, so it's late initialization. So just if I don't initialize it, blow up. Now, one important thing. If we say, hey, well, this is a mutable property, I don't want it to be immutable. Well, if we try that, what we'll notice here is we get this error that says the late and init modifier is only allowed on mutable properties. And well, if we think about that, the reason why is because we have to mutate it as soon as, whenever we wanna change it. So it has to be a mutable variable. Otherwise it would be defined immediately. So as soon as it's defined, if this were this way, of course, we're gonna get an error. But if we were to define this as NYC, it would work just fine. But since we are using late init, it has to be a var. And that's how you can define a late init and use it in Kotlin. Let's say that you wanted to have classes nested with init themselves, Kotlin supports that. So let's say you have a class called vehicle and the class vehicle has a brand and the brand is gonna be string and we'll just default that to unknown at this time. Now, perhaps a vehicle also has a type of a steering wheel class for whatever reason. So we have a steering wheel. So we actually put this class inside of the vehicle class. And then I could say something like this file name, or we made some var or whatever we wanted to do. And we could say the steering wheel and inside of here, we could even provide a function of some sorts. Let's say, fun info and then we could just say, could be a print line of some sorts of all inline function. And we would say name, we print the name. Now, if we wanted to use this class, we could actually very easily come up here and say, val steering SW for steering wheel, say vehicle.steering wheel, create a new instance of it. And then I could say sw.info. And if we were to run this, we will see that it prints the steering wheel, which is we see here. Now we can also, we're not limited to just one class, we can have multiple different classes inside of here. So we could have a class and we could call this one a transmission. And perhaps we just, for whatever reason, we're building some automotive software and we're going to have a var type and it could be run a default, this to automatic. And then of course, maybe we might have a function called shift and we'll just have it print off something like that say, print, print ln. And it will say the vehicle has shifted. And what now we can do is of course we have the steering wheel where you don't have an instance of the transmission, we would say val transmission equals vehicle.transmission. Of course, in that we'd say transmission.shift. And of course, we're gonna see when we run this, we'll see the steering wheel and then we'll see the vehicle has shifted. So now each time if we were to run this multiple times, we'd see the vehicle shifted twice, very simple. So we can actually nest classes inside of here. Now notice we haven't done anything with the brand here. So we could also have some information about the info function here and this one is just print off the brand here of the vehicle. And the brand of the vehicle will kind of print off there. And let's actually set this to a var 'cause we know we're probably gonna change that as well. So maybe we wanna do something like this val vehicle equals vehicle and move on to change the vehicle to brand equals Fiat vehicle.info. So we'll print it, we'll see Fiat and then we'll see the steering wheel and the vehicle shifted and the vehicle has shifted. So that's how we can nest classes inside of Kotlin. And it's useful for when things are somewhat related and you wanna kind of keep a class hierarchy accordingly. So he might have a vehicle. And inside of that, there's a steering wheel. So as we're creating it, we can kind of see that these things are related. So there's many different related domains that you're gonna encounter in software. This is one way to organize them with nested classes. Kotlin is nested classes are easy to create. Inside of another class, you can have another class to create different types of hierarchies. However, there are times when you would like to access instance variables and methods and so forth inside of an nesting class. So let's assume the steering wheel, we wanted to print the brand name. So we would say brand of car is the brand of the car, a vehicle with a boom name. We'll call it the steering wheel and we'll call it with leather, say leather with a leather steering wheel. Now, notice we have an error here, brand is not accessible. The reason for that is by default nesting classes do not have access to the member variables of the outer class. If we want to do that, we need to specify that this is an inner class of vehicle. So vehicle has an inner class and the inner class can now access these variables. So, you can see brand is the brand new vehicle. So let's go ahead and get rid of this. And then we can say, Fiat is the brand, vehicle info. Now we see here, the steering wheel is the constructor of steering wheel can only be called with the receiver of the containing class. Now, what that means is because steering wheel has access to the member variables, it's gonna have carries a reference to the outer class. Meaning that we can't use the actual class name vehicle, we actually have to use the instance because string was gonna carry a reference to it. So we have an instance of vehicle here is called vehicle. So I'm just gonna go ahead and delete this 'cause it's the capital V, which is the class name when we go to the lower case V. Now, if we wanna just change this and call my car, we can do that, so it's called my car. And now I can say my car.steering wheel and then we're gonna actually see, it's gonna create an instance of the steering wheel class. We're gonna see steering wheel.info, which is right here. Now, if we go to info, we'll see it's print off the brand of the vehicle and let's go ahead and run this. And what we will see now is that when it runs, it says Fiat, Fiat is the brand of vehicle with leather steering wheel. Now, since the transmission is still a nested class, we cannot call my car.transmission. So the reason for that is it's a nested class. If we wanted to create a transmission, we would have to say vehicle.transmission. Now we wanted to make this a inner class, of course, we'd slap on the word enter. And now of course, this is not going to work. we have to use an instance because the transmission is going to have a reference to its outer class. So in that case, when we create an instance of transmission, it's gonna keep a reference to the instance of my car here. And that's how you create an inner class, which can reference an outer class in Kotlin. Often during development, you'll need to create some type of construct that shows you what different types you have to work with. And for example, we might wanna have something different account types. Maybe we have various different users who have account types and they look something like this. So we have one that's a bronze account type, we have a silver account type. There's a gold and there's a platinum. Very often you'll see these, for example, the credit cards or different types of gym memberships or something like that. And if you're modeling something like this, you'll wanna create a user defined type. And that's typically done with an enum. And to define an enum, you will use the keywords enum class. And then for example, I'm gonna call this one account type. And then by default, inside of enums, if you give the names of the types, which is your user defined type, you uppercase them. So we'll say bronze, and then they're separated by commas. So silver, gold, platinum. And then if I wanna use the account types, which we'll get rid of this now, I can just say account type.gold. And now I have an instance of gold, so I can start representing something like that. So if I were to just to print line this, and these are very useful inside of various different if statements and conditional clauses and so forth, so we can see gold there. Now, this is something in Kotlin. Anytime you're interacting with an application, a lot of times you'll be talking to API and perhaps you'll get a value down a that's gold like that from an API. And so you need to create an account type off of that. And to do that, you can use the account type that value of, and then perhaps you would say, so let's say we had a string that was given to us from an input somewhere, maybe an API, and we'll call this account type from API. And then we'll just call it gold. And if we wanna in turn this gold right here into an actual account type, then we'll say account type, we'll see value of, and we'll say account type from API. Now, if we've run this, we're gonna run this here and we'll see that well, that didn't work. There's no enum constant by the name of account.gold, but we do see one here. So what's going on here? Well, the reason is, is because this is an uppercase value. So we can just say here to uppercase. And if we run this again, we'll see that it runs a compile is just fine. That gives us our account types. So now we can actually say print line and we're gonna actually say account type, and it'll say gold as we see here. So now we actually have an instance of gold, which is nice. So we can actually account type, does this actually equal and account type of gold? So this should return true, which we get running and say, it's true. So now we can actually enter work with these different types of enums. Now, a lot of times this is very useful inside of conditionals and different types of things like that. So you might have your user class, which if we go back over here to our typical user class that we have, we might have a user that's associated with account type. So we might have a var account type, and that would be an account type of whatever, and we might wanna default it to account type.bronze. Maybe that's what it defaults to, or instead of doing it here, we could actually just do it, put this all up inside of the constructor and have it use a default value. So account type, and we don't need that and say account type equals, but we do need that. Excuse me, account type people's the bronze. And then if we decide to override it, for example, we could say val user equals user, and then we don't wanna say Donn Felker. And then I don't have it provide an account type. But if I would, if I'd like to, I can say, well, he's actually going to be a platinum account, for whatever reason, he's a good customer or something like that. And now I can actually start working with this. And then in my application, I can say user.account type, I can then do some different types of things with conditionals inside of that and then start working with it. And it gives us some user-defined types that are basically strongly typed. We're not working with strings doing string comparisons, and we can work with them accordingly. That's how you can create and work with very basic enums and define them in Kotlin. Sometimes it's useful to provide values associated with an enum. For example, if this was used for gym membership, let's assume that if you're a bronze account type, you're going to then get, let's say 10% off. If you're silver, you're gonna get 15% off. If you're gold, you're gonna get 20% off. And if you are a platinum customer, you're gonna 25% off, maybe bronze is a single member, this is two, this is 10. This is more than 10. So start getting more discounts for more members on your plan or whatever. So how would you define this? Now, what you need to do is provide a constructor for your enum. And so what you would do is you'd say val discount percent, and this can be any number of things, but we'll make this an int, it could be floats or whatever. Now you'll notice we have a bunch of errors here because the enum has no default constructor. So we need to use the parameters. But if we were to provide a default constructor, maybe we said 10. Now all of these would work and they would default to 10, but let's not do that. Let's make the compiler work for us by defining that we actually need to have a particular value parse d into each individual enum. So here, we're gonna say 10, this one's going to be 15. Then this one will be 20 and this one will be 25. So there we go, pretty simple there. And we can go ahead and delete all this stuff out. And now, if we were actually to print line this to the screen, we would see that we can still do account type.gold, and we'd still be able to print gold. And then we can actually say, discount percent. We can actually print the discount percent. So for your to run this, we would see that we have gold and 20. Now you're not just limited to a particular number of parameters, you can do another one here. So we can say val, say number of subscriptions and this could be an integer value. Now you see here again, we're having the same problem. So maybe this is the minimum number here to reach this one, this one you have to have a five, this one you have to have 10 people. And this one you have to have at least 15 people. And these are perhaps the minimums. And now again, I could then say, all right, well, what does this one five for number of subscriptions? And I can start providing a kind of a known simple little data type structure, a custom data structure here that says, hey, this is enum class, it's an account type. And it's gonna be bronze, silver, or gold, et cetera. And it's gonna have these discount percentages and subscription percentages or a number of subscriptions that are required accordingly. And I can start to define that here in code, so it's strongly type. And of course, as usual I could actually, perhaps maybe even say I only know this because it comes down from an API and I can say account type.value of, and again, I'm gonna use gold to uppercase and I can even do something like this. Maybe it's regular case like that. And would we be able to see this, let's take a look here and we can actually just say value. Actually, this is do instead of gold, let's do platinum. And we'll run this again and we can see that we have platinum. And now if we actually do something like this, print line, I should just copy this twice, it will say discount percent and number of subscriptions. So basically what we've done at this point in time, as we've gotten a string here, we have acquired the account type enum and then we're able to print the values off that. So let's say that the value platinum came as a value from an API or a database call, we could then create this type at runtime. It's all right, well, they are platinum account type and it's a 25% discount and a minimum number of subscriptions is 15. So that's how you can use constructor parameters with an enum in Kotlin. Let's use the discount example again, and let's assume that you need to provide a discount for bronze or silver, gold or platinum, but you had to be calculated. So what you could do is you could define an abstract function and you could say, discount percent, let me say calculate discount percent. And it's gonna return a, it's gonna be a function and it's gonna return and integer. Now what you're gonna see here is I spelled abstract wrong. And now, as soon as we get that fixed, we have a bunch of errors here saying class bronze is not abstract and does not implement the abstract member, calculate discount percent as defined inside of this type. And the same thing happens here for silver and gold and platinum. So what we can do is we can add some curly braces here, and then we can implement that interface. And actually we'll see, assuming implement that abstract class, the abstract function. And we can either just do it inline here, or since this is gonna be... If you're gonna perform some type of complex calculation, you could do that in here and you don't perform your logic and so forth and then finally return whatever five or whatever, whatever you've calculated, or if you just know what's going to be very simple, you can also inline it. So we might be able to just say five here. And the same thing would happen here. Again, I would need to implement that member here, and then I could say 10. And what I'm gonna do is just copy this over 'cause this is easy. That way, I have to space that over what 15. And again, these are different percentages than I had last time. But for the example here that are all the same here. So calculate that accordingly. And it looks like it's expecting a semi-colon at the end of this after we've kind of built all that up. Now, what I can say is if we use that same type of example, again, maybe we have something coming from the API and it's just all spelled kind of really weird with weird casing. We wouldn't wanna say we have the account type and we got that from the account type dot value of, and then we would take this from API, go to uppercase, now we have that type. And then we could say print line account type.calculate discount percent. And if it's gonna be gold, which we could run it, we should see that we're gonna get 15, which we do. If we change this to numb, which we're just doing weird casing here to mess with it and platinum is 20. So that's how you can implement an abstract method inside of an enum. This will be very useful, again, if you are not sure what you are going to return. Of course, it's gonna end and you need a lot larger calculation, you can use a larger block here. So you could do a bunch of work here, and then you're gonna return whatever that value is. If it's, for example, maybe it's gonna be a var percent and you wanna start off at zero, no discount percent, and you get to return to percent here. And perhaps you're gonna kind of do calculations inside of here based upon maybe some other constructor parameters that you have inside of the account type, which we don't have any here. But if you did, you can perform all those calculations here. And that's how you can use abstract function inside of a Kotlin enum. You can also iterate over all of the types inside of an enum very easily by using the account type.values. So you can use the values method, which has built in for you. You don't have to define that, it's just part of the enum built in for you. And so let's say it's for, say type, let's actually say account type and these types here, then I could say print line, and then I could say account type. And if I were to run this, then what I would see is that when I run it, we're gonna see all the types here print on new line, bronze, silver, gold, and platinum, and the values method returns in array of those account types. Now, because it's an array it's so it's a collection that's built into Kotlin, we also get to have access to all those nice little helper functions such as Foreach. So I could do the same thing here and I could use print line I could use the kind of built IT. It's kind of one line all of this together, instead of having three lines of code that does the same exact thing. So you can iterate over each one of those. And that's how you can integrate over all the values inside of an array, really of enum as an array, very easily. To add a static method to an enum, what you wanna use as a companion object. But first you wanna terminate the different enums by providing a semi-colon at the end, then you'll wanna say a companion object. And for example, here, we could actually have a function that says get account type by name. We can just parse in the name and it's a string, and this could just be valid. We could just use the value of named.to uppercase. And you may be wondering what this is for. It's a kind of a nice little syntactic sugar to say account type by get account type by name. And I could just parse it and gold. And so I can say account type, and now I could actually print this to the screen and say account type. And then if we hit run, we should see that we're going to get gold on the screen, which we do down here on the bottom in the output window, I could change just to platinum, which is different casing or whatever. And then of course, for whatever reason, if I just spelled wrong, it's going to blow up because we don't have a value with a constant of this type, which is what we would probably want to have happen if you're trying to use that in your production code. Now, just an important note here is that Kotlin, doesn't have a concept of a static method. This is just Kotlin's way of kind of providing the same functionality out of the box. And that's how you can create a companion object method. It kind of acts like a static method inside of a Kotlin enum by using a companion object. Kotlin doesn't have a concept of a switch statement. Kotlin's switch statement is known as a when. So let's go ahead and create a user to work with real fast. And this user will have a first name of Don and the last name of Felker. Now the when statement looks something like this. You say when and then you provide the value that you're going to evaluate here as the condition. Say, user.first name say, hey, when the user's first name is equal to something and each line here will be a condition that we're checking. So when the user's first name is Don, then we want to go ahead and perform some type of action and we'll put that inside of some curly braces here, and we'll say, print ln, this is Don. And then we can run this. Now, if we run this here, we'll run and we'll see in the output window that we say, this is Don. Now, if for some reason we changed this username to Dan, of course this evaluation's not going to run, and we're not gonna see anything printed out to the screen. So what we can do is also provide a default brand, which is known as an else inside of the when and we can provide the brackets in here too. Print ln, this is not to Don. Now, if we run this again because user's name is Dan, which I'm sure there's a Dan Felker somewhere, we'll see this is not Don, so the else branch has now executed here. Now, one of the things, and you can put multiple different things inside of here. So we could do a print line, foo var, or do some type of logic or whatever. These are just basically blocks of code that you can do stuff with. Now, one of the challenges that you're going to run into when you first start developing with Kotlin is you're not gonna understand how to write a when statement. At least that was my experience. So thankfully the IDE helps you out a lot here. So you may be familiar with writing something like this, user.first name, equal equals Don. And then you may have this print line statement here, and then you're gonna have an else. And this is very common in all C style languages, which you're probably used to seeing many times now. So if we comment this out, we'll see here that Dan Felker. So we should see, this is not Don, let's run this now. And we'll see, this is not Don. If I change this back to Donn Felker up top, we'll see that this Don, so that condition will be run. Now again, I know that I probably wanna use a when statements very Kotlin idiomatic to do so. Now you see as I put my cursor here, we get this little yellow light bulb alt enter or hit this and say replace if with when and automatically it rewrote the code for us to a little bit different here. Now this doesn't look like the one that's down here, yet it does the exact same thing. And what this is basically saying is, hey, is this username first name equals Don. Now it's going to just say print line. Now notice how there's no brackets here like I have here. So let's undo what we just did before. If I have multiple statements in here and I said, this is cool. And then I come back up here and say replace the if with the when, the IDE will automatically provide the brackets for it. So if it's a single line statement, what will end up happening is when we replace the if with the when, it will then turn it into a single line in line statement. So it's easier to read, but it's also optimized it here because it says, hey, we don't have to evaluate the wind for the else here. So we're just gonna throw it in this item here, which also means we could just say, this could be Dan. Now at this point, it's getting kind of redundant. So we say, this is Dan. And if we were to run this, now let's run real fast. You're gonna see that it's gonna say this is Don. Now, if I put in Dan, say, this is Dan. And then if I put in, this is Dana, it's not gonna work 'cause this is Dana. So then I can rewrite this in my opinion, it's a little bit cleaner. I can actually just say, get rid of this stuff here. And what this will do is it'll valuate when I can put this inside the, the user.first name. So what this is gonna do is only evaluate whatever's inside of the first name variable. So what the saying is user's first name when it equals Don, go ahead and print this out here. When that equals Dan, print this out here. Now, when would you wanna not use that? That's a very good example. So you could actually say, well, when the user's first name is Don, then we wanna print this as Don. But what if we only wanna print it when it's Donn Felker. So user.last name equals, excuse me, we wanna go over here. Username equals Donn and user.last name equals Felker. Then at that point, we wanna say print this as Don, otherwise we don't. So let's go back here and let's change this. Again, this go to Dan, let's go to Donn Felker and run this. What you'll see here is it's gonna say this is Don, but what happened if we added just Felker, I misspelled whatever, and run it again. This is not going to run, it's gonna say this is not Don, because what this one statement is doing is using this entire line as an evaluation. And again, once it starts getting kind of long and goes after the initial line break, I prefer to put it inside of a print line here. So you kind of start evaluating multiple different conditionals inside of here using this when statement, and then of course you can have a fall through as the else at the end. You can mix having blocks of code inside of here, or you can kind of have one line evaluations and statements here, which will then evaluate these expressions and print onto the screen. You can not put multiple ones here on the same line, as soon as you need to do multiple things, you will need to provide a block for that. So that's pretty easy to do. You can just go ahead and provide that print all those, print ln and accordingly, and you can put some other stuff inside of here, print ln. This is ABC, whatever. And then that's how you can go ahead and use a basic when statement inside of Kotlin. And of course, this can be used with different types of things, such as primitives can be used to check different instance types. This is just a conditional very much like an if. And again, if you don't know how to write the when statement, you can always go back to the if statement, write your if statement, and then go ahead and use the helper inside of IntelliJ, Android Studio or any other Java IDE that's built by JetBrains to have actually have it. Go ahead and replace your if statement with a when statement. There are times in a when statement when it's used as an expression that it needs to be exhaustive. What that means is ln statement must be provided. So let's assume that we have at when statement based upon an account type. So if we have an account type and we'll just say account type.gold, and we can say a when statement here. So when an account type, and then we say gold, what we can actually do here is we can actually have some type of code here and we can say, return gold member. Maybe we wanna return certain strings based upon whatever their status is. So you can see here, we have an error and it says, hey, this is by default, a unit and returns a unit, but we're telling you to return a string. So what we can actually do is have when return of value. So we actually, val equals message. So I wanna say a message off of this when statement. And what you'll see here is it's telling us, hey, well now when the when expression is used like this, it must be exhaustive. So we have to actually handle all of the different scenarios because this val must be populated with something. It has to be something. And the compiler knows by default. Well, account type has different values, it has bronze and silver and gold and platinum. If you parse in, if this is platinum, it doesn't know what to return. So it's telling you, hey, you have to return all of these things inside of here. And what it's saying is here, you can do this in one of two ways, you can add a necessary bronze, silver, or platinums, or add an else branch, so there's two ways to do this. We could say, hey, a gold member, or we can say, what would we say, else and we could return a regular member, something like that. We'll see here we have, it looks like it's returning, we don't need to add the return statement there 'cause it's actually used as an expression, so we don't need to return anymore. And then what we can do here is say, print line message. And when we print this, well, you're gonna see that they are a gold member. Now, if I were to type this as platinum, we know that this is incorrect and it says regular member. Now that's not correct here. So maybe what we wanna do is if we say, if they are gold, then anything else like that, where maybe platinum is a highest level, we'll say platinum member. Platinum. And then inside of the classroom, we can say not eligible for special access, whatever that might mean. And if that's basically saying this is maybe a string, that's going to be displayed to the screen. When they're platinum, then use this, otherwise for all other instances, if they're gold, silver, or bronze, then we want them to only see not eligible for special access. Well, here they're a platinum member. So we can actually say platinum member access allowed. And then when we rerun it, we'll see platinum member access allowed. And then we'll come up here and changes to silver and we're rerun it. And it says, hey, you're not eligible for special access. Now that's when we could do it. One way we could do it now, we could also change this and provide an implementation for each individual one. Now it doesn't matter, we could type bronze here, we could type the different ones here. This would, of course, maybe there's a bronze access, there's bronze member access. And so we have this, we'll change this to silver. And notice we still have the squiggly align up here, it's still complaining that we don't have everything that we need and then we can change this to gold. As soon as I provide the gold, I've provided all of these values, gold member access allowed. Now the beautiful part about this is regardless of whatever I've typed inside of here. And I look this telling us we can inline this here, we can inline the message, which we don't really wanna do, 'cause we're assuming this comes from an API or database. The beautiful part about all this. Once we run it, we'll see that we get the correct bronze, silver, gold is that, and just, excuse me, I'm gonna reorganize here because it just makes more sense and matches the code, is it when we have account types, they're gonna have a value, which means later on in our application, if we decide to, for whatever reason, our business needs change and we add a new level Onyx. Now our application will not compile. The compiler is gonna check this and say, hey, this when statement is not exhaustive. So if this account type is in another class file or another file or application, which it's most likely is going to be, and we've added Onyx because it's coming down from the API. Cool, we have Onyx now, we're gonna to add that and we're to try to compile our application and the Kotlin compiler's gonna say no, no, no, no, no, you can't do this because you have to make sure that when expression is exhaustive. Now of course, that's, of course can be gotten around by if the else is here, then say unknown member type, or whatever and you can have that and will be, you can add it in there. So if you add another one, you can say pink and whatever that would be, maybe it's special, cancer awareness or something. It pops in there, then you can have that inside there. The different types that you have in here, again, this doesn't matter if the bronze is the bomb or whatever, the expression will be evaluated and the message will be returned at that point. And that's how you can work with an exhaustive versus non-exhaustive when statement. And again, the non-exhaustive one is going to be perhaps if you just want to perform some type of evaluation. So this would be non-exhaustive one account type. And I could say gold. And I would say, maybe just print something to the screen. This is gold. Now this is non-exhaustive here. We're not required to provide an else statement. Because this is using the expression, it's gonna be returning a value. We have to provide an exhaustive approach. We have to exhaust all the options. So we need to provide all of the options or also provide an else at the bottom so we know what to do there. And again, there's no Onyx here. So it starts complaining and so forth. And if we delete the Onyx and we had to go to platinum there, we fix our compiler problems. And that's how we work with the exhaustive versus non-exhaustive when statements Kotlin. In programming, it's very often that you'll have in a class, this you simply to hold data. And so what we might have is a class called person and as string and a last name of string, and it doesn't maybe even have a body that's just has no properties or any of them be no functions or anything like that. So this is a person and we may define person one as P one. And we would say person, and we'll save their first name is Don and their last name is Felker. And I'm gonna create two of these, so P one and P two. Now, if I were to actually say, are these equal to each other, you might think of course, that these are equal to each other. They have the same contents, they must be equal to each other, so let's run that here. And what we're going to see is that it's false, these do not equal to each other. And if we are to print these out, we're going to see P one and P two actually have different little codes here at the end, which we can get to that in at a later time, basically showing that, look, both of these objects are actually different. They're different is not actually comparing value equality inside of them. Now, thankfully, the language designers at Kotlin have thought about this and said, well, there are times when we just wanna carry data around. And so if we have a class like that, you can then use what's known as a data class. And to use a data class, you'll just slap the word data at the beginning and then you also have to have at least one parameter and all parameters must either be val or var. And so we've added that here and now we can actually say print line P one equals equals P two. And if we run this, we'll see that both of these values are the same. And what will happen behind the scenes is it's actually comparing the values. Now there's a couple of things that are making this happen. The Kotlin compiler is actually overriding and creating the equals operator and the hash code operator, as well as the two string and a couple of other functions, which we'll talk about soon. So if we're gonna take a look at the equals and hashcode is what's kind of helping us with the equality here. However, with the print line, we can actually, as we remember print line will call the two string method by default. So we can say P one and then what will happen if let's actually go back a little bit here and take this off. If we run this just as this is as a regular class, we'll see that we have, if we do a print line, we'll see person at 49476842, so it's not too helpful. However, if this is a data class, what you'll notice here is that the data class has a two string method, which has implemented for us, which is a very nice format here, which has person first name, last name. And let's say we add the age inside of here. So val age int, and we'll put some numbers here, 30, and let's just do the same thing 30, and we run it again. You'll see that the print line method also outputs that parameter. So it'll actually output each of the parameters as a value in the two string method. So this is all generated for us. The equals the hash code and the two string. And this is how you can create a data class inside of Kotlin. Now you can also have functions inside of here. So you might be able to say full name. And this is very similar to our other user object, which we've seen before. This will return the string, and this will be returned like this. We'll say return, first name and then last name. And we can actually come up here and print the full name. And if we run it, we will see Donn Felker and of course we can have do other things such as this such as name, length. If we wanna have a method that perhaps returns an int, and all that does is full name.length. And we can do the same thing up here, full name or name length, and that will provide a full length of the name and characters at the character council at 11. And that's how you can create a very simple class in Kotlin, very simple data class in Kotlin and how it can perform data equality and so forth. If you would like your data class to be constructor list or seem like it's constructor list, you will need to provide default values for each of those. So you could say first unknown here, and that was gonna go to first unknown known here, last unknown. These are just values I'm typing in there. They could be anything, an age we could just default to zero. Then of course, it's getting kind of long, so it's gonna put these on a separate lines. So now we have our class kind of broken apart into separate lines. And then if I like to create a P three up here, I could very easily do. So Val P three equals person, and I have to provide any values because they're all default. Now, again, I can't have another value in here. Val is just Fu, which is a string it's not provided. We'll see, we get a bunch of errors here, even up here, because there's no value unless I defaulted to something. But at that point, these are all vals. And then of course, if we print ln, the P three, delete these, you're going to see that we have the default values that are output. So first unknown, last known, known, and age zero. And that's how you can simulate having a parameter list constructor in a data class. Kotlin data classes by default will generate what are known as component methods. So if P1, we'll see component, we have component one, which is a string, and this is going to be the first name P one dot component two, is going to be for the second item in the parameter list, which is a string. So in the order in which they occur is what the components will occur. And so this would be val first name, val last name. What do you call that? And then as you can see here, we do not have any more components because that's all. So P1.component, component one, component two. However, if we come down here and add another item here, so Val age, enter int, and let's provide a value up here. 30. Now, if we insert P one dot component. We see we have another one. And this one is an integer type because this is the third parameter up here. This is the Int. So it's the third component in the parameter list. And so we say Val age equals component three. And these are generated for you behind the scenes by the Kotlin compiler. So if we were to print line all of these things out there, we could say first name, and we say print line, last name and print lane. We can not do age. And if were to run this, we will see Donn Felker, 30 being printed out to the screen. Now, of course, we could just replace this with P one dot component one and P one dot component two. And we just copy this here, save time. It would do component three and we can delete these other ones here and delete all these. And if we run this, we'll see the same thing we just saw before: Donn Felker, 30. So component one, component two, component three. Now, if I switch this around, so I'm gonna put 30 here and see if I'm gonna get a competitor error at first, so I'll say 30. I need to switch the parameter order down here. I can actually do this. Then component one, two, and three are gonna be different. Component one will be Don, component two will be 30, components three will be Felker. Let's put it back to how it was before with the age at the end. And one more thing that we can ask you actually do here too, is we can actually specify these in correct order. So let's do this. Let's just say age equals 30, last name equals Felker. First name equals Don. And actually let's even flip these two. Let's flip these two right here. So we're completely out of order at this point, but however we're using named parameters and Kotlin is gonna figure that out for us in a way anyway, because this is the way that they're defined. So these are the way that the components are going to be adjusted. Not in the order in which they're parse d in, but in a way that they're defined in the data class. So first name is component one. Last name is component two and age is component three. So even if I use name parameters in a different order, Kotlin will figure that out behind the scenes and correctly render them in the correct order with the correct component. As we can see here, component one is Donn Felker. Here is component two and component three is 30. And that's how you can work with the components that are generated inside of data class. And I do have one more use and that's going to be de-structuring, which is coming soon. One of the main reasons for the component values that we see inside of data classes, such as component one, component two, component three is for de-structuring. So component one, references the first name, which is the first parameter as defined inside of the data class. Component two will be the second parameter here. And component three will be the third one here. This is used for de-structuring. So we can say val, and we can say first name. And then we say last name and we can say age. And I'm gonna say equals person. So as we've defined the person here on line two, what we're doing here is we're actually defining these three variables and we're de-structuring the person object. And what's happening behind the scenes is Kotlin is using component one, component two and component three to shove it into these variables here. So now let's print line each one of these to see what they look like. I'll say first name print line, last name, it will say print line, age. And if we run this, what we're going to see here is that we see Donn Felker 30. So this is great. Now this isn't based upon the name of these variables. So I could actually switch these around. I could say last name, age, first name. Now watch. If I run this, it's not gonna be Donn Delker, 30 anymore. It's not gonna print these in that way. So it's gonna print 30 Donn Felker. Now, the reason is it's based upon the de-structuring. So this is component one. This is actually gonna be the first name. So last name right here, which we print right here. This is the second one, that's Don, that's correct. Then we have first name, which was actually using component three's location. Component three, so we're just gonna see first name, component three's location, which is age, which is why we see 30 printed first here. And the last thing that was printed, I have listed as the name of the age variable, is showing Felker because that's actually the second component here. So this doesn't matter what these variable names are. So in essence, I could actually just rename these two component one, component two, which I would have, these names don't really mean anything. And I would never do this in production, but this is just an example that it doesn't matter the name of the actual values that you're destructuring into. It'll still print those values. So let's go ahead and move this component one, component two, component three. Let's print them in a proper order again. So there was Donn Felker 30. So again, I could just call this F name for first name. I could call this L name for last name. It could be, you know, whatever casing. L name, and then we could call it go, and this could be person age. It doesn't matter what, as long as whatever is in this third component is this is the third component is gonna be shoved into this variable here. And that's how you can de structure a data class into values here, just in one quick line, because if you were to do it the other way, you might have to do something like this. First name equals person dot first name Val, last name equals person dot last name, and there's nothing wrong with doing this. It's just, you can also use destructuring to your advantage very easily by turning this three lines of code into one line of code. And that's de-structuring data classes in Kotlin. It's very often in development that you'll need to create a copy of some data. So perhaps you need to create a copy of this person and you'll want to create a sibling. And the sibling will, then you can easily do that with the data class with the built-in copy method. And the copy method will automatically create a copy of everything inside of the class and give you a new instance. So if we were to create, to print this to the screen here, we would see person and then if we were to come down here, we could see sibling and we would run it. We would see that the two classes had the exact same data. Now it's very often, you may only wanna change a couple of the attributes and you can do that with the named parameters. And so I could say something like Sam. Change the first name to Sam. And if I were to run that here, we'd see the rest of the data stays the same, except for the first name changes to Sam. And maybe Sam is perhaps an older sibling and Sam's age is 44. And we were to run that. And now we could see Sam Felker age 44. Now this is an easy way to do this. This is very useful if you have a very large object graph. So perhaps you have a person, or maybe you have a, perhaps a data class with a order. And that order has an amount which is an int. And then the order belongs to an action needs to be, have a Val or a VAR amount, and then actually belongs to a person which could be the customer. And that will be a person instance. So if you were to do this, you could actually come up here, let's go ahead and create this order. So we say Val order. And this order would be, let me say the amount is equal to 100 and the person equals let's say it equals the sibling. Excuse me, the customer is the sibling. And of course, if we print line this, we're going to see that the order has the amount of 100. And then inside of there, the customer is that person instance with the age of 44. So let's say I wanted to copy that order. And so I say new order, and I know let's say the customer is the same, but I just wanna change the amount. I can say order, and I'd say the amount is 200 this time, perhaps they purchased a different product, but it's gonna be the same customer. I could easily do that. Oops, excuse me. We'll say order dot copy. And the amount will be 200. And once we get the 200 here, we can print line that for the new order. And then once this runs, we'll see that we have another order here for the amount of 200, with the same exact customer. Now, this could also be, you know, let's take this something a new route here. So let's say Val new order two, I could say order dot copy. And perhaps I want it to create another order of the same exact thing but this time I have a different customer. And this time the customer would be the person. Now I can say print line and we can see what this would look like. And it would say new order two, and we'd run it here. We can actually see that the order amount is still 100 but this time the person has actually changed. And that's how you copy a data class in Kotlin, period. It's important to note that this is not a full deep copy if you're using lists and so forth, a lot of these items will be shallow copies. So be careful copying lists this way. You can build your own data classes, but there are a couple of useful data classes built into the standard library. One is known as a pair and it's just a pair of values. So you could have foo and then you could have bar, and these are just a pair of values and they're accessed via the first and second parameter values. So you can have a pair of value. So you don't have to create a, if you have two values you need to parse around, you don't feel like creating a custom class for it, you can use a pair. And of course, when you print line these to screen, you're going to see that we have the following. So we have the first just foo and the second is bar. Now there's also another way another syntax you can use for this. And you'll see convert to two here. And this is a syntax using the two syntax. So basically we have foo is going to match to bar. It's a pair. So this is another way to create a pair is foo to bar. It's a syntactic sugar over the application. And this is inside of the Kotlin standard library. And you can look at the implementation here, creates a (indistinct) pair from this and that. So this and that is basically the two values. So you don't have to worry about the implementation, but you can either use a pair like this or if you want, you can go ahead and use the regular pair method like this. We have a small error and there's our pair. Now there's another one here. If you have three values, you can call it a triple. And a triple is the same type of thing. So we can say foo bar is or whatever, and we have three values. And the same thing happens here. You can say triple.first.second.third. And these are all very similar. Now they don't have to be strength. They can be whatever you want. If you want them to be an integer and you want this one to be Boolean, you could do that. And this is up to you, how you would like to have them. And then of course, as we see here, the triple dot second is gonna be Boolean as we see right here. We see the third is gonna be integer, and then we can see we can also copy that 'cause it's a built-in data class, so we can copy these and work with them as they would with regular data classes. So these are the two built in data classes right now that are inside of the Kotlin standard library. Of course, more to come, if you're watching this in the future. It's pair and triple, they're both in the standard library. Adding a protected modifier to a variable in Kotlin is easy. All you have to do is inside of your class, add the protected modifier to your variable name prior to the declaration. So here I've added protected two favorite food. Now let's go ahead and remove that real quick and see what the cause of that happens is. Back in the main file, I've created a new instance of the person class, and this person class has a favorite food property on it. So I could set perhaps watermelon. Now this works just fine, works as a regular setter and getter. I have a property that we're used to working with. However, if I were to add the protected keyword here, this now modifies this variable. So it's only visible from within the current class, in any of the classes that it get inherit from it. So if we come back here, we now see in the main class, I no longer have access to set this value. And if I try via intellisense or code completion to see favorite food, we're gonna see that it's not there. And the reason for that is because it's now protected. So only this class and any of the classes that inherit from it are going to see it. So let's see that in action here. And I've actually already created another class called chef. And remember, I have had to add the open keyword so I can extend the person class. I've created person and chef. So basically a chef just extends person. Now, maybe if I have a chef class, for whatever reason, the chef can have a favorite food. So I'll say fav food, and we're just gonna say string. And so when I create a chef now, I have to provide a favorite food. And then what I can do is I can actually set that. I'm gonna create an initializer block, which will be called as soon as we create the class. And then I can say actually, favorite food. See there's favorite food. And if I go to the declaration of it's actually gonna take me right up here to line five. Now back here on line 15, I say favorite food equals my favorite food that I just parse d in. So that's interesting. I can access it from within side of chef. Now, if say for whatever reason, I don't want to allow regular persons to modify the favorite food variable. I only want it to allow the chef to do it. So I've not really restricted anything up here. I could still come here and say favorite food and I can still change here, but maybe I don't want to have a public API per se, that allows me or any callers to modify. So I don't ever want the person variable. So I never want the person not favorite food to be called. Now, if I were to call a chef, so, or create a chef, I would say, perhaps something like this, and we can say, Bob and Bob is 33 and Bob's favorite food is ribs. Then we can see here that now I can actually set the favorite food here, because this is being set right here, which in turn is being set inside the initializer block. But let's go ahead and assume that I want them to only allow chef to change it. So I create a function, as I say set favorite food, and they may a parse in the food. And then what I would do is this a favorite food equals food. Now we have an interesting error here. We're getting a squiggly line. And if we put our cursor over it, we'll see that we get an error of an accidental override. And what this is basically telling us is that, hey, Kotlin actually does us a favor here. When we declare a property up here automatically what's being created behind the scenes for other JVM based languages such as Java and so forth, are variables like this. Excuse me, methods like this. We have a set favorite food, which is going to allow us to parse in a value, which is a setter, which can be string. And then there's also a getter, so an accessor. And these two methods are created simply for, it's gonna return a string, interoperability with other JVM languages. Anytime you've worked in traditional Java environments, anytime you declare a variable, you need to provide a mutator and an accessor. So a setter and a getter. And this is typically the method in which you do it. You prefix the set, whatever variable name is and proper, of course, camel case. And then of course, if there is a getter, which are usually is in this case, you wanna get the value, you have the getter in the same format. So prefix it with the word get. So set and get, these are generated automatically for you behind the scenes. So Kotlin's saying, "Hey, we can't really do that. "You shouldn't do this. "We're already gonna generate this for you, so no dice." So I'm just gonna go and rename and set my favorite food just for an example. So now if I go back to my main class, what I can actually do is that the chef can actually set my favorite food. And maybe I wanna change that to celery. I don't know who likes celery that much, but that's beyond the scope of this. So we have a favorite food that set the celery. But now if we try to change the food to set my favorite food on the person, well, that method of course does not exist in the person class. So that's interesting thing. But what we can do is we can actually print with my favorite food from the superclass. And that's very easy to do. Say print line. And inside that print line, we'll go ahead and say the favorite food. So that's pretty simple. Now, if we were actually gonna call this from the person class, P dot print my favorite food, that'll work. And if we're gonna do it down here. C dot, let's actually do it two times, C dot print my favorite food and then same thing down here, print my favorite food. Because again, I am extending the person class over here. So the chef class is extending the person class. I then can call any of the methods that are available to it. So that are not private based. And I'm calling print my favorite food. Now for the person class, this is gonna show up as unknown because we are not able to set the favorite food of that person unless we're using a chef instance. So the favorite food is just going to show up as unknown for the chef we're actually gonna see it twice. So we'll say print my favorite food, it'll say ribs. We're gonna change it to celery. And then once we print it again, it'll show as celery. So this is us enabling to work with a protected variable. So if there's anything that you would like to protect from any outside callers, you wanna hold some state inside of your class, but maybe you still wanted to be available to other children classes via inheritance. Then you can go ahead and provide a protected variable and you can do that. And of course this works with objects and value types, et cetera. Now, of course, if we were to run this, here's what we would see. We would see unknown, which is the person knowing see ribs and we'd see celery. And that's how you can work with protected variables in Kotlin. You can also make a method protected as well. So we're going to use the same example here that was used in the variable protected modifier lesson. And so here we have the print, my favorite food method, which can be used back inside of this main class. So again, like I say p.print my favorite food. And when you say c.print my favorite food, and that's going to print the actual favorite food, which is called from the person class. Now, for whatever reason inside of your application, you may determine that you do not want this to be part of your public API. And what I mean by public API is a part of your class that can be called anywhere that's perhaps public, and this would be a public method. So we can actually access it publicly. Let's say for whatever reason you want it to only access this within the current instance of person or any of the children classes such as chef or whatever. To do that all you're gonna wanna do is slap on the protected keyword. And now this modifier was now applied the per print my favorite food to only be available from within this class and from within this class. So let's go back to the main file and take a look what happened. Now we can see that the print my favorite food is protected. So what does that mean? Let's take a look here and the auto-complete, we don't see print my favorite food anymore. However, if we were to go perhaps into the info class here, we can say, print my favorite food. We could still call it from within the person class. So give us a little flexibility here. And also any of perhaps other classes, perhaps that we wanted to set my favorite food and then print it for the console or whatever, we can call it from within any of the other children classes as well. So now let's say that's what we wanna do, we only wanna print it when we set the favorite food for whatever reason. So we can say, print my favorite food. Now we can come back here. And of course, if we cannot print or have anything to do with the favorite food from a person perspective, because maybe that's just part of the given, the way you're designing application, but we can actually say set my favorite food. So we know that the person's food was ribs before, but for whatever reason, we wanna change it now and change it to be a potato. And so if we were to run this now, what we should see happen is it's gonna be set from ribs to potato, and then it's gonna print potato to the screen. Let me see print potato to the screen. And the reason why is because inside of the set my favorite food method, we're setting the favorite food to a different food and then we're calling print my favorite food, which is protected inside of here. Now, of course we could, maybe there's no need for this to be inside of here. And maybe the food does not even need to be inside of here, which when you start thinking about it perhaps, the person, we don't need to know anything about the person having a favorite food. So maybe we just need to move both of these into this class down here. And this would both work here. So we're gonna see favorite food now, is not going to have a problem here. So what is it saying can vary by... Variable cannot be initialized before declaration. So what we can then do here. So we've trying to initialize it before, it's really been declared, there's a couple of things we could do here. So we just kind of go ahead and remove this and we could actually remove this as well, because let's assume that every chef needs a favorite food. So to get access to this and since we're mutating it, we're gonna change this to a variable. So throw the var word on there and now we can print it and now we can also mutate it as well. And if we go back to our main class now, we've kind of cleaned up our API a little bit. So we definitely do have our person class and we do have the chef class, and this will still run accordingly because the person is very kind of a just a shell of a class. And of course we print and we see potato. And what I mean by that is the person class just contains the name of the age. And it's just a very basic information about a person and we've extended it using inheritance. And we've created a chef and added a favorite food for that chef. And this chef males will have additional methods like cook their favorite meal or anything like that, or prepare foods. And as we start building an application out, we can start separating it into different classes, yet also having values in here that are perhaps more protected and the modifiers are in way such that they can only be seen by children. So, which kind of brings up a good point that if you had a chef class, well, you could also have a class, you have a sous chef class. And this one would take in taking the name and a string and an age and int and a favorite food perhaps. And this one would inherit from chef. And so we have name, age, favorite food. There we go. We just gonna renew this to fave food or whatever, we'll call this the fave food. And then of course we could have inside of here, something else, we have some other classes and stuff inside here, or you can actually, if you would like to, you can say inside of your inner block whatever reason you wanted to print something when it was created, you could do that side of here. And so for my favorite food is not accessible to the outside world, it's not a public API. So I can't call c.print my favorite food, which is a chef. I can't call it anywhere in public because it's protected. However, any of the children classes can then still call this. So here person is just giving us some root level inheritance. Now we're kind of created chef class has some stuff around it. And then we've got a sous chef class that maybe has some other things inside of here. And you might wanna have something like prep foods methods and stuff like that or something that a sous chef would do. And that's how you can create a method, which has a visibility modifier of protected. How do we use the internal modifier on a class? And what does it use for? It's a great question. So the vehicle class that we have here is a very simple class that just takes a color of a vehicle, and then we can print it out. Now, of course, this class would have a lot more information about a particular vehicle, et cetera. However, let's go ahead and assume that this vehicle has some axles. So we knew another class. So we're gonna create a class called axle and we're gonna have a count. And this count is gonna be, actually let's call it number. And it's gonna be entered your value and that's basically the number of wheels. So we'll say number of wheels on the axle. And then of course it would be a whole bunch of other stuff inside of this axle class that would do things for us, but we're gonna leave that out for brevity here. Now we know that the vehicle is gonna have a number of axles. So let's go and say axles, and then we're gonna have it as an array of axle. Now, of course, we don't know what that is given this period in time. So we're gonna actually go ahead and apply the late init modifier saying, Kotlin, I don't know what it is right now at compile time. I'm gonna initialize it later. Don't worry about it, I'll take care of it. So this all makes sense. We have a public class, we have axles, and then we might even have something else, like another class. And this one's gonna be a truck because perhaps we know that the truck is going to be, of course, it's going to be a vehicle and we need to parse it in the color. And then for whatever reason, we already know that a truck, this truck that we're building or all trucks in our application are gonna have some axles. So we say axles equal, we'll say array of, and allows us to create an array and I could say axle. And perhaps this first access to wheels, and this next axle has four wheels, basically, meaning we have two axles, one with two wheels in the front, four on the back. So this all makes sense, this is great. If we go back to our main class, we can easily create a vehicle as I've done here. I can actually say vehicle that axles, I can see the axles. I can say, val truck equals truck and I can parse in blue. And the truck's gonna have axles that can have access to, but let's go ahead and assume for whatever reason that we do not want anybody outside of our current module to know about this axle class. Maybe we only want to expose things about vehicles, but internally for organizational purposes, we want to be able to have a class that represents an axle. Maybe it has a bunch of utility methods that just helps us do things inside of our application, but we don't want other people to know about this class or even be able to use it, we just kind of wanna keep it internal to us. Like this is our class it's for us to do work with. We don't want anyone else using it. There is a way to do that and you can apply the internal modifier to the class. Now notice we got a bunch of errors automatically right out of the gate. So right here in axles that says public property exposes an internal type argument axle, which means bill over here in main, if I were to type t.axles, well, we would have the ability to have this. So now this can be interesting if we don't want that to happen. So what I can do is I can actually say, hey, you know what, I don't want axles to be... Basically Kotlin saying, hey, look, you can not expose axles because axles' internal to this module. Meaning that you can use it in this module. We'll compile everything together inside of perhaps you're building a library. We'll compile everything together, but we're only going to expose vehicle and truck because those ones are public. However, you said, here's an axle class, it's internal, so don't expose it, but you're trying to expose it here, so don't do that. So in this case, what I really need to do is actually say, put private axles. Now, as we see here, oh, now we have another problem. Okay, can I access because it's private. So what we can do is then we say, you know what, let's change this to protected 'cause we have a child class. Now, if protected, then what do we have here? Protected exposes its internal type argument. So now we have a whole different situation of maybe we don't want to expose these axles. So you have to start rethinking your API design at this point in time if you want your axles available inside of these other types here. And so maybe you don't wanna do that, maybe you wanna put it as private just like this. So now you have your axles that are private. And then perhaps you want to say the number of axles in the wheels and you just wanna expose this as a function. So you could do that. So you say, add axle and you say a number of wheels int. And then what you could do is you could do something like this. And all this would do is, would say axles dot. You know, we just turn this into a list actually to make it a little bit easier to work with, let's say axles dot and we need to make this actually a mutable list so we can actually change it otherwise we have a read only list. Add, and then we'll say axle, and then we'll parse in the number of wheels. And there we go. Now we can actually have that. And so if we know that we need this, we can say, add axle. I see two, add axle four. Now what this is allowing us to do is have this axle class inside of our module, but not allow it outside of the public API. So if we are over here, now we could say t.add axle. So I could still kind of work with the axles, but it's hidden behind an API here. So I said add axle on a vehicle. So the vehicle can also add an axle. But if I were to try to do anything with that axle such as return, and maybe I want to say, all right, well let's return the axle, someone might think so. So we say, get axle, so you get axle info. And what we're gonna do is we return all the axles, we'll turn a list of axle. Well, as you can already see, we have a problem. Public function exposes internal return type axle. So even if I wanted this get axle info, this is not gonna work because Kotlin would say, look, this is internal. You're trying to expose us as a public API. We're not gonna allow that to happen. So perhaps I need to just expose some additional information, I exposed the strings and then maybe for whatever reason I iterate over them or whatever. Now, if I do need my class to be accessible outside, then internal is not gonna work. However, it's very useful. If you have a particular function class where you need to encapsulate behavior, but you don't wanna expose this behavior and all of its intricacies to the outside public and you don't want anyone to be able to call it. You want that to be perhaps any of the interaction with that internal class to happen through its public API, such as we're doing here in the main class. If I wanna add a axle to a vehicle, I can number of wheels three or four or whatever. And maybe this method perhaps does a bunch of validation and a bunch of checking before it actually creates this axle class, or maybe it has to do a whole bunch of other things that if at your application maybe goes out and checks to see if are any access available at the manufacturer, can we even add an axle right now? What is an axle, et cetera. So there's a whole bunch of things you can do, but it allows you to lock down your API internally. And so you kind of wanna play with it and see what works best for you, but it's very useful for hiding bits of code and functionality and logic inside of your application, but still providing you with the ability to be organized inside of your module. To create an abstract class in Kotlin, all you're gonna do is slap the abstract keyword right on the class itself. Now you have an abstract class. Let's go ahead and delete that though. And back at the main file and see how this impacts it. By default classes are open. So we can go ahead and create instances of them in Kotlin. So now I have a vehicle, I wanna provide the color, which would be red and I can create many different vehicles. So I create, we'll call this one A, this will be blue and I can create many more, et cetera. However, if I want this to be abstract, all I have to do is add the abstract keyword, which is a modifier to the class. Now I am not allowed to create an instance of an abstract class. Now you may be wondering why would you not wanna create an instance of an abstract class? If you think about it, when we're designing type systems, we have various different types we're trying to represent. In this case, we might be building an application which works with vehicles, but we do not want users of our application to create just a vehicle instance, we want them to create actual implementations of a car and a truck and so forth. And so a lot of these vehicles will have some similar things to them, for example, they may all have a color. They all may have a number of wheels. So we say val number of wheels, they may all have various different things and doors and so forth that are all similar. So we could say number of doors as well. So val number of doors. Now, for whatever reason, this vehicle may actually drive a certain way. So it may need to shift gears a certain way. So it may be an automatic, it might be a manual, different type of vehicle. And we may not know what that is. So we may wanna actually have, if we, for example, wants to provide a method to tell the vehicle how to drive. And so that function might be just be called drive. Now this perhaps does something. Well, now depending upon the vehicle, drive can mean one thing or another. For example, in a car that's not a automatic, we just step on the accelerator as long as we're in the drive position, it drives. However, if you're in a manual transmission environment where you have to shift gears manually, well, drive is gonna require a few more steps. So we cannot abstract all this information into just this class here and too abstract. So this class we might wanna actually say abstract, abstract function drive. Now I'm not gonna provide an implementation here because the implementation is gonna change based upon each different implementation of the class. Now, one thing that may be the same as everything else is a function called open door. And this function just simply opens the door. And all that does is open the door. Now it's gonna be the same, perhaps for every single vehicle you have, you pull on the handle and the door opens. Now, of course, this is different if in real world, we're gonna have electric cars and electric doors and electronic doors and manual doors. But for the brevity and situation here, perhaps just assume that every door is opened the same way. And so we can leave that inside of our class. Here's an abstract class. We can start having other different things inside of here, such as function called stop. What would a stop do, it just stops the car and maybe even better say, turn off. Every car can be turned off, vehicle we could turn it off. Let's just turn off the ignition. And if we only wanna be real specific, say, turn off ignition. And perhaps this is gonna turn off the ignition, whatever that implementation looks like is what it looks like. However, we now have an abstract class that does a couple of things for us that we don't have to re-implement everywhere else. And this is the vehicle class, this is how we can define natural class, but we can not implement it. So we actually have to implement it in another class somehow, but so we can have a function that's abstract, which means, hey, whoever's going to extend this vehicle class at that point has to also implement this drive method because it's gonna differ between different classes. However, they can go ahead and use the open door and turn off ignition methods. And that's how we can develop a very simple abstract class and define it in Kotlin. To implement a abstract class in Kotlin is actually pretty easy. So let's say that you wanna have a car class that extends this vehicle class. So we'd have class car. And for whatever reason, when you say we're also gonna have a color, which is gonna be string or have number of wheels as integer and number, this needs to be bow number of doors. And then what you can do as you're just gonna go ahead and extend the vehicle class. And vehicle of course is gonna need that color and number of wheels and number of doors, because those are constructed parameters. However, notice we have a red squiggly here. And the reason why we have that is the car class is not an abstract and does not implement the abstract base class member drive. So what this is saying is, look, maybe you can do one of two things here. You can have an abstract class extend an abstract class. So you could map this one here and you can maybe even call this a two, let's call this a two door car. So call us a two door car, and I'm gonna go ahead and break this into a new line here so it's easier to read. And so instead of parsing in the doors here, I already know that this is gonna be my two door sports car for whatever reason. And now I have an abstract class here and this one might have a function here, an abstract function, and so I'm gonna call it drive fast. Of course, I don't know what dry fast means 'cause in this two door car, it could be a Porsche, it could be a Lamborghini, it could be any number of cars, but I know that this two door car is going to drive fast and how it does that depends on the implementation. Maybe it's an electric car. So that's one way I could do it. I could also just go ahead and implement a car if I just know that I want perhaps a simple TownCar or this is called a hatchback, even simpler, hatchback car, so you know it has little hatch in the back. And then I say color of course is string number of wheels gonna compute any variable, number of doors it's gonna be, who knows, same thing here, I'm gonna put in a new line. This one's gonna implement vehicle and it's gonna be the color number of wheels, number of doors, et cetera. Now, again, I'm going to get this because I'm gonna get this error saying that we have not implemented the drive function. So what I can do is implement that member. And basically what we're telling here is, hey Kotlin, I have now implemented the hatchback car. It is of type vehicle and this one is going to drive a certain way. And so when the drive method is called, it will then call this implementation right here. Now I can have many different implementations of this car. So I'm gonna actually copy this and paste it right above. And then I'm gonna call this one, perhaps, call this a TownCar. I'll call it a TownCar and same thing we have drive. So we have different implementations here. And this TownCar might have something different where it has a very smooth ride. And this one is a very basic smooth, and this one is very basic. It's very just hatchback car. So we're gonna have many different implementations here, however, in order to implement an abstract class, you also have to implement the abstract functions here. So, which is interesting, let's go with this two door car. Let's create some space here and let's implement this two door car. So we've already seen how we can do it once. But now we have an abstract class that extends an abstract class. What does that look like? And so let's go ahead and implement something here. So we'll call this Lambo and of course it will, perhaps we just are gonna assume that every Lambo is gonna have, so it's gonna be extended vehicle. I'm not even gonna provide this things inside of the class Lambbo 'cause every Lambo that we're going to create is gonna be red and it's gonna have four wheels and it's gonna have two doors. There we go, simple enough. Now we have this little error here saying, hey, look, drive has not been implemented. And actually we know it's gonna be a two-door car, so let's change this to two door and we can get rid of this right here. And now we have Lambo says, look, drive fast has not been implemented. So let's go ahead and implement drive fast. Okay, look, we have two things here. Well, let's just go ahead and put and drive fast 'cause that's what it was complaining about. And we'll say, and whatever this is going to look like, is what it's gonna look like. Depends on your implementation. Now we also have another error here saying, hey, the drive has not been implemented. And why is that? Well, because drive fast method came from this abstract class and the drive method comes from the vehicle. So because two-door car extends vehicle and our Lambo class extends to door, we basically getting the entire object that we need to implement here. So as you can see, this can be beneficial, depends on how you are developing your classes. However, it can also be very difficult if you decide, let's say you have 20 implementations of vehicle and the subclasses are majors 100 and you decide to add one abstract method to the top where the people must implement it. Well, now it's gonna repl throughout your code base and you're gonna have to implement that in each of your child classes. So it's something to think about that you'll encounter from time to time. Sometimes that's necessary, sometimes that's what you expect to happen. Maybe you need to implement some type of method, like a safety check to ensure that all of the vehicles have meet a particular safety rating and you have to implement that in each class and that's what you need and that's okay. However, other times you're gonna realize, well, maybe I don't need that method to be abstract, and that's gonna be on a case by case basis. Now over here, of course, we can go ahead and start implementing all of these. So if I wanna say var or val Lambo that I can just say equals Lambo. Now I've got a Lambo. And remember, I didn't have to provide any variables there. Well, why didn't I, because I already knew, could I said, hey, all of our Lambos are gonna be red and are four wheels. And since this is a two door car, we already know it has two doors. I don't have to provide anything, I already have a Lambo now. And so if we wanted to provide a method up here, we could say what kind they are. Another thing you can also do is let's go ahead and create a couple of these other cars here. Var two door equals let's say TownCar. Equals TownCar. Now of course, what are we missing? We're missing the color, so let's call brown. It's gonna have four wheels and four doors. And now I have my two cars. And again, I could have multiple other ones inside of here. I've got my Lambo, I've got my TownCar, I've got my hatchback. I've got all these different types of cars. So one thing I could do here, so we have a hatchback too. So, we have a hatchback car. This one's gonna be silver. It's gonna have four wheels and two doors. So this point, you might realized, well, some hatchbacks have for some have two. So that's why we need the, have the number of doors in a way that we can configure them. So this is how you can implement an abstract class and you can also have an abstract class extend another abstract class implement that. And then you can see how the abstract methods and members are required inside of each individual class itself and you're required to implement them. So for example, our Lambo here extended from the two-door car, which extended the vehicle car. So we had to implement the drive method and we also had to implement the dry fast method in order to fulfill the requirements of the abstract class. And basically what the abstract method is saying is like, look, we don't know what drive does, but we know that every person or every class that implements this vehicle class has to provide an implementation of drive and the same thing down here. And we didn't have to implement it down here is because this is an abstract class. And that means like, hey, there's nobody who can create an instance of two-door car. But if you are going to use two-door car, then we do know that you have a drive fast method for whatever reason. And whenever you implement that, you have to also implement whatever else vehicle is asking here. So if I were to throw another abstract method on here, so abstract fun stop. Now notice one thing, Lambo is gonna give us an error. And if we scroll down also, so is TownCar, and so it was hatchback. Well, why is that? Well, simply because stop is implemented everywhere else. So I would have to go into TownCar, implement stop. I would have to go into hatchback, implement stop. Now here's an interesting thing, maybe I know that all of my two-door cars, the only way to stop them is to slam on the e-brake. And so what I can do is I can implement stop inside of my abstract class here. I'd say pull e-brake, which is the emergency brake. That's how we stopped these two door cars, just as an example. So in this case, Lambo doesn't have to implement the stop method because that's just handled in the two-door car level. So I've actually can override an abstract method inside of an abstract class of the parent abstract class. So again, this stop is actually from up here inside of this stop. So you can go here and you can right click on that and you can say navigate, excuse me, not navigate. You can go to command U on Mac, it'll take you to the implementation of where this is at. And I think it's control+U for windows. So this abstract class has implemented one member, but it's still parsing this member down further below. So we're still saying, hey, drive needs to happen. And what we can also say is, you know what, well maybe any time a two-door car is driving. Well, we also know that we are going to drive, we're just actually just gonna drive fast. So anytime you drive two door cars, only drive fast. So notice one thing, I can actually get rid of this down here, because the Lambo now is using the drive method. If anyone calls drive, it's just gonna call drive fast, which calls this drive fast method. So let's really see this in action. Let's go over here into our main file. And inside of our drive fast, let's go ahead and just do a print line, driving fast. So if we have a two door car, it's gonna drive fast and let's do a another two-door car here and let's create another one here called, let's do another one. Let's call this one Honda. And maybe you were saying, hey, you might like this really fast Honda or let's do Acura. And this one's gonna be a two door car, a two door car. It's gonna be blue and it's gonna have four wheels, there we go. However, we do need to implement this Acura drive fast method. So we're gonna say print line zoom zoom. And what am I getting at with this? What we're gonna see here is when we call the drive method here on a two-door car, it's gonna delegate to the drive fast. Now the drive fast has no implementation, we don't know what that does yet. However, that's gonna delegate down into the actual implementation of the abstract class. So the two door car has a drive fast method. So anytime we call drive, it'll call drive fast, which then calls the drive fast method, which is implemented in the abstract class. So let's go here and let's go ahead and create. We'll have our Lambo move that down here. We can get rid of these two for now. And let me say val Acura equals Acura. And now what we can do is we can say something like this. Let me say Lambo drive and Acura drive. Now they're both gonna drive, they both drive the same, whatever. They have the same abstract class, they have the same interface so I can work with them. They're both vehicles. Now we see it on here, one is driving fast and one is going, zoom, zoom. Now an interesting thing that you can do here is you can start creating all different types of implementations. Now I can still we'll call drive fast. That's not restricted, I can still say Acura.drive fast. and I can still do this and everything will still work and we'll still get the same results. So driving fast, driving fast zoom, zoom. 'Cause remember, drive, if you go to implementation command B, it takes us to the two-door car, which is just going to delegate down to the drive fast. Now implement the drive method. That drive method was from the original vehicle class. This is an accidental import. And so that's how you can create an implementation and instance of implement an abstract class and have it also extend other abstract classes as well. One of the more interesting things about abstract classes is the word abstract. Anytime you think of an abstract class, think about the definition of the word abstract. What does it mean? It means to basically pull something away from it, to abstract the details away. In software when you use abstract classes, especially in Kotlin, it means you can kind of use this as an abstraction of source. So let's assume that we've created these four different cars. All of them all inherit from a base abstract class called vehicle. So we have vehicle, we have a two-door car, which extends vehicle. We have a Lambo which extends a two-door car and an Acura, which takes is a two-door car, a TownCar and hatchback. And each one of these have a drive method. So the hatchback says driving a hatchback, drive for TownCars is so smooth, the drive method for Acura says zoom, zoom, and the drive method for Lambo says driving fast. Notice you say drive fast and why don't I implement the drive because that's implemented in its superclass up here. And anytime someone calls drive, we just call the dry fast method because we're just going to assume two-door cars just drive fast for whatever reason. So we have all of our cars here. Now, if we wanted to call drive on each one of these, what we could do is we could say lamo.drive, we have call acura.drive. Now this is great and all, or we could even say drive fast if we know we want it to just drive fast. However, there are a lot of instances in power when we know that we wanna rely on the abstraction. So we'll say drive vehicle, let's create a method called drive vehicle, which just tells the vehicle to drive. Vehicle. And then what we're gonna do is we're gonna take in a vehicle and inside of here, what we're gonna do is say, vehicle.drive. Now this vehicle is an abstraction. We're not creating an instance of it, we're just saying, hey, this method is gonna take an instance of a vehicle. We don't care what kind it is, we just want a vehicle and we're gonna tell it to drive. So what we can do here is then you could say, Lambo, you say drive vehicle Lambo parsing instance. So I'm gonna just duplicate this a few times and I'll say Acura and then I'm going to say hatchback and TownCar. Now, when I run, this what's gonna happen is drive vehicle is gonna call in. So we're gonna say Lambo, we're gonna get an instance of a Lambo. It's in the call this drive method, which is gonna say, all right, who's implementing this drive method. Well, a two-door cars implementing the drive method because well, who is implementing, okay, it's going to call drive 'cause Lambo is a two door car and say, oh well, I'm gonna implement drive fast, which is this method. Well, who's implementing dry fast. And to say one Acura and Lambo are. So that case is gonna go here and call driving fast. So if we were to run this, what we're gonna see really easy is we're gonna see each one of these methods, method invocations are gonna dive down and call the appropriate methods. So for a Lambo, it's driving fast, for an Acura, it's zoom zoom, for a hatchback, would say driving a hatchback and for TownCar, it's so smooth. And so abstract class thing, again, think of the word abstract, allows you to abstract the details behind some type of, almost like a contract. So this is the contract of all the vehicles are going to have. They're gonna have color, they have number of wheels and have number of doors. There have a drive and a stop, which is different for each vehicle. And then it's those child classes implement can either decide to override and implement things or if it's an abstract class, it can leave it as is and let the implementers downline implement it themselves, such as TownCar is doing here, what it needs to implement drive. So again, you could have an addition. You could extend two-door car to be a slow two-door car. And instead of it being dry fast, you could override that to, I could drive slow. So, and then again, up in your main file here, you could still just use this method to call drive and that class would then react accordingly and you can start performing all different types of operations with different types of vehicles, but have them have the same type of type signatures. And that's how you can use abstract classes as abstractions on a very simple manner. So when do you want to use an interface? Let's take the simple example of you going to eat at arraystaurant. So here you are, you're getting ready to go eat at the restaurant and the waiter brings you out a menu. Now the menu I want you to think of as an interface. And what is an interface? Well, an interface is a contract saying, what are the things that we can do for you? So here at the restaurant, this is an Italian restaurant. They say, well, here we can make you spaghetti and meatballs, we can make you bakesidi. We can make you lasagna, we can make linguine and clam sauce and shrimp and garlic and all kinds of stuff like that. And you determine the one thing I would really like is spaghetti and meatballs. And so what you're using is this contract. You're reading the contract saying, hey, what are the available options, which this menu is. It's the list of available options of what this restaurant over here can produce. And this is the restaurant. And the menu is the interface between you and the restaurant. So the restaurant says, this is what we provide, this is our contract of goods that we can do for you or things we can build. And then what you do is you communicate through this interface here, this menu and say, hey, I want to order this. And so you tell the waiter, hey, I wanna order this spaghetti and meatballs. So this order gets go over here to the kitchen. And then what they end up doing is they end up making your spaghetti and meatballs and they end up making it blah, blah, blah, blah, blah. And here's all the noodles and it kind of looks like a smiley face now. Anyway, there's some spaghetti and meatballs. And then what ends up happening from there is that gets directly returned back to you and then you get to enjoy it. And you are now a happy camper because you are enjoying your spaghetti and meatballs, so it's a pretty good thing. All right, there we go. So your spaghetti meatballs, now this is the contract. Now here's the thing, let's assume that this restaurant has many different locations. So this locations, you could have another location, which is over here. It maybe some different town, but they're owned by the same company. But when you go to this restaurant to this chain restaurant, you're like, you realize that, hey, you know what, I really like the spaghetti and meatballs. And so you go to the blue one here, which is the same company. They have the same menu, so they bring out you the menu again and say, all right, here's all the things that we have to offer. And these things of course are gonna be your spaghetti meatballs and your bakesid and your linguine and clam sauce and et cetera, et cetera, et cetera, et cetera. And of course you say immediately, well, I like the spaghetti meatballs from this place. Again, it's owned by the same company. So again, you're gonna make an order again say, I want spaghetti and meatballs. The order goes over to here, spaghetti and meatballs. And then what you don't know behind the scenes is, well, this is just the contract, like you don't care how the spaghetti meatballs are done, you just want the spaghetti and meatballs. And so what ends up happening is maybe because of the way that the business is built or the software is built. In this instance, when I'm running over here, instead of building and making the spaghetti and meatballs myself, they actually kind of just go over here and say, hey folks, we need some spaghetti and meatballs. And maybe they've already pre-packaged those spaghetti and meatballs before. And they actually have already delivered a box of those spaghetti and meatballs. And the spaghetti and meatballs are already pre-packaged and ready to go. And then as you can see here, they got all these little meat balls in here and you're ready to eat them, or they're ready to warm them up. And so at this point in time, you, again, you've just ordered the say, I want spaghetti and meatballs. The restaurants are also adhering to this contract and saying, okay, spaghetti and meatballs came in, cool. If you're at this restaurant, we make it fresh because we're the main restaurant. If you're at our other location, we've already made a bunch and kind of pre-packaged it. So this location can kind of get it done because we have a special way of making it that only two people know how to do. And so now over at this location, what they do is they come over here and they say, all right, cool. Well, let's go ahead and take some spaghetti meatballs here and we'll head and put it on the plate and we're gonna warm it up and we'll do whatever we wanna do to it. And here's the spaghetti and meatballs or whatever. And then at that point, we're gonna go ahead. And once that's done, we're gonna go ahead and deliver it back to you and boom, there you go right into your stomach. So now you have spaghetti and meat balls over here. The key thing is here from a user perspective, this is the user who is perhaps consuming a library, your code or whatever, this is your interface. Your interface says, hey, here's the things we can do. And then the implementations are these restaurants. These are who implemented the interface. This restaurant A implemented the interface and restaurant B implemented the same interface. They both said, hey, we can both make you spaghetti and meatballs. Now how they do that, you really don't care about. The caller of that from the interface perspective doesn't care about it. The caller says, hey, I want spaghetti and meatballs. At the end of the day, you know that you've just been delivered the spaghetti and meatballs that you want, you don't care how it's done. Now behind the scenes, the implementation could be different. For example, this is code. This code might call into a completely different module to generate something or this code might generated itself. It all really depends on how you're doing it. But what it allows you to do from a developer perspective is say, hey, I have this known interface here. And so anyone who interacts with us just needs to parse us an instance at this interface and call this interface and whoever implements it over here will do whatever they need to do to return back what they want. And so you can have one, you can have multiple different implementations of these over here. You could have, we have two colors here, but you can keep going. And there could be all different types of implementations of this interface and they could be all over the place. They could be ones up here, there could be hundreds of different of the implementations, but they all, every single one of them adhere to the same interface, the same contract. So it doesn't matter if you're ordering spaghetti and meatballs from this location or this location or any of these locations, they all know how to return back to you the spaghetti and meatballs that you want, all done through this interface. Now, if you take this a step further and we can go ahead and kind of clear this thing out, again, you have yourself over here and now this is we're going to a futuristic restaurant. And this futuristic restaurant now has just a digital ordering pad. And you come in here and you get to press on some buttons. So I want, spaghetti and meatballs or baked ZD or whatever. This is just an interface too, think about it like a graphical interface. This is an interface that's been implemented. Now, depends on where you're going. You don't really care who's implemented it, you just wanna know that you're gonna get either your spaghetti and meatballs, or you're gonna get your baked ZD. And you really just don't care where all these different types of things are coming from because to you, you interacting with whoever agreed to that contract. And then at that point in time. So for example, let's even take this a step further. You could have two different implementations of that interface. So this is gonna number one will be up here and number two will be down here. And as you order this item here, right in this little section, so I wanna order this. And at this point in time, you're gonna kind of go down this path right here. Well, cool, that means I'm gonna go ahead and this automatically then says, okay, let's spaghetti and meatballs. We're gonna go to this module and generate whatever we need to generate. And for whatever reason, you're going down this path over here, well, all that's just kind of, we have custom codes for everything. So everything is just kind of done right here in this whole section. So what that allows you to do, the interface allows you to hide the implementation behind a particular interface. It's the contract that this is the things that we can do for you. So anytime you want something to adhere to the same contract, you can do that. And that's exactly what has happened over inside of the examples here. In this case, we're using mammals so we can have a mammal and it can be a cow, it can be a human or whatever. We noticed that it walks, it can speak, whatever. All we know is it with a mammal interface, if I tell a man what to walk, it will walk. Now I don't care how it walks. Does it walk on four feet, two feet, 12 feet, 15, I don't know, don't care, I just know that it walks. And so when you have an interface, it means that, hey, these are the things we can do. To implement an interface in Kotlin, you'll type the word interface and then the name of the interface. I'm going to call this one discountable as in something might be able to be discounted such as a physical product, or maybe even a digital product, but I just want it to be discountable. And instead of there, I can declare values or excuse me, functions that I would want the implementer to implement. So here I'm going to implement a function called discount percent, which will then return the percent amount that whatever the discounted item is going to be will be discountable. So now I have created an interface that is called discountable. It has one function, I create another function that could do something else. Perhaps I'd call it a foo and maybe it doesn't have a return type, it doesn't have to be, it might be something such as calculate something or you could just do whatever you wanna do inside of your function. So it doesn't have to have a return type and return types can be anything you want. And that's how you create an interface in Kotlin. To implement an interface in Kotlin, you'll want to actually create a class. So you say class, it might wanna have a physical product. So we'll just go ahead and say generic toy. And this class is going to extend and implement the discountable interface. So here we have the red squiggly and we can click on this button and say, implement members and it'll ask us which ones. We could select discount percent. And then what we have to do is we have to implement whatever that value is. And of course, if we do not implement it, we can leave the default to do here, which will then if we look at the implementation, will go ahead and throw a null implemented exception. So if we run it, we'll kind of get something in our log saying that this has not been implemented for later, but this is how you can implement the interface. Now, if you do add additional member on here, so let's just call this foo for whatever reason, we're gonna get a compilation error again. So we won't be able to compile until we implement that on anyone else who has implemented discountable. So we may also have another class called digital product and digital product is also going to implement discountable because perhaps you can discount that as well. And you will also need to implement both of these values on to both of these as well. So they'll both be discountable. And that's how you implement an interface in Kotlin. And some examples of this might be you build an interface for your file system. So if you build a file file system interface, you might have something like this. Interface, we'll call it fs for five. It's very comical file system and I think nobody even has one called fs. And we might have a method called reader, which is going to read back and give us back a list of string is called files. We could have function read file, and it's gonna return us back a string. And we can have a whole bunch of other things inside of here. Now, what we could do is, I'm actually just gonna rename this so it's a little bit more, make some more sense of file system. Then what we could do is we could have an implementation of the file system, and we might wanna call that a real file system, or you know what, let's just call it a fat 32 file system, which is a real file system. And it's gonna implement file system. And then what we'd have here implement, oops, implement these members. And then we have to do something with each one of these members here. So we would read the file in a fat 32 method, and we would read the directory in a fat 32 method. And then there's other file systems here. So let's just go to return an empty list, just so we don't get a compiler error and let's just go ahead and return an empty string. And of course, we would read a file that the file would probably have a path of whatever, and we're leaving it off for brevity at this point. Then if we wanna have it ext file system. So class ext files, we've gotta have a file system here. Same thing, we need to implement those members, and we need to actually implement the other one too. And it's gonna leave us to do there so we don't have the error. Now, this is great because we have the ext file system. We have the fat 32 file system, and we know that anytime we're working with a file system, we need to do something. Now, lastly, we may have a very interesting one, we have right here. So I have class memory file system. Why would we want this? Now, this is very interesting here, because we might just have something that allows us to, let's go ahead and implement these members. And we might be able to have an implementation here of memory file system, where we take in and a constructor here. Watch this, we say read directory. We take in files. So we've taken a list of strings. And look what we're gonna do here, and we take in the file content, B string, and now all I'm gonna do here is just return files. That needs to be a val, so we can actually work with the inside of the class and the same thing down here, return the file contents. So the interesting thing here is if we go back to our main application. If for whatever reason inside of our main function here, we wanted... If we were to work with just the file system, so it will say file system. And we were broke with the fat 32 file system. And then we're gonna do something with the file system, blah, blah, blah. We could actually say, I'm gonna read this directory and it's gonna read the directory. Now this would be very pertinent to file 32 fast system. Well, what if we wanted our application to run on the ext file system? Well, now we're gonna have to do like some, if the FL statements to check what file system we're on or whatever, or what could be provided to us during real time is actually a interface. So we might actually say something like this. Val file system, file system right here. Remember, this is the interface here, and we can say equals fat 32 file system. Now that's still works and we can still kind of do the same kind of thing we did before. And if we were using something like dependency injection, we could actually get this from a constructor, which is beyond the scope of this, but we get from a constructor or a setter or something like that, some variable, some type, in which, we could set up pretty easily. Now the real power from this comes in when we determined later on. So let's actually do that now. We actually just create a method here, call get file system to function. Get file system is going to return us a file system, turned fat 32 files, okay, there we go. And all we can do here is just say this file system. Create file system. Now at that point in time, we can do this. Now, the interesting thing here is if this was inside of a dependency injection or whatever, we could use the in-memory version. So if I'm writing a test, maybe I've just moved this code into like a helper function somewhere. And then what I do is instead of this one, I say return memory file system. And then what I do is I know list of no path to file another paths, so remember this path of file pars, file content go here. So what I'm doing is I'm actually creating a in-memory file system, kind of faking the whole thing. And then inside of my application, I could say, filed.read directory. And then what I can do is actually give a known list of files here. And so now I've actually and I've enabled myself to have different implementations. This is the contract. Remember the contract says, hey, I'm gonna redirect and I know how to read a file. And if I read a file, I'm gonna get back a string and if I read a directory and to get back a list of strings. That's all I know how to do, I don't care how it's done, but I'm a fat 32 system it's done differently than an ext file system. And you know what, I may also build a memory file system, which allows me to provide the values to the actual file system so I can actually basically do this in memory or for testing, et cetera. So interfaces allow you to separate and think of them almost like shims. They allow you to kind of really decouple your application. And so they're just a way for you to communicate. It's a known contract between you and your application and what it can do. When creating interfaces, it's very often that you'll do it in an anonymous fashion. Here, I have set up an interface called on click listener. This is something you've probably seen in various other UI toolkits. And this on click listener has an on click method that is called when a particular view was clicked. And so we have a class here called view. This could be such as like a button, a text view, an image view, some type of map or anything like that. And it has a listener that's going to be assigned to it. Now, we're just gonna very simply here use the late init modifier to say at some point, this will be called and set. And we're not doing any null checking here, so this is not production quality, but this illustrates the example. So anytime the view is clicked, it'll then instantiate the click listener and then whatever happens inside the click listener will happen. So that's the interface here, on click listener with the on click method. So we have a few other classes that extend view such as button image and map. So let's assume we create a button here. If I wanted to set the click listener, I could set it to an instance of the click listener if I had implemented it in an instance of it, in which I could do my own custom one, like this class, my listener. And that would be on click listener and then be like this. And then perhaps I wanna say print line and I say clicked. And I can do that very easily by just saying new listener to list my listener. And there we go, now that click listener will work. And if I call button.click, the listener will now get invoked. So if we run this here, we should see clicked in the output window, we do see clicked. Now this is not an anonymous implementation, this is a concrete implementation. So let's go ahead and get rid of this. This is not what we want here. There's a way we can do it directly in line here. So what we're going to do is we use this word called object. We're basically creating an object right here in place, and we'll say click listener, and then open and close brackets. And then you'll see object is highlight and you hit alt+enter for Mac and implement the members. And it will just allow you to implement that member, which is on click and then we can just do print line here. Let's say this was clicked. And of course, now if we run this, what will happen is this basically created an inline object here. A we've done it in line object, which is an implementation of the on click listener directly in line. And it was called here and directly set up accordingly. So that's how you can create a new instance of that. Now, sometimes you'll also see methods perhaps that allow you to set listener. So to say, set my listener, we'll just call it that. And this is gonna have a click listener, and this click listener is going to be this dot click listener equals click listener. And so what it basically says, this one is gonna be set equal to this one up here. And so that's another way you could do that there too. So inside of here, we say button.set click listener, which is a method now. And so I can actually just cut and paste this out of here in the same thing. I'm gonna use an object, so object: the name of the interface, open and close brackets and then implement the required methods in the middle. So the abstract methods and this place on click and an instance of on click list will be anonymously created and set into this value here. So when the click button is called, it will then say this was clicked. And just to show some differences here, let's say this was clicked ABC. And if we run this again, when it gets to button.click, we'll see this was clicked ABC. Now the same thing happens here for the map and the image. So you could then eat it very easily and say map. So val map equals map and we would say map.click listener equals, and you could do the object thing here as well. Is that on click listener and then open and close brackets. And there you go. And then you can do something inside of there. And then of course, if the map was clicked. Now, what this allows you to do is have the very common interface, a contract of, one of you is clicked, it'll just invoke a function. And then you get to decide what to do when a map was clicked or what to do when this particular button is clicked. And so this is a very common pattern you'll have seen inside of various UI platforms. So that's how you can create an anonymous interface implementation in Kotlin. You can easily create a raise in Kotlin. So let's do that real quick. We'll create a variable called items, and you can say array of and then you parse in some primitive. So we can say one, two, three, four, five, and then we can do something with that array. So now we have an array of integers. So we can say items.foreach. And what we're using is the extinction function on the array class. There is a foreach extension method built inside of the arrays instead of the Kotlin standard library. And so what that allows us to do is parse in a lambda expression, and we can actually just do a print line with value. So we can say for each and print line will then provide the, take a value and print it to the screen. IT is the default name of the item inside of the lambda expression. If we want to change it to something different, we could change it to maybe the word value and we would do so like this. And then at this point, we could say value. Now that doesn't really provide us any additional benefit here. If I hit alt enter, we can see that we can replace the explicit parameter value with the name it, and this just kind of cleans up the code a little bit. Now, once we run this, what we will see inside of the output window down here on the bottom is all the integers have been printed out. Now, an additional cool thing with some of the built-in array classes of Kotlin is we can also have an int array. So by default, we'll specify how we would like some integers, we can also have a double array. And notice this is going to create a problem here because these are not doubled. But if we were to turn them into doubles by providing a.zero. So what is actually a valid double value, we can then at that point, start using the double array of call. There's multiple other ones in here as well. So we have the array of double array of, float array of, longer range of, of course, the int array of, char array of, shorter arrays, by arrays and Boolean arrays. Now you'll notice we don't see a string array in here. So what do we do there? So that's actually pretty easy. We can actually just do array of, I know we have to provide some values in here. So Donn Felker via a simple array of two items. And if we were to run this again, it would just say, Donn Felker, one string on each line. Now that's not all we can do with some of the arrays as well. We can also get the array of objects, so we'd say array of, and what I can do here is since we have our user class, which if we remember that it just has a first name and a last name. What we'll do here is we'll have user and with page, say Donn Felker. And then we might have another one called Jane Doe. And now we have two users who are part of the user array. Let's actually call this users. And then at that point, we can actually do the same thing that we saw before and then we can iterate each over one of them and we'll call print line and we'll call this IT. 'Cause remember IT at this point is just a user itself. Up here it's going to be a string. So the compiler is going to know that. If we run this, now we're going to see the two string value of the user class. So let's go take a look at the user class again. And the user class, I've added a quick two string implementation, I've just created an override so two string, and we can go ahead and return something. Now its full name and it just returns this string up here. And that's how we can work with strings and primitives and objects and easily create a raise of them and alter them and so forth. Now let's assume that we, for some reason, we needed to update the users and we needed to add one to it. So what we could do is to say val updated users, and we could say users.plus, and they're gonna add new user to it. And we say, Jon Doe here, and what this will do is if we take a look at the plus implementation, what does it do behind, underneath the hood. It's just an extension function that creates a copy of the array with a one larger index, adds the item at that given index and then returns that new array. So we have to make sure we're using that new array. And so of course we could do updated users.foreach and it's a print line. And then what we could do inside of here is just say IT. And now actually see if we run this again, we'll see additional person down here, Jon Doe has now been added to the second array, which is these three down at the bottom. There's other things you can also do with those user objects. So you can actually say users dot there's a little extension function on here. So you say reverse, this will give you the reverse array. There's a whole bunch of other collection classes you can do and which we'll talk about in the future here and so forth, but you can also get the size. You can get a particular value added. So if I wanna get the value at item number zero, the location, and I can say print ln and I can say item, that's going to be at that point item zero is going to be Donn Felker. And then if we run it, you'll see that it's just Donn Felker's printed down here at the bottom. Now you'll see that we have a squiggly here because we can actually use the indexing operator. So we'll place that and then we can actually do the same thing here that does the same exact method. We can set a value. So if I wanna set the index of zero to be a different user, so I guess a foo bar. And then if I were to go ahead and get that, and if we look at the implementation of set, you'll see it's just gonna return a unit. So we don't wanna do anything with this anymore. So now at this point, we'd want to actually get the value again. So we'd say val item equals users dot and we'll just use the indexing operator instead of the get. If we were to run this, we'll see here that the item that's printed on the bottom is foo bar because the first item in the array was updated. Now, again, we see a squiggly here. We can update this with an indexing operator again. So we're gonna say, hey the first user is actually gonna be equal to this, go get that first user and print it at that point in time. So there's all different kinds of stuff that you can do with arrays. You can add things, you can copies, you can reverse them, reverse them, slice them. There's a tremendous amount of things you can do with this, these collections, such as the other collections in Kotlin, which we will cover. And that's how you can work with arrays and build them quickly in Kotlin. To create a list inside of Kotlin is pretty easy. You can just say let's create a variable here and we can say val item equals list of, and then you can provide a list of, perhaps a list of primitives like we have here. So we have a list of integers. This is very similar to how it's done with the array of operator for arrays. So if we want to loop over each one of these and print them out to the screen, we could say something like this, say items foreach. And then once we compile this and print it and run it, it will then print to the screen as we see in the output down here, one, two, three, four, and five. So we have a list here. Now, this is important to note here that this is actually an immutable list. So if you notice here, I don't have the add method here. I can't add an item to the list at all. So if we look at the implementation of list of, we'll see the here, this is gonna return a new read only list. So that means that we can not add items to it. The same thing goes for, if you were to create a list of perhaps a list of users. Let's say list of, and then we can actually create a list of users. So I'm just gonna do a new line here, say Donn Felker, and then we can have another one would say, user would be perhaps Jane Doe and then so forth. We could have a number of them and so forth. And of course we could also iterate over these as well, pretty easily. So it would say users dot foreach and inside of this, we'd say print line and inside the print line, we could just say, if we could print the user. And that's gonna call the two string method on the user, just like it did when we did the array version. So we'd see a Jane Doe and Jon Doe, and actually did print here and do print line to it all put it on the same line. Print line will add each item onto a new line as we see here. So again, these are immutable lists. If we want to add an item to a list, we're not gonna be able to do that. So we're have to create a mutable list, which will be in the next lesson. So here we can also get access to items. So maybe we wanna do the first item. We could say users dot first, and it's gonna give us the first item. And notice the first method. This is an extension method on top of a generic list. So we have a first item. So if for some reason there was a default, if we were to print this here, let's actually do print line, just to show you what this looks like. We should see Donn Felker again, printed, and they're ready to go. Now there's also the opportunity here for us to perhaps we need to create a empty list for whatever reason, maybe we have a null value or something like that. So let's say that we have a name. So let's say val name could be a nullable string equals whatever. And for whatever reason, the code is null. And then we wanna create a new list and we can say stuff. We could say, it's going to be. If the name is a null. So if we wanna do something like this, we put it around the if statement. if the name is null, then we could return empty list. And that would just return us an empty list. Otherwise we can then return a list of name. And what that's gonna do is automatically the compiler is going to infer, Oh, since Donn is doing a list of names, it's gonna be a list of strings, otherwise this is gonna be an empty list of strings. Now notice here, I was getting a compiler error 'cause it doesn't know enough about the type here. So if I wanted to specify the type for empty list, I should say string their work. And we could say list of name. Now, at this point in time, you can see that this is grayed out because the compiler can infer the type. So I can go ahead and remove that explicit type argument. Now empty list, all it is again is just some, it's just a function just gives us an implementation of what's known as an empty list. It doesn't have anything in it. There's basically the size. If you look at it, the size is zero. It is empty, is hard-coded to true. So this is just kind of a general helper method inside of our application. So if we were to see if this was empty, we could actually say stuff.is empty, that would return true. We'll see a bunch of different other methods on here as well we can see for lifts is empty. Again, we can't add anything to it because it's an immutable list. So that's one way we can work with an empty list. Now we can also grab the last item of a list. So we could say users.last, and that will give us the last item of the list. At that point in time, we can also print that to the screen, which is gonna go ahead and show Jane Doe at that point in time. And then we've got Jane Doe. So there's a bunch of different operators on the list you can look at it. So you can just say users and you can use IntelliSense to in code completion to check it out. You can do last, you can do last of the prodicate, all different types of stuff, which we will cover in the future. And you can also get the first value or of null if it's nothing's there, so we'll see here, returns the first element or null if the list is empty. So there's a bunch of different things that you can check for there as well. And that's how you easily can create a list inside of Kotlin with either primitives or you can create it with objects. In another lesson I showed you how to create a immutable list using the list of method. The list of method allows you to create a list of some primitives or objects or anything of that you desire. If you'd like to create a list in which you can change, because let's take a look at this items, does not have an add must. We can't add or change or remove or do anything of that nature with an immutable list. It's just there, it can't be changed. If we need to create a mutable list we can do so with the mutable list of, and we use mutable list of, we can provide a type if we'd like to such as we can do with the list of operators. So if I know it's gonna be int, that I can do that, and I can say one, two, three, four, five. But then what will happen is type inference will kick in and the IntelliJ IDE would let us know saying, hey, we don't really need that explicit type argument, so let's go ahead and remove it. So at this point in time, based upon the parameters of the mutable list method, Kotlin can infer the types for this list. So now I have a mutable list of items. Now this work the same way if I have a list here. So if we just do list and we leave that, we'll notice that both of these things are going to compile just fine and do the same thing. We have the same method for each, we can print those. The differences here though is that we can add an item here, so I can add the number six now. And then if I copy this and I kind of reiterate over this again, we'll see that I have six in here. So let's go ahead and print a line here so we can see that there's something different and let's run this. And what we'll see here, we print one, two, three, four, five, and then we're gonna add an item here on number six, and then I'm gonna run it again and say one, two, three, four, five, six. So we can also do something else and say, we can say items.remove, and we could say a whole bunch of things here. We can remove if we meet a particular condition, we could remove all the items. We could remove a particular item. So I could remove element number zero, and we can print this off again. So what would happen here if we were to run this. So now we have a list where we have to make this a little more clear, let's add this in here as well, and we'll run it again. And we'll see here that we have now one, two, three, four, five, one, two, three, four, five, six, one, two, three, four, five, six. So, why didn't we get anything out of here, because we're asking it to remove a particular item. Now it's not finding the item zero in there, so let's tell it to remove number three. So let's run that again. And then what we will see down here in the bottom window is it was actually found item number three. So it didn't find one, two or three. It wasn't the index base, it was actually the item in the list that it was removed. So that was able to be removed out of there. Now there's a whole bunch of other things now we can replace items inside of here. We're gonna replace all, we can add a whole bunch of them. So if we take a look at the implementation of add all, we can take in our collection and add another one inside of here so we can add a whole bunch of them. So if I had another list, we can add that list to it. We can remove something out of position. So let's say we wanna remove this add position zero, let's run this and see what happens. Now, notice how this has index. So now we have two, three, four, five, six. I'm just gonna delete this stuff here, just for brevity. And we'll run this again, and we'll see that we have two, three, four, five is printed. Now, if I were to change changes to remove, just to reiterate here, and we could say, I wanna remove a particular element. I wanna remove element, that is the value of three. Now we're gonna see one, four, five is the output down here in the bottom. So remove at removes it with an index. So instead of the index of the list, we can find something to remove. However, if we wanna move an actual item given its value, we would say remove element. Now we can also say we can do something with a mutable list of objects as well. So we're gonna say user Donn Felker, which is something we have done before with the other types of lists and we always have a user called Jane Doe. So now we have two things in here. And if we try to remove here and we use the, I'm using an index here, because it's the number three, even though it's saying, hey, you're wanting to remove something here, removing int is narrow. You remove at index instead because it realizes the compiler that I'm trying to use a number here. So it's trying to help me out saying, hey, you sure you don't want to remove a particular index. Now what I could do, let me just go ahead and comment this out for a second. Inside of here is once we let me run this, so I'll show you what this looks like. We're gonna get basically two users printed. If I wanted to pull this out into its own variable, I could do that and say, pull it into a variable. And I could say, Don. Now what I could do inside of here is I could say, remove Donn from the list. Now let's print this list and see what happens. What do you think was gonna see here where see, we just have one item in the list because what happens is we have these items. So let's actually do two things here. Let's do this or print each one of there. And then inside of here, I'm gonna do a print line and we'll just print out a little break there. And we're gonna see as both items are printed first, which we see here. And then we print this nice little line break. And then we say, hey, I want to remove Donn from the list. Now, remember remove, remove the element, it's gonna tell the element that we'd like to remove. Remove at is gonna tell us we want to remove an index. So I can say, hey, if I move index one, it's gonna remove Jane Doe. Now, if I run this here, we're gonna see Donn Felker and Jane Doe and then we're going to remove Jane Doe and it's just gonna say Donn Felker. Now there's a whole bunch again, this is a mutable list, so I can continue to add and remove items accordingly. And the differences it's beautiful exchanging the other one is immutable. So if we want to do different types of things with it, you can inspect the various different operations using the code completion here. We can remove it out, we replaced things we can set certain values of I would like to set the first value to equal a different user. I could say different user is now going to be Jon Doe for whatever reason. So let's go ahead and change that and get this down here. Now, what we're gonna see at this point in time is I want to set item one to Jon Doe. So the first time through, we're gonna see Donn Felker printed, and then the next time through, we're gonna change the first, the item index one to Jon Doe. So we'll see Donn Felker, Jane Doe. We change it and it's Donn Felker, Jon Doe. Now knows we have a little bit of squiggly here, alt enter. We'll say we can replace the set call with an indexing operator. So using the indexing operator, we can say, actually take the first item in the index and replace it with this one. So it's the same exact code this does the exact same thing as this. Now we can also access these items via the index as well. So I might say print, say print line, and I wanna do items zero. And that's gonna print the first time. Now it's gonna be the same exact thing as if I were to do a items.get, and I could provide the index. Now notice again, we're gonna get the squiggly and what IntelliJ is telling us here is, hey, we can actually use the indexing operator. So go ahead and do that. Now, that's how you create a very simple mutable list inside of Kotlin. You can create a mutable list of primitives, could be strings or intergers, or could even be something like an object itself. All right, I'm gonna show you how to filter a list of primitive values here. I am filtering a list of strings. This could be integers doubles, longs, Boolean values, anything of that nature, but I've created a list using the list of, so this is an immutable list, a list of names. That's why we have Don, Bob, Jane, Jenny, Tushar, and Cavita, let's assume that we wanted to filter this list. And we only wanted to include, we wanna include everybody but Donn this time. And so we would say name.filter, and then we can parse in basically a lambda expression. So there's a couple of things we could do, we could do open closed parentheses, and we need some type of predicate. If we look at the implementation of this inside of the Kotlin collection standard library, we'll see that we had the filter method and we needed a predicate. And this predicate is requiring a lambda expression. That's going to return a Boolean value, basically return the list containing only the elements matching the predicate. So if it's true, it will be included in the list. If it's false, it will not. So what does that mean? Anything inside this filter we parse in which I'm gonna use the lambda expression version. If this equates to true what happens inside of here, it will be included. So it's pretty easy using the default iterator value of it. I say it does not equal Donn it'll be included. So what this means is I want to look through all the names and filter it and check to see if it doesn't equal Don, if it doesn't equal Donn then include it. So how can we tell if that worked, let's print this to the screen and we'll say filtered, and once we run it, you're gonna see that down here in the output window, we'll see Bob Jane, Jenny Tushar and Cavita. So Donn was not included. Now we could also flip that around and say, we also only want to include the names if it includes Don. So let's do that. Say filtered it does equal Don. So here we're gonna get back in array with one value in it which is gonna equal Don, which makes sense. Makes sense, it's good enough. But sometimes, maybe inside of your application, again, this is a predicate, a whole number of things could happen. This could be a bunch of code that's doing something inside of here. But let's assume that you wanna check to see if anyone has a letter A and I only wanna see if someone has a letter A. So what we might do is say it.two lowercase because I wanna check for lower upper case.contains a character and I wanna see if it contains the letter A. If it contains a letter A, I want it to go ahead and return it, so let's see what happens here. If we run this, we'll see we get returned Jane, Tushar and Kavita. That's right, 'cause Don, Bob and Jenny do not have the letter A inside of there. So you can start filtering based upon all different types of things. And the same thing will work if you had a list of ages such as this. And then you wanted to actually filter them. So you'd say val, over 18, or you say adults, which can be over 18, and you'd say ages.filter it.greater than or equal to 18, print line that, and we could see the adults. And if we run this, we'll see that we have 23, 33, 19 and 99, which is true because if you're 12 or year nine or 17, you are not an adult. One additional thing you could do is actually create a function called is adult. And perhaps this function has a whole bunch of things inside of here. So we wanna say age, let's just call it value. Let's be int. And then inside of here, say if the value, so we'll say return value greater than or equal to 18. Again, this is gonna be Boolean. So we need to return a value. So this function will return if this person is an adult, but now let's assume for whatever reason, you have to put a bunch of if statements in here. Maybe in one country and adult is 14 and the next country it's 21 and the next country it's 18. So you might need to have many different lines and logic inside of here, but it's gonna return a Boolean value. Doing that inside of this filter is gonna get real messy. And so what you can do is that you actually pars a function reference into the filter. And you can do this with a lot of the methods inside of Kotlin. So here's the age filtering tool, it's like colon, colon is adult. And what this will do is we'll take this as a function reference and say, as soon as I say, age is.filter, it'll basically parse in the values one by one into the is adult function. And then we'll run it through everything that's inside of here. And then whatever the result is, if it's true, it'll be included in the filter. Otherwise it will be excluded. So if we run this again, we should get the same result as before, so we see 23, 19, 99. Now we could change this and so we could change, if we wanna say is adult, we say his child and we probably need to fix the spelling of that. And then we can say less than or equal 18. And if we rerun this again, of course this variable name is wrong, it's not adult, that should be children. And run it again. Well now see 12, nine and 17 for the age of the filter. So that's how you can easily filter a list and array and so forth in Kotlin. Let's assume you have a list of data and you wanna find something in that list of data. This could be primitive as we have here such as a list of strings, or you could have a list of objects, anything of that nature that you want to filter. Not filter, but just find a particular item in the list, how can you go about doing that? Well, of course you could use a for-loop. However, there's also a built in utilities in the Kotlin standard library. So Lauren have is I'm gonna create a result variable. And then what I wanna do is I wanna actually find the first instance, excuse me, names.find, I wanna find a first instance of Don. So let's go ahead and say it equals equals Don. And then I wanna go ahead and print out if I actually found it. So print ln and a result. I we'll go ahead and run that. And then what we're gonna see here is we have Don that was returned. And that's what we expect Donn was in there. Now however, there is a kind of a gotcha here. So let's just throw something in here we know doesn't exist and let's run it again. So I'm gonna use the word foobar. Now we have the word foobar, of course we're gonna get back null. Now we kind of had some help from Kotlin to find this here. So we had find it was gonna find the first one. And if actually look at this, what it's gonna do is it returns the first element matching the given predicate or null if no element is found. So fine, kind of helps you, but now you have to start worrying about null values at all times. So if you're gonna do a null value, you say, so if you wanna compare something results equal equals. When you say result dot length. Well, now you're gonna get a problem here because it's saying this is a null variable. So you need to check to see if it's null first. You may have heard too, that Kotlin will protect you from null pointer exceptions, and doesn't allow you to do nulls. Well, that's false, as you can see here, we can use a null. However, I prefer to use things like first. Now first will actually give me the actual value first of Don. And so if we run this here, I'm always telling me like, I'm always going to get a variable back. Now, if I run this, I'll say result dot length. It'll be four 'cause Donn has four characters in it. Now the real interesting part comes in here. It equals equals foobar which we know foobar does not exist in this list. But if we run it again, what we're gonna see is we're gonna get an exception from Kotlin and that's the no such element exception. And if we look at the implementation of first here, it says it returns the first element matching, or it'll throw an exception. So this is something you need to be aware of. And I prefer to use this method because now I actually have a string that's gonna be returned if it's found. Now, but what if I wanna find the first or the no value? I feel that's a decision I need to make because I need to be able to read the code and know that, hey, I'm gonna get the first one or null value because it gives me context into understanding what's happening in my code. As regards to find, it does make sense, but it's not as obvious, it's not in your face. So first has also one called first or null. So I'm gonna take first or null. And now again, we get back a string here that's gonna be empty. So I'm just gonna go ahead and take that off there, just 'cause it's not gonna work and we'll print it and we'll see it says null. Now of course I could do the null operator check. Let's give me the null safety check.length. Otherwise I could just go ahead and do something else, do that or that should work. I never going to get back null because there's no values there. So I can find the first instance. So what this also means is if I have another instance of Donn in here, well actually let me say this to say, Donovan, let's see, we have Donovan in here. And I wanna see the first instance of, watch the always say first.contains. So we'll say dot two lowercase.to contains Don. So I just wanna find the first one that it's gonna find in that list. So what is it gonna find? Well, that's not gonna find anything. So let's go back to the result here, you didn't get rid of the length. First it dot two lowercase contains Don and that's not gonna work, so the capital D there. We'll run that and now we'll get back Don. But we had we actually have two instances in here. So what if I wanted to return the last instance, we can do last. So there's the last operator we can find the last instance of a list Donovan. So there's many different things we can do inside of here. We can find names.index of and what we're gonna see here, index of first index of last. So I could get the index of the first. And what is that gonna return? That should return us a zero. And if I do index of last, I can do index last. It'll show me the last value here. Of course, if I were to throw something in here and X, Y, Z, what do we think would happen here? We're getting negative one, so it's not found. So if you look at the implementation of that or negative one, if the list does not contain such an element. And this is all within the Kotlin standard library. So there's a number of things that we can do to find values inside of here. And just like we had the last before, we also have last or null. So we can also take last or no as well, so we'll just change this back to Don and we'll see the last or null returns us back with Donovan. And then we have Donn X, which there's nothing in there with Donn X in there, it will return null. So we've got a null value at that point in time. Now, the great thing about it is I can read this code six months down the line and understand that I'm sheltering the names list, and I wanna find the last value or give me null. And then at that point, I know exactly what to expect at this point. I'm either gonna have the value or I'm gonna have null, and I need to plan for that accordingly. Now, thankfully Kotlin is not going to allow me to just print the length of this because this is a nullable type. And I need to basically say, hey, what happens if this is null and I'll just provide the null safety operator, which says, hey, if this is null, just return null, otherwise give me the length, that's what that means. You can filter to include certain things and you can also filter to exclude certain things. So for example, if we wanted to have the result to include everything from the list, except something, we could have used a inequality expression inside of our predicate, or we can just use a names.not filter not. And then we can say it.contained the letter A. And what this will do, it will print line the result. And this will go ahead and include everything that doesn't have a letter A. So in this case, we're taking the list and we're filtering it to not include anybody who has a letter A in their name, which is Don, Bob and Jenny. And that's how you can use the filter not, which is basically the opposite of filter. So if we include filter here, we're now going to see the folks who have the letter A in their name, which is Jane, Tushar, Cavita and Donovan. So, yeah, filter and filter not, which are basically the exact opposite of each other, or you can also just use inequalities inside of your expression. It just really depends on what makes sense for you and your code readability. I prefer to actually use the different variations, so filter and filter not because it's very readable and I have to do less thinking and cognitive load, cognitive load for me in regular development. And in six months from now, I can read it as just kind of regular English. And that's how you can filter and not filter. You can easily take one list and then take some values out of it and drop it into another mutable list with the filter to commands. So we can save names dot filter to, and then you have provide the destination, which is gonna be where you want it to be applied to. And this has to be a mutable list, which is the approved list here. So let's take the use case of you have an approved list of people that are going to be on the guest list for a particular event and you want to randomly choose some more people out of this other list. And perhaps you wanna say anyone who has the letter A in their name, we're gonna randomly choose them. Not really fair randomized assessment, but it does work. So we'll say print line, and then we'll say approved. And what this is gonna do is gonna take anyone who has not taken just kind of say anyone who has a name A, and we're gonna append it to this list up here. So if we run this here, we'll see Danielle, Paul, Jane, Tushar, Cavita and Donovan. And so that's how we can take one value here, which is a list of names and say, hey, if anyone in this list has letter A or whatever this predicate is, again, it's true or false. It could be a very complex, if else switch with a bunch of things happening behind it. If this returns true, then include it and drop it and add it to this mutable list, otherwise don't. Now we can also do the opposite too, filter, not to. And what this does is say, all right, if you don't have a letter A in your name, well, now we're gonna move you into this list, it's the exact opposite. So we have the inclusion and exclusion. So now it's Danielle and Paul, they have already existed in the list, but now we want anyone who doesn't have a letter A in their name, so Danielle, Paul, Don, Bob, and Jenny. So we're gonna take anyone who doesn't have letter A in this list and apply it to this list. And so that's how you can easily filter from one list into another list. Now, again, the list has to be mutable. So if you were to change this to just a regular list, which is an immutable list, this is not going to work. As you can see here, the error states that a required collection must be mutable. So we need to change this to a mutable list of, and we can get rid of the type parameters there. Again, if you want it to provide them, you could, but we don't need to because it's already being inferred. And that's how you can move one things from one list to another using filter to and filter not to. Sometimes in code, you'll find yourself having a list of arrays or basically a list of lists and an array of arrays. So let's start with the top example here. We have an array of think of a bunch of people bringing a bunch of their fruits together, and we're gonna have a party and everyone's gonna bring all their fruits, and we're gonna combine all of our fruits together. And I need to see which fruits all of us have and we're building an application for this. So mine, I have apples and grapes, theirs they have oranges, pears, and strawberries, and there's the other people were bringing Kiwi and watermelon. So I need to see exactly what I have. And so I am going to combine all of those together, all of these arrays, because maybe they've come into my application at different periods of time. And so I have the three different arrays, and now I have an array of arrays. So basically I've created a list of arrays, excuse me, and list of lists. So I have a list of mine, a list of theirs and list of. And now if I were to print this, it would show an array. It's gonna be a list with three items in it. And this one, the first item would have two items in that list, the second one have three items on that list and the last one would have two items in that list. Now there's actually a method called flatten. And what this will do is it'll take all these values and flatten them out and basically put them into one list. So I'm gonna go in calm this out so we can run this. And you'll see here that down we have, when we print this, these brackets indicate a list. So this is one of the lists, this is the next list right here. And this is the last list here with the kiwi and the watermelon. And of course through the whole thing is inside of one set of brackets here, which represents that the whole thing is inside of a list itself, so it's a list of lists, kind of a mouthful. Now, when I call the flatten method, what it does is it just takes all those values and flattens it into one list itself. So I'm able to combine all of these lists if they're inside of one master list and flatten them out. Now you can do the same exact thing with arrays. It's exact same thing, we'll have an array of arrays here. And so we have an array of the same exact things. Instead of using lists, I'm just using arrays. And I'm gonna call it the same exact method on it. And so I'm calling flatten and both of these actually do the exact same thing. This one is gonna be iterating on a list. And this one is going to be iterating on an array. So you can see they are different there's different codes. So see line 19 inside the arrays file. And then we have line 69 of the itterables file. So there is different code, but it does the exact same thing in our use case here. And so we're going to take arrays and if we run these here, what we'll see as a top two lines will be for our lists. So we have a list up here and the next one, this is the array of arrays, and it's printing out the, hey, there's a bunch of strings in there and then it kind of stopped printing and then it flattens them out into one large array. So that's how you can go ahead and flatten an array of arrays or lists of lists inside of Kotlin. Let's assume you have multiple lists that you'd need to combine in Kotlin. These are all immutable lists, so you can not mutate any of them, but you would like to combine them and have a list at the end that contains all the items. It's fairly simple. All I need to do is you have a variable and you actually don't need a variable if you're just parsing this, you can just say mine.plus theirs. This is gonna return a new instance and they say .plus others. And this is gonna turn all of the values of concatenated together. So if we were to run this, you'll now see that the result contains apples, grapes, oranges, pears, strawberries, Kiwi, and watermelons. So we've combined all of these immutable lists into one final immutable list. Furthermore, you can do something interesting. You can, if you have this list and you want to remove a whole bunch of items from it, you could do this. You could say minus, and you could remove a bunch of items. So maybe you wanna remove theirs from that list. And what you're gonna see here is our result. Now is gonna take out theirs, we're going to remove the oranges, pears, and strawberries, which on the second line item here, we've removed the oranges, pears, and strawberries. Now the minus operator does allow you to do a whole bunch of stuff. You can parse in an itterable, you can parse in some sequence, you can parse in an array elements. And you can also parse in a single element itself. So if you just want to remove one item and you just wanna remove Kiwi and you were to run this, now you would see that the end that Kiwi would then be removed. So you can parse in a list or an exact element to be removed. So that's how you can combine concatenate lists together inside of Kotlin even if they are immutable. One of the most powerful operators in Kotlin on an a collection is going to be the map operator. So we have a list of names here. Let's assume that we wanted to take the first three letters of each name, make them uppercase and make the abbreviation for the person's name. It's a pretty rudimentary example, but let's go ahead and try to do that. We could use a bunch of different loops to do that, or we can use the map operator. So when I say items.map. And what this allows us to do is parse in the lambda function. And what we can do here is start doing something. So it is going to be each name in the list. And then we could say sub string, we're gonna say zero to three. So we'll get the first three letters.two uppercase. Now, what this is going to do, map is going to return a brand new list, so, it returns a list containing the result of applying the given transformation function to each element in the original collection. Okay, what does that mean? What that means is for each item inside of this list, when we call map, it's gonna execute a function. That's basically gonna give us that value, and then whatever we do with this value, it's going to return this back. It's gonna return whatever the result of this is back. So let's print it off and see what happens here. All this sub string is doing is taking the first three letters of the name and making it uppercase. And so as you can see, the result is Don, Jan, Carl, Pitt and Clar for the three letters of each of these names up here. Now I could just completely ignore it and just say, foo. Now watch, if I run this, we're gonna see foo over and over and over. The reason why, this map, this function is executed every single time. So it's executed for Donn Felker, it gets foo for Jane Doe. It gets foo for Karthik, it gets foo for Peter, it gets foo for Clark, it gets foo it just executes each individual time. So here, I'm just returning these values. So at this point in time, I'm gonna get those first three letters. Now we can take this a step further and we could say, all right, well, we can break this into multiple lines and say, let's say I wanted to get the initials off of everybody. Now there's multiple ways that I could go doing this. Now, what I could do, very kind of, this is very naive approach, but it works. I could say it.split, and I wanna split this on the space. And now I have some words. And then what I can say is I wanna return basically the words with the first word and then I wanna sub string that one. And that's gonna start from zero to one, basically take the first letter of the first word, plus words.sub string is gonna be the second word, .sub string. And you take the first letter of that one as well. And so we're gonna see here type mismatch. It says, found string required unit. So I actually need to just do this. And you may notice like, why am I not returning it? Because what happens inside of the map is the last value is what's returned inside of this map, which is why it's pointing with the up arrow. It says map inside the IDE. So this is a little hint, it says, hey, this is the return value of the map operation. So now, if I were run this, we're gonna see that it's just going to return back the initials, DF, JD, KM, PP, and CK. Now this is very, this is not production quality code. If the user has multiple names and multiple spaces in their name, this is gonna fail. If they only have one name, this is gonna fail. So this is not production quality, but this just illustrates something that you can do inside of the map operation. So let's go ahead and you rewind this a little bit here and go back to where we were before with the abbreviations. So now I have the abbreviations. And just to run it again, you'll see what we have. You have Donn Jan, Car, Pitt, Clar. Now, what I might want to do at this point in time is actually go ahead and I can perform form another map. I can map over an existing result. I'm just gonna move this over here. So whatever the result of this, which is Don, Jan, Car, Pit and Cla, I'm going to go ahead and do an operation on that. And I can map on top of that. So I could say it.length and we'll return that. Now what you're gonna get back is a list of integers. And each one's gonna say three 'cause each of the abbreviations was three long. So that kind of makes sense. Now, what I could also do is something like this. I could decide that maybe I want to sum something, I could sum by a particular value, say it.length and run that and we're gonna see 15, so some by that. But what does that mean? It means we've taken each of the values here and then sum them up into 15. Now I can also decide if I don't wanna do a sum, maybe I wanted to do a filter. So I remember this is applying to the result of this map. So I can say filter I'll know when I wanna get everybody's first three letters of their name, and then if their first three letters of their name have the letter O in it, so it's it.contained, it's all we'll do to lowercase just in case, actually two uppercase. Actually it's already uppercase. So I say, it.contains. Oh, if we run this, we should only get one value back. And that's gonna be Don, D-O-N because I've now grabbed the abbreviation and then I've now filtered it. So I can start stacking these things on top of each other. Now, if I, for some reason, would like to reverse these things, reversed. If we run that, you're just gonna see Don. So let's say contains and so go ahead and flip this filter not. So I don't want it if it contains a letter L which should give me all the other ones. We're gonna see CLA, PET, KR and JN. And now that's because the array was reversed, I've reversed this array. And I can continue to stack different operations on top of this. So this allows it to be very functional. And so I don't have to perform all of my logic directly within one particular map, I can map from one thing to another thing to another thing. And this allows you to have functions that return, perhaps a list of something. And then inside that list, you might perform an operation. And then you might map over that operation and map over something else and allows you to transform your data in real time, basically as you're typing it, it's much more declarative and easy to read. So that's how you can perform a map operation with just regular primitive types. Now, of course, you can do this with other classes as well. So let's say we have a class called person, and this person has a name, which is a string, let's do something simple like that. And we'll say people, and we'll say, list of person, Donn and then we say person Jane. Now I have all my people. Well, I can also map right over these people too. We can say people result. And what this will be is I'll say people.map, and now I'm gonna have this IT value as a person. So I can say person.name, I can do something like that. So I can just get everybody's name. Now, if I were to print this to the screen, I would say people result with this gonna be, is a list of strings, it's just gonna give you everybody's name. So this would be Don, Jane, Bob, Cavita. So I'm just iterating over, I'm just getting a list of that. So it allows me to transform that data, which makes it very, very nice. And of course I can map on top of that, which might be it.length. So I'm gonna get the length of everybody's name, and maybe that would be a useful function for me somewhere. I could put this inside of a function and say, all right, give me the length of everybody's names. And it would give back the length of everyone's names accordingly. Now, maybe you would want to transform a little bit further and you could say, all right, what's the length of their name and perhaps also I would like to show their... I want it to include their name as well with that. And then you could create various different mappings and return arrays or raise arrays list of lists, et cetera. The possibilities are endless. The map and flat map operator can sometimes be the source of a lot of confusion and determining which one to use can always be kind of something that will confuse you throughout time. Very often that I've had to look the documentation and play around with a few examples to understand what I'd normally need to use. Now, working with collections most often, you wanna use the map operator because you just wanna return values and perform transformations on those values across all of the items in a list or an iterable of some fashion. However, let's determine what the differences are. Here we have a class in line 23 and it represents a shopping bag. And in each shopping bag, we have a number of items that go into a bag. And when you go grocery shopping, you usually have a number of bags. So one bag has grapes, apples, and oranges, the next bag has milk, eggs, and pasta and the last bag has some bread, naan and cake in it. And we have basically a list of grocery bags, and those are gonna be our grocery bags that we have from the grocery store. Now we may have also went and purchased some clothing. And so we went to a few retail shops and those bags, we have three of those as well. We have a shirt, pants and trouser, let's say trousers. So we have shirts and trousers, we have socks and shoes, and then another bag has a jacket, a sweater, and a scarf in it. So we're gonna use these two lists. So basically these two groupings of bags. So we have a list of three shopping bags. And up here, we have a list of three shopping bags, and we're gonna perform a flat map on it and then print the result. And then we're gonna perform a map on the retail bags to see what the differences are. So here we can take grocery bags.flat map. And then what we do is for the transformation function, we just tell it to, hey, return all of the items. And so if we look at the declaration of flat map, it says returns a single list of all the elements yielded from the results of the transform function being invoked on each element of the original collection. That's confusing. So basically what it's saying is like, look, what's gonna flatten out, if this is a list, it's gonna flatten it all out. Now, if we look at the map, we've already looked at this before. It says returns a list containing the results of applying to give and transformation function to each element in the original collection. So that makes sense. All this function is really doing is saying, hey, map over all of the bags and then we're saying, and give me all the items. Okay, so let's do something here. Let's just run this to see what the result is. If we run this, you'll see down here, the flat map actually says, hey, I've grabbed a win inside of each bag, I've grabbed the list of things inside of the bag. I've grabbed the list of groceries that are in each bag. And I got back each list of groceries. And then basically what it is. I flattened them into one single list. So it's flattened them all into one single list. In this case, flat map, when you have a bag of groceries and each bag of groceries has many items in it, it's like taking everything out and putting it on the counter. So you're flat mapping. You flat mapped everything out on the counter, it's all right there in front of you. Now, the retail bags and we're using the map operator. What we're basically saying is, well, I have a bunch of items in the bag, and I may have taken all these items out, but I'm still leaving them in little groups. I have a group number one, which contains the shirts, pants and trousers, and the group, number two, the socks and shoes, and in group number three, which is a jacket sweater and scarf. But I don't have the bag anymore because I've taken them out of bags. And you may be wondering, what do you mean I've taken them out of bags. So let's do this real quick. Let's say print retail bag. If I run this again, we're gonna see a couple of print lines here. So here we go, we have three shopping bags. So if I just print out the bags themselves, that's like putting three shopping bags on the counter. Now, if I were to take those items out and put them on the counter in the same groups that they were already in, that's what retail bags is doing. It's just removing the bag, but I'm still grouping everything together. Now, flat map basically says, hey, look, I've just taken out all the groceries and just put them all on the counter. They're all right here in front of me, I just kind of put them out one right next to each other. There's no groupings, we're just one, one after the other sequential ordering. So the best way you usually think of this is if you have a list of a list of something to a list of lists, then at that point in time, you need to start thinking about, all right, maybe I need to use flat map if I need to kind of get into each individual item and combine them into a larger lists that's congruent. However, if I just need to operate on each item in a particular group, then perhaps I just need to use map. So lists of lists think flat map, everything else I default back to map. And then of course, if you ever get confused, hop into the documentation, I hope that helps. Sometimes you wanna have a list of data, but you don't want items to repeat. And so you want the data to be unique inside of that list. That's known as a set. And so what we can do is create a set of values. Like I say, set of, and this is a helper function inside of the Kotlin standard library and the Kotlin collections library. And I'm a create a set of names. So I would say Don, and let's say John, and then Felicia, there we go and I can print this out. Now what this will do, it should be names. When I print this, it'll look almost similar to just like a list that we have Don, John, Felicia, that works well. But the benefit of a set is if we look at the definition here of the set of helper method, returns a new read only set what the given elements. Element of the set are iterated in the order they are specified. And the return set is realizable, so we can realize it. But the interesting thing is here is it does not allow duplicate elements. If I wanna put Donn in there twice and I run this, you'll see that Donn is not in there. Let's see if I tried to put Felicia twice or John, so, let's do John again, we see this not included. So we cannot put that inside of there. So with a set, you can't have duplicate items in there. And so that's how we can create a set that's basically a read only set. If we wanna create a mutable set, you would actually, which through the set you can change. And again, you could specify this differently. This is also gonna be known as basically a set of string. That's the way you could do this. Instead of string, of course, this is not the helper method doesn't work this way, but that's what this is, is a set of string. If you wanna create a mutable set of, you would do like this and we don't need to provide the type parameters because they are inferred. If you wanna add something, you'd just say names.add, use the add method. And here, we're gonna go ahead and say, Jenny. Yeah, go ahead and print line names. And when we print that again, we should now see Don, John, Felicia and Jenny. Now what happens here if we try to add Donn in and let's go ahead and print it again. Now Donn already exists in there. So if we run this and see what happens now, we'll see that Donn is not added again. So the set is remaining unique. Each item in the set is unique. So the supplies for, if you're gonna have your strings, your inner integers, et cetera, each item in a set is unique. Now let's take this a little bit further here. We've kind of understand what we can do for duplicate names here. If Donn is up here twice, it's not gonna show, but now what if we have a class? So we have an object. So an object should not have duplicate values in there for a set. Now, however, if I've added a person class here that just has a name, and then I add two people to this, two persons to this people set, we should technically only see one Donn in there. Now, when we run this, we actually see two persons. And notice this little number here at the end. I'm not gonna get into the details of what it is, but this basically is saying, look, these are different objects. And so the equality checks inside of here are not being done. Now, a way you can implement these equality checks inside of your classes with the equals and overriding all the proper methods to ensure equality instead of your classes. However, if these are just classes that are going to hold data such as this one is, then what you can do is you can just turn this into a data class. And use this as a data class it'll work the same way, but all these equality checks are already done for you. It's comparing all the fields, et cetera to see if they're exactly the same. So now if we run this, I'm basically adding two Dons to the set of the same data class and say, nope, no, no, it's not gonna allow it. So I could come up here and add another one. Now I'm really sure I don't wanna add Donn three times. What will happen is it will come back and say, nope, there's only one. And so then I can actually maybe just change this one to, let's do this one is Janet, run here, we'll see there's Don, there's Janet inside of there. So there's the set is not allowing multiple different versions in here of the different types of folks. Again, so you have a mutable set. So this is very much a very kind of Kotlin specific idiom. You have immutable and mutable. By default everything's immutable. So you need to think about this, if you want something to be mutable, you need to tell Kotlin, hey, this is a mutable list, this is a mutable map, this is a mutable set. And if you need it to be unique, like a list of data to be unique, then you should be using a set. And if you're gonna be using classes, you wanna make sure that the equality checks are implemented correctly in your given class or you're using a data class. Loops are one of the most common structures in programming. And for loops are gonna be something you've probably used in other languages. If not, they're gonna be something you'll be very familiar with. So let's assume we have a list of values and those values are maybe integers one, two, three, four, five, six, seven. You can iterate over these or perform various different operations of them pretty easily. The first thing you can do is just iterate over the list. You can say for number and values, print line number. And what this is, it says for every value that's inside of here, we're gonna give it a variable called number, and we wanna print that number. And if we run this, we'll see one, two, three, four, five, six, seven is printed. So now I can call this something else. This doesn't have to be called number, this could be called a chicken, I mean, just for whatever, just for purposes of demonstration, this can be anything you want it to be. It's gonna print the value. And this is each value that's in this list will be put inside of here. So the same thing goes for these primitive values. It could be names, it could be anything of that nature. And then again, well that changes from chicken 'cause doesn't make sense, but to name, we'll run it again and we'll see Don, Bob, Janus. That's each one of those values in that list and that's how we can kind of iterate quickly over a list using a four loop. Now there's also ways you can do it if you wanna do some type of counting. For example, if we wanted to say for I in zero until 10, we could print a letter I. Now we're used to seeing I as a counter variable in very many languages. And you'll see here, what's happening is I is defined as an integer in zero until 10. So it says it starts to zero up until we hit 10, We wanna do something. So each time it's just gonna iterate and increment the variable value by one, so we have zero through 10 here. Now there's also nothing you can do. So that's a very common way to do some looping and you can set this as a variable up here. So 'cause that val say upper limit is 10. And so you can say until upper limit. Or you could say here's another way, you stop. So stop and then you can make another one called start. So there's all different types of ways that you can do this in your code. And it's up to you depending on the situation. For I and start until stop. I mean, it doesn't really make sense 'cause then I have to go find what start and stop means. But as it's just a demonstration that you can run this and it'll still execute accordingly. So I'm gonna undo this here and we're gonna go back to how it was before until 10. Now you can also perform stepping. So what this says is for I in zero to 10, I want you to step by two. What that means is basically count by two as we're iterating. So instead of incrementing by one, increment by two. So therefore you see two, four, six, eight. So if I were to jump this up to, let's go to 100. We're gonna see it's gonna count by twos all the way up until 100. And there it has done here at the bottom. Now I can say step 10 and that's gonna iterate all the way up to 100. So we're gonna see starting at zero all the way up until 100, and I've had to do step five, of course is just gonna run it till it's five and then it'll iterate upwards. Now you can also do it the opposite way. So let's go down to the step, let's get rid of step again. So we have item 100 down to zero. So this will be basically a reverse for loop. So it's gonna count from 100 down to zero and it's gonna increment by one by default. Now again, I could do step two here and that's gonna count down by two or I could do step 10 and then we're gonna have 100 all the way down until 10 to zero. So we can see here on the 100 all the way down to zero. It's all one more step by 10. Now the last thing you could have possibly, let's take a simple class like, let's create a quick data class called person. And this person of course is gonna have a name, which is a string. And let's create a couple of people up here. So we'll say val people equals list of I'm gonna say person Don, person Jane, person Karthik. And then what we'll do inside of here, we'll say for I and people we're gonna print the people. And so you can iterate over objects as well. So here we're either writing over each item in a list, just using a simple for loop inside of Kotlin and that's how you can use a for loop and Kotlin. A while loop in Kotlin is very easy to create. So we'll say while X is less than 10, we wanna perform some type of operation. So here we'll say print line X. And then what we need to do is increment the X value. So it continues to increment upwards as we are progressing through the loop. And if we run this, what we'll see is while X is less than 10, we perform some operation. Now a while loop is very useful where you need to do something while a particular condition is met. And so this could be maybe you need to be running something in a loop the entire time and only do it when maybe perhaps a certain button is pressed that needs X it out. Now, for example, if I forget to set this X variable here and increment it and I run this, watch the terminal window, I'm gonna stop this pretty quickly, but then we're gonna stop it now. But this is a very long scroll of zeroes. I mean I'm scrolling a lot and it's barely moving. If I let that run, most likely the IDE would have had out of memory exception or stack overflow exception because it's continuing to print line over and over and over and over and over and over and over and this loop is never going to stop. This is basically an infinite loop at this point because there is no termination that's being provided by it. And when we increment this X value, we're actually incrementing the X and eventually this expression will end up being false. And so as soon as it hits false, the loop will exit. and then the next line of code will run. So say print line, let me see this all done. If we run this now, see all done is printed afterwards. And if we just let this go for 100 or whatever, then it could be a problem. Now, a lot of times folks will actually run these while loops in various different programs in the backend and incline applications while they're waiting for a file to process while they're waiting for something to happen. Perhaps they put asleep command in here for the thread to sleep. And again, there's pros and cons of all this. I'm not telling you and advocating to you sleep inside of your code, that's usually a code smell. However you'll wanna understand when is a good time to use a while loop. A lot of games, stuff like that use a lot of while loops to keep things running in a game loop. So while loops you'll need to make sure that everything's going to run inside of the while loop inside of between these two brackets, everything is gonna run while this expression is true. And so this could equate to be some type of value and perhaps this root loop runs until a user presses a particular button. Maybe it's drawing something on the screen and you're determining where to render things. A while loop is a perfect opportunity to do that. I need something to happen over and over and over until an exact particular condition has been met. That's when you're gonna wanna use while loop. And that's what you can do is this can be a very, this could be a method call inside of here if you want it to be, this could be a fun is okay too, or like keep going, how would you want to try Boolean. And at this point you could have some code in here. It says return, true or false, true or false in this point, we'll say true for whatever reason. And you could actually just call it this right here. So we'll keep going. While keep going, blah, blah, blah. And this method in here could be going out and checking file system, checking an API, waiting for an input or checking the input or interrupts some form to say, "Hey, should I continue to go," and so forth. So that's when you're gonna wanna use a while loop in Kotlin and how to use it. Let's assume that you have a list of people here in a list. Now you could, of course, go ahead and iterate over each one of these inside of people like this, for I and people. And you could print line each one of these people inside of this list, and this will work just fine. There's nothing wrong with this. In fact, many people probably do this day to day and it works. However, there is a more idiomatic way to do it in Kotlin and you can use the Kotlin collections to view that. So you can say, people dot, you can say for each, for each, for each here, and then what you can say is print ln, and then you DIT. And that's basically ended the same exact thing for you. It's gonna allow you to iterate over each item inside of the array, but allows you to say, "Hey, for each item and this array, "do some particular function." Anytime I'm working with a list, a map, some type of collection array or whatever it is, if it's a, some type of collection inside of Kotlin, I'll always wanna prefer to use iteration on the actual list itself. It makes it much easier unless I'm modifying the list and I need to do something with a list. And then I might use a different type of loop structure around it. But for the most part, for most of the operations, I'll have, I'll prefer to use the actual, each operator. So I can iterate each over each one of those things and so forth. So another thing that you could do, I mean, you can also, as we've seen before, you can perhaps map these things together and say map, I could say a sub string of each one of these things, and it would be it.name.sub string. I'll actually just take each person's name and then I can do for each and then I can perform some operation on that. And I could say something like it.to upper case. So I kind of start chaining all these different things together. And as soon as I get one or two of these things in the line, I'd like to kind of drop them here. And then I might say for each, and I can do a map on top of this. Actually, I can do another map here if I want to. And then as soon as I have that list down and I wanna do something with it, I can then do four each over it. Now for each returns a unit, which means it's just a unit. It doesn't return on our map, which is why I was not able to add a map operator before. So if I wanted to do something additionally to this, to transform these values, I would before map here and say it.to uppercase and we can turn through this.reverse. And if we ran this, we would see each person's name is upper case and reversed, but we did not print it, which is the problem. And here, when we actually print it to the screen, we'll see everything here will be all the names reversed and upper cased in there. So again, if you're going to be using any of the Kotlin collections, I highly recommend using the for each operator to iterate over a collection. It's much more idiomatic and easier to read and actually less code. Very often in Kotlin you'll receive multiple lists and you need to combine those lists into a list that does not contain duplicate elements. Now you could iterate over both lists and check to see if either one contains a value and if it does then not include it. So each item in the new list is unique. And that's usually what you're after. Is a list who has a unique values but really at the end of the day what you're looking for is actually a set and a set, if you look at it, is a generic unordered collection of elements that does not support duplicate elements. And that's line 252 here, this is in the collections class of the Kotlin standard library. So basically I set is an order to collection that does not support duplicate elements. Now, thankfully, there are some utilities built into the Kotlin collections library that allow you to do this real easily. So here we have two different lists of people. This one is Don, Jake, Janet, Cavita, and this list has Don, Janet, Jumo, Cavita, Kevin and Kathy. For whatever reason, there's in both lists, both people are present. Now, maybe this could be because these could be groups that are on a popular site that you have, or maybe you're building an event management platform. And so Donn and Janet and Cavita are both going to these two events, but there's some differences here. But you wanna find all the unique people that are there. So this could happen for one, two lists, 100 lists, and you need to find out which ones are unique. Now, there is a built-in operator to do that. So say val, unique values. And all we really have to do is say people.union, more people. And what union will do is a return a set containing all the distinct elements from both collections. And it's gonna return a set of the values. So a set of whatever type it is, and this tastes we're using persons. So what it will do is it'll look inside of here using the equality operations on the data class, which it checks, you know, the values, et cetera. And it'll check to make sure that there's no duplicate. And so now, if we were to run this, so we'd say print ln, unique values, we run this, we'll see that we will only have Donn inside of here once, Jake is only in here once, Janet's only here once to Cavita, Jumo, Kevin and Kathleen. There is no duplicates inside of here. So even if you were to add another one here, say a union something else, and you could say person, and again, you'd say maybe Donn again. So the third time that Donn is gonna be inside of there, you'll see that it's still only added once because the union operator will return you the set containing all the distinct elements in both collections. And that's how you can merge multiple different lists and return unique elements across all of them into one final set. When working with list of data, it's very often to want to know the index of an item as you're iterating over it. With Kotlin's built-in for each iterator, you don't have access to the index. You only have access to the value, which is the person. However, there is an overload that we can use for each indexed. This will allow us to use, get us the index of the current operation and the person at that given index. So for example, if we were to print line this, we could see something like this: it's an index and we'd use string interpolation here. And then we could say person, and if we were to run this and actually we can use move the curly braces there because the class will give us a good two string method. We'll see, when we print this out here, we have the index zero and the person objects that's associated with the index zero. Now this is very useful if you're doing operations in which you need the know the index of a particular value inside of that data structure for whatever reason, you now have the index and you have the value that's associated to that given index to perform whatever operation that you need. So if you need that, you'll wanna use the for each index operator. We're using the lambda expression version here. You may also have seen us with parentheses like this. This will also work. However, you'll notice that IntelliJ or Android Studio, whatever you're using, will give you a hint here that it does not need that. This is a lambda argument so it can be moved out of the parentheses. Basically just remove the parentheses and the lambda expression is parsed in via the Sam operations. And that's how you use for each indexed. Arranging Kotlin allows us to specify a range of values. So here I'm specifying a range of values from one to five saying if the I is in within this range of one to five, then print the value yes, otherwise, print the value no. So if we were to change this to 12 and rerun this, we would see that the value of 12, because I is not within this range of one to five. We can also say for J, this will give us a range very much a for loop variable now. So I can say print ln. So JN zero to 10, the range again is zero to 10. This is the range that we're working within. And so we could say J and when we print this, we're gonna see one through 10 printed down here. Now, again, this is, if you're familiar with the four loops inside of Kotlin, you can also provide stepping. So I might decide to say, I wanna step by two, meaning that instead of incrementing the value of one for the J counter variable, it's gonna increment by two. So it's basically in this range from zero to 10, I want you to execute something if J is within that value, execute some code, count from zero to 10. And then I want you to make a step of two, which as we're gonna see down here iterates from zero to two to four, six, eight, 10. And so every once in a while, you will use ranges. And of course these values can be variables, et cetera. So this is a very simple implementation of how you can use ranges to count inside of Kotlin. A map is a very common data structure in computer science. A lot of times this would be called a map hash map or dictionary. And basically it's a key value pair that you can store inside of a data structure. And this is a map data structure. To create one, you can use the map of helper function and this will actually let you create an immutable map of values. And what this means is we'll have a map where the keys are strings and the values are strings. The keys over here are over here, which means NY maps to New York, NJ maps to New Jersey and CA maps to California. And of course you can continue mapping all these down there. Then if you wanna get that value, it's pretty easy. You're gonna say, do say states.get, and you can parse in the value of NY. And then of course, we would print line this and see what the result is. Now, as you're executing this, you'll see that New York has been returned. However, if I were to type, let's go FL for Florida and run it, we're gonna see that's not in there and a null value is returned. That is because it will return the corresponding value if it exists or null if it's not present in the map. So it's very often that sometimes you'll wanna say get or default if you want a value to be known. So I don't wanna work with a nullable type. So I'm gonna say unknown value. And then at that point, when I run FL we'll get back unknown value that's printed out down to the screen down here. So that's one thing you can do. You can also use get or else. And or else is going to get or else is gonna allow you to provide a function where you can actually return some code, which could be whatever, could be foo. This is function that you're gonna call that it's going to be invoked when the default thing, nothing can be found here to match FL. So if I were to run this, we're going to see that when we say, get her else, foo is being returned. Now back to the original method here of FL, we saw that the squiggly here inside of IntelliJ is saying that we can improve this through the, basically the code improvements. And when we press the light bulb here to get that, or over here, I'll be able to just turn this into an indexing operator. So basically saying, hey, state's just like an array index. Give me the value for the key of FL. And then of course it returns back a nullable string, and we're gonna get back null here. A couple of other things that are very useful inside of matches. You can check to see if a key is there, so you can say, is there a key there, returns a Boolean value. If the key is there it'll return true. We can also say, hey, does this value exist? And we know the value NY does not exist as the values, 'cause the values are here in New York, New Jersey, California, those are the values. These are the keys. And so the values don't container. So we can do that. And so there's a whole bunch of operations. And if you're interested to see what you can do, because a map is part of the collections library in Kotlin, so we can actually get all the entries. So, which is gonna be a set, because remember, in a map, we can't have a duplicate. We can't have duplicate keys. So key has to be unique. So when get back to this result, if we print this, this will be a set of strings. And we can see here, there's all of our entries, which is gonna be, for all the, assuming it's gonna have the keys and the values, all the entries. And then what we can do here is also, we can see the keys. So if we wanna look at the keys, you can also inspect all of the keys, which will return back NY, NJ and CA, and of course you can inspect the values across the board. So you can start working with all of these different types of things in a map. Now, that's how you can kind of set up a regular immutable map inside of Kotlin. Just like lists, which can have a mutable and immutable lists, you can have immutable maps and mutable maps. And so if you were to create your map, so we can just call this map, actually call this items. You can create a mutable map. And what a mutable map will do is it'll allow you to add and remove items to the map. So what we could say here again, we could do the New York to New York. And again, this is just gonna create a map of basically state abbreviations in the United States to the actual full spelled out version. And we'll just do the same three that we have for the immutable version, which are New York, New Jersey, California. And of course you could have all of them in here if you want it to. Now, at this point in time, if I wanted to remove an item, I would use the put command. And then inside of the put command, I would provide another key. And so I could say TX and then the value, what it would be, it would be Texas. Now, notice how we have a little squiggly here. What this is telling us is that the put method can be converted into an assignment call. So I'll hit Alt + Enter or you can also just click on the little light bulb right here when you put my cursor here, there you go. And we can just say, it looks like that. I'm gonna leave it for put for now, just 'cause it reads a little bit better and we can print line so we can see what this looks like. And if we run this, we'll see down in the output window that we have one, two, three, four items. So we said New York, New Jersey, California, and Texas. Now, if for some reason, I wanted to, whatever reason, to remove an item from the state of structure, I could say items remove and all I have to do is provide the key here. And now to provide a key, I'm gonna say, all right, I'd like to remove New York. And then of course, I wanna go ahead and validate that our items were removed. And if we go ahead and run that again, we'll see that we now have the first call, had all four items and the next call had removed New York. Now something interesting will happen here inside of your code. If you already have this list, let's go ahead and remove these we'll leave Texas. And we decide to put an item in here. And we decided to put New Jersey in, though we already have New Jersey up here on line five. If we decide to put New Jersey in, and we say Joisey, because sometimes that's how they say it out here. And we print line. We're gonna notice an interesting thing will happen here. And what happens is, is that New Jersey is actually replaced inside of the race. So instead of New Jersey, it's replaced with New Joisey. So perhaps that was a mistake in our code. Now it's perhaps created a weird side effect where there's a bug. Now there's something you can do to kind of help prevent this. So if you thought that New Jersey was there and you didn't mean to overwrite it, or you don't wanna override it, you can say put if absent. And what put, so if we run this, we'll see is New Jersey was not overwritten. Why, because put if absent will only put the item there if we scroll up, we see this, if the specified key is not already associated with the value, otherwise it'll return the current value. So if we look back at the code here, again, it returned a value. So what does it return? Let's go ahead and check it out. So say print line and what this will return is gonna be the item that was currently existing already for that key. And so we're saying, hey, put something in there if it's absent. Hey, I'm trying to put something into this map that has the key NJ, and I wanna use Joisey. And what it comes back is saying, Well, just letting you know, we already have something in there, so we're gonna go ahead and show you what that is, and we're not going to overwrite it. So that's very useful, so you don't overwrite anything. Now on the flip side, let's say for whatever reason, we realized there are some, you're allowing the user to edit something on a screen and they are removing items and putting them et cetera. And you want them to allow them to remove something, but only if the key and the value match. So if I remove this, if I say remove, what will happen is New Jersey will be removed from the list. And we can see that when we run this. But what if I wanna keep New Jersey inside of there, but I only wanna remove it if somebody accidentally kind of got wise was a wise guy and said, "Hey, the data is Joisey." So what this says is, "Hey, only remove this key "if its value is Joisey." So let's go ahead and run this and what we'll see down the last print line is that New Jersey was not removed. So this key and the value stayed put, because while it did find a key for New Jersey, it didn't match the value. So the value Joisey was not removed. Also sometimes during development you'll need to retrieve an item, but sometimes it won't be there, but you do expect it to be there. And if it's not there, you would like to add it to the map. There's actually something you can use and that's called get or put, and what that means returns the value for the given key. If the key is not found the map, it calls a default value function and puts its result into the map under the given key and returns it. So if we go back here, so get or put we're gonna take this key, and we're gonna say, UT, we'll say Utah, and notice how it's giving us an error, because it actually needs to be an expression. So it's a lambda expression because perhaps this needs to be a method that goes out and does something or a bunch of different things. So it gives us the ability to do that. So we're say Utah, and if we print this, what we're now going to see is because we have not ever added Utah to this map, we put Texas in, and now we're gonna basically say, "Hey, map, get me Utah, but if Utah does not exist for UT "for this key UT, go ahead and insert it for me "and return it." So, which we could basically do inside of here. So we say, val result equals this. And then we would say print line and what the result would be would just be that, that value. So when we run this here, we're gonna see Utah was returned because it's basically saying, "Hey, give me the key. "Give me the value for the key UT." And behind the scenes, the mutable map is saying, "I don't have this in here, let me go ahead and insert it. "And now I've inserted it. "I'm going to return it back to you." So there's one little line of code can save you a number of lines of code if you need to add and remove things to a value. Now of course, this is a map, it's mutable map. You can do all different types of things with it. If you would like to clear it at this point in time. And then we can say, so here, we're gonna print all the items and we'll print it again. The items that clear is gonna go ahead and clear all the items out of the map. So it's just a very, very kind of just typical map, hash map, dictionary, object, data structure that you used to working with. And that's how you can work very easily with a mutable map in Kotlin. Retrieving values from a map is usually pretty simple. And you've seen this probably in other lessons. You can retrieve the value via the get method, what you could just say NY here or this will return you your result as we can see here, or you can actually go ahead and use the indexing operator to return the value. Now, there are a bunch of other things that you can do because a mutable map inside of Kotlin is inside of the collections library. And so there's a bunch of different types of collections. And the map is one of the most common that you're going to end up using. Now there's a bunch of different utilities that you can use on a map, just like for a list and arrays and other common collection data types. So if we say items dot, the best recommendation I can give to you is to start looking around at all the different things it can do. Now, one of the things that I am really happy about is actually the, any method. And any method basically says, "Hey, if there's any items inside of this list." And this is very useful, especially in common expressions where you're checking to see if there's anything in there. And so if you would like to see if there's any items in the map, then do something. So you wanna do X, Y, or Z. Otherwise, maybe you wanna prompt the user to add some values because they can't continue. So any is a quick way to check if there's anything in there. Now there's other things in there as well, which kind of is the opposite of that, which is none basically says returns true if the map has no entries. So is there nothing in there, okay, cool. Then at that point, it's kind of the opposite of any. So this would then operate if there are no entries and this operates if there are entries. So these are very useful little operators onsite of the map. This is also very common stuff that you're gonna see instead of lists of stuff as well. Now, if you also would like to return values outside of a map, again, the most common way is going to be using the items.get or get or default or get or put get, get or else, get values. You can actually iterate over the values themselves. You can also check to see if it contains a particular key. So does it contain the key and NE for Nebraska, this will return true because we do have the key Nebraska inside of here. Now, one of the things you have to be aware of is when you're using the common methods inside of here is you may wanna say, "Hey, I wanna filter "a particular value." So let's say val result equals filter. And I wanna say, item.key equals NY. Now you may think, well, that's gonna give me back my value. Well, that's incorrect, you actually gonna get back a map. And we can see this simply through the code help inside of IntelliJ. But let's just go ahead and print that out to see what that looks like. And when we look at the output, we're gonna see that we actually get back an actual map and we can see it's a little bit further if we actually just filter this just by saying contains. Let's go with the letter N and we'll go ahead and get rid of this NY here. And what this is gonna do is actually filter the map and give us a new map back that only has any of the items in the map where the key contains a letter N. So we have New York, New Jersey, New Mexico, Nevada, Minnesota, and Nebraska. If we were to change this to the letter F of course, we're only gonna get back Florida because as we see down here, because Florida is the only one that has any of the keys with the letter F inside of it. Now that it's kind of a very common filter method you're used to seeing, but you can also use filter keys. And what that will do is remove the requirement for you to type keys, because it is the key right now. So now it is the key, we'll run the same exact thing and it'll filter out the keys. And of course, we just get the letter N, you can see that here, so we can say filter key. So you can start filtering based upon the keys. If you only wanna filter the values, you can say filter values, same thing. Does it contain the letter N and that's only gonna be a capital N, by the way. And so we're gonna see back, we do have New Mexico, New Jersey, blah, blah, blah. If we wanted it to be lowercase, then we will kind of get all of our values that have a letter N which might be additional ones like Minnesota and California and so forth. They're all gonna be included inside of there. So there are different ways that you can work with these things. And of course, these are basically collection operations. So if we wanted to filter those values, and then I wanted to provide a map over it, and I wanted to do something to that map, and again, this is gonna be a transform function that's applied to this. I could say at this point, I wanna take the first three letters of the value. And I just wanna go ahead and map that value and give me the value. So if we did this and then we did the substring of it, and we said, all right, we'll take the first three letters. What would that look like? You may think, all right, I'm gonna get a map back. No, you're actually gonna get a string because we're just actually taking the value here. So you can take a look at all of the different things. So we can perhaps filter keys, filter not to, we could say map keys. So there's different things you can do inside of here. So you can map the keys, you can map the value. So if we take a look at this, it's going to be it.value. And then what we're gonna do here is we're gonna say sub string zero, comma three. And if we run this, what we're gonna see back is now we still have our map intact because we're using map of values instead of the map. So previously again, let's go ahead and comment this out, just to see the difference here, we use map, and then we say it.value.sub string. And we said, all right, we wanna take zero to three. And when we ran this, we just got back a list of strings that contain the first three letters of all the states. And the reason for that is the map is just a mapping. "Hey, this is a transform function "applied to the map entry." Which is basically each entry inside of a mutable maps is called a map entry. And the map entry is a string. As we can see here, it's a string and a string. So the key is a string, the value is a string. And I'm just saying, "Hey, I wanna take the value. "And whatever is returned from this is what's going "to be returned from the map." So it's gonna be the first three letters of the value, and it'll be shoved back in there. And now, if we kind of switch us around and come put this out, there we go and we run this, now, of course, what then that values is gonna do. It's gonna say, "Hey, well, let's go ahead "and take the values and allow you to transform something "on the value itself." So returns a new map as we see from the documentation with entries having the keys of this map and the values obtained by applying the transform function. So in short, what that allows you to do is transform the values, but maintain your map structure. You can do the same thing with things like map.keys. You could do some type of transform function inside of here. We don't wanna say map.value. We would say map.key. I'm gonna say, just make it all lowercase, to lowercase. And now if we to run this, we'll see that the result that we get back is now we have all of our keys have now been turned into lowercase, but we still maintain our map structure and our map data. So anytime you are working with a map and you wanna kind of transform the data, iterate over it, you wanna go ahead and take a look at some of the various different map operators. So you can do flat maps and regular maps and map not nulls and so forth like that. Which are very useful. But this is one way that you can find and filter values other than using the traditional get and put methods inside of the immutable map and maps themselves. One thing that you will eventually run into in almost every programming endeavor is the point in time in which you have to start working with null values. Now, by default, you wanna try to work with as many non null values as possible, which by default is why Kotlin makes you specify nullable types. However, there are times when you perhaps need to perform some type of map operation. Now, for example, this map might have you do something and you have to call into a certain function. So we have a function down here below and it's called find value in web service. Now this doesn't really find a value in a web service, but we are trying to emulate that environment. Let's assume for whatever reason that all of your items in your map, you need to go up to a web service and you need to ask it for some value and you don't control the web service, but for whatever reason, that web service could either return a null value, or it could return back that particular entry, maybe it's been modified. You know, it could be something different or it's just gonna return it back as a regular entry. So if we were to print this out here, what you might see at this point in time is that we have a bunch of weird values in here. So it looks now we have basically a map, which again, is just going to return us back a list of certain values or whatever, we're going over the map here, and we're saying, hey, give me all the values of everything. So it's returning us back a list, but this list has a bunch of null values in it. And we don't want these nulls because now when we have a null value, you can see that the entry that's returned is a list of all these different entries, which makes it very difficult because now we have to worry about null checking and so forth. And if your method that you're sending in this result value into, so if you have another method down the line that says, you wanna process results and process values, and that method for whatever reason takes in a result, and it takes in a non nullable type, which is what you should try to aim for, because nulls can create a lot of problems for you. That's gonna create a problem here because you're parsing in null values now you have to do the checking, now you have to remove them. Well, thankfully, there's a way around that. So you could say map not null. And what map not null will do is if it encounters any situation where a null value is returned, which is what it will do here, and basically what that happens is any time one of the keys inside of our map starts with the letter N we're just gonna return a null, otherwise just return the entry. So if it encounters, you know, New York, New Jersey, Nevada, Nebraska, anything like that, it's gonna return a null, which is what we saw here. That's just, these are all the null values that we see down inside that were returned back and so forth. So we got them there, we got there, we got it there. We got them all over the place and we don't want that. We only want the values that are not gonna be null, because perhaps we need to do some processing. We don't have to worry about nulls, et cetera. So you can use map not null. And if you run this, now, what this will do is Kotlin will say, all right, we're gonna iterate through the map, if we get a null, we're just not gonna include it. So here down on our result, we could see that we do not have any null values whatsoever. These are just values that are not null. So if you ever need to have a list map, whatever, and you don't want the null values included in it, but you do need to perform some type of processing over them, and a lot of times you may not even have the ability to change that code because maybe it's inside of a library somewhere and it returns these null values, you can use map not null to ensure that the map that you're going to get back is not null. Now, of course, items might be missing that you were expecting to be there, but you'll have to handle that as a case by case basis. And that's how you can work with a map and make sure the items are not null. This also works for lists as well. Let's assume that you wanted to generate a very large list for whatever reason to do some testing or perhaps just to play around. What you can use is the generate sequence method that's inside of the Kotlin standard library, which returns the sequence. Now a sequence is a completely different beast, and there's some other videos that talk about what sequences are and why they're important, but just know now that you can generate a sequence using this method. We wanna seed it with the value of one. And then for each iteration, we want to do something, we're gonna perform some type of function here, and that's gonna be the next function that gets executed. And all we're doing is we're taking that existing value, which is the integer value of one and incrementing it plus one. And then we say dot take, and what take is going to do is re return to sequence containing those number of elements. And basically we want to do this 50 million times, which is a lot. And so again, this is a shorthand version for making longer numbers. Now, of course, we could have done this too, that would work, but thankfully Kotlin allows us to have some readability with the underscores. So we wanna do this 50 million times, and then we wanna turn this into a list. So now we have a list of 50 million items that are all different, that have different values in there. And then let's say that for whatever reason, we wanted to do some operation. Now we could do this operation here, and now we have a very large list to work with to see what's gonna happen. Now I'm gonna run this here, actually. And then what you're going to see is it's going to take awhile. And so this is currently running. We'll know when it's done, when we see the print line is done on here. Now, it's very possible that you could hear my computer fan start-up in a second because this code is still running right now, it's still filtering and averaging over 50 million items and it's running on my laptop. And now I can hear my fans turning on, and there it goes. It's now done, so it took quite a while to generate that. And so we get back a double, so we could say the result, and then we say print line and we could say the result, and then we can see the results here. And again, if we were running it again, it would take quite a while to run this, but this is one way that you can use the generate sequence built inside of the Kotlin standard library to generate a very large list to do some testing with. So again, this is generating a with 50 million items in it, and then I'm iterating over it, basically filtering out the items that I have a modulus of three, zero. So if it's divisible by three, then go ahead and take those out and then I wanna average those together. And then what happens is I get the result back, which is a double, and that's printed out down here at the bottom. And that's how you can generate very large sequences and very large lists inside of Kotlin so you can work and do some testing, very useful. Let's assume you have this chunk of code here. And if you run this code, you'll notice that it takes anywhere from on my machine, upwards of nine to about 20 seconds to run. And the reason why is because we're generating a sequence, which is like a collection, and we're turning it into a list. And this line basically says, hey, we have 50 million items in this list. And then what we're gonna do is kind of do some operations over it and then we're gonna calculate the average of some of the values in there, and then we're gonna print and result and done. And so we can see that here. Now, one of the problems that we have with a lot of code sometimes is we don't know how long it takes to run. All I know here is like, wow, that was a long time to run. So how could we measure this? Well, what we can do is actually create a function called measure. And this function measure is gonna take in what's known as a lambda expression. And this lambda expression kinda confusing. So it's called a block and this is just a function. We're just parsing a function. And this function right here has no parameters, that's why it's an empty, it's like an empty parameter list. And this function is gonna return units. So this block right here, right here, this block is going to have no parameters and it's gonna return a unit. So it's basically a void, so it's gonna be a void. So we can parse some type of function inside of this measure block here. But now we need to be able to measure that. So we're gonna say, nano time. And then what we're gonna do is say measure nano time. And we can actually parse a block in there. Measure nano time, if we take a look at it, it executes the given block and return to the elapsed time in nanoseconds. This is built into the Kotlin standard library and the timing file. And all it does is it looks at the system nano time, and then it executes the block. And remember the block, which is what we parsing, it's just a function. So I'll show you how to parse a function in a second, we're gonna parse a function in the measure nano time basically just grabs the nano time, then tells the function, hey, execute, do whatever you need to do. Remember the function is returned to unit, which is basically the same as void in most JVM languages. It's gonna return a void, and then at that point in time, it just calculates how long it took a nanoseconds for this function to execute, because again, that's a blocking operation. We get the start time and we calculate the end time and that's how long it took and nanoseconds. So now we have nanoseconds, but we're not too familiar with how it would work with nanosecond, so let's go and get the milliseconds. And so we're gonna do time unit.nanosecond.two millies and now we're gonna parse in the nano time. That's how long it took. And then now that we have the milliseconds, we can just do print line. And then what we can do is a print ln. We actually take the millies, print ln and parse it in the millies and then type in miliseconds. And what this will do is show us how long something took to execute. So how do we use this function right here? So what are we gonna do, because this is a block we'll just scroll up here. And this is called a block because it looks like a block of code and I'll show you what I mean by that. Let's type in measure and see how we hit the code completion here. And it looks like these little brackets are, we'd like to call that a little block. And so right now we're in the block. This is the piece of code that's gonna get executed. So we can just provide these brackets here because is a lambda expression. And anything that's in between these brackets is considered the block of codes. I'm just gonna go ahead and take all this and I'm gonna go ahead and clean this up a little bit, and I'm just gonna drop it right in here. So now all this code that's inside of here, this area right here, this is the block of code. So remember it. So this right here is what turns into this block. And then when we get onto the nano time, that's, what's executes right there. So anything between these two brackets here. So top bracket, bottom bracket is the block. And all this code that's inside of here is what's gonna get executed and measured. So let's go ahead and do that. So now what's gonna happen is we'll still run this code as we normally would have, but what's gonna happen is it's going to run and then it's gonna show us how long it took the run. So if we run this now, okay, execute this. Again, this is gonna take anywhere from five to eight to 10 seconds. And as it runs, it's basically calculating it. Again, take a look here right now it's executing this code right here. It's already calculated the start time and it's calculating the end time as soon as it's complete. And as soon as it's complete, we'll then see the millies ms, there we go done. So we know that we are done, we got the result, which is the average that was calculated and it took 18,244 seconds. So on my machine, this operation takes a little over 18 seconds. Now it's important to note that measuring time and diagnostics for how valid timing is a difficult task inside of JVM. So this is a very important note that this can not be treated as the end all be all performance checking solution. The reason being is the JVM has in deterministic garbage collection. So we could be in the middle of running this and for whatever reason, the JVM decides, you know what, we needed higher time. It's to garbage collect. And that could happen right in the middle of your execution and might throw this off. So I don't want you to take the measure nano time as the end all be all solution. However, it is a very, very useful tool to kind of get a good diagnostic feel of all right, how long is this taking, why is this taking so long? And you can use this. So what I recommend is taking this little measure thing, you can kind of copy and paste it into maybe a scratch file somewhere or some type of a tool where you say some code snippets and use it any time you want to start measuring things, or you can put it in your project under a utils and then decide if you wanna use it or not. Now, of course, you probably don't want this in your production code, but it's very useful if for some reason you're like, wow, how long is this really taking? And then you can start determining, oh wow, this is the chart, you know? And then if you wanted to, you could say, all right, well, is it really, is the measure actually, is it this piece right here? So then, what is that? So we actually, we'd probably want to take this out here and say, wanna take this list out and say list var list. So we do a late init var list, and that's gonna be a list of integers and now we can just do this. So then we can see how long that took. So if we were to run this now, we would see as it's generating how long it's gonna to take. So to generate the 50,000 and then turn it to a list, as it's executing. Okay, so I'm gonna stop it here. Now what we see here is that it took about nine seconds to generate that sequence. So then we could start measuring various different components here. All right, how long did it take for this other stuff? So if I wanted to, I can move this down here. Okay, that took nine seconds. How long does it take for this stuff here? Let's go ahead and run that. And then you can run this, et cetera. And then you're gonna be able to see how long it takes for each one of these things to be generated. Now, of course, there's a bunch of variables in here. Okay, we're not late, we're late in initializing. Is that impacting it? If you're looking for very, very fine tuned ways to tune it, this is not gonna be a solution, this'll be just something to help you kind of gauge whether or not something is slow and how to do it very easily. So here we can see that the averaging itself takes about eight seconds. So generating the list took a little over nine, almost 10 seconds, and this takes almost nine seconds itself to calculate the average and print it. And that's how you can use the measure nano time to create a measure block, which then you can use inside of your application to measure chunks of code to see how long they take to execute. To understand sequences in Kotlin, it's important to understand how list operate over list of data and how the mapping operations occur to each of the list items. So we're gonna do is create a list of strings, and then we're gonna filter that list. And it's not really gonna filter anything, it's just gonna go ahead and process it and print line, and then return back true basically saying all the items will be included. And then finally, we'll go ahead and then print line it at the end of each one of those. Now for each is one of the things we're doing here is actually showing that a list is eagerly evaluated. So each time there's an operation, a new list is created. So what that means is if I were to perform this same filter operation twice, and I were to say, hello here, what would happen is Kotlin is then going to create a, it's gonna take the list from the beginning. And then in the filter function, it's going to create another list. And then from that list, that list will then be turned in and sent into this list right here. And when this one is done, a new list to be created and sent into here. So basically we're having this type of thing happen here, where these lists are being created over and over and over, but there are new lists. So this is a list off of this one, this is a list off of this one. And then a new list is finally created that we have a list down here. So Kotlin is lazily evaluating those. And the easy way to see that is with this function here. So let's go ahead and run this and what we're going to see here on the screen down here on the bottom is that we have the filter was run for every single item in the list and it didn't progress until the next function until all of the items are processed in the list. The same thing here, and then the same thing here. So as each item walked down, so this function did these ones, this function did these ones, and this function did these ones. So it operated one at a time. This one, then this one, then this one, and that's what's happening behind the scenes. Now this can be home problematic when you have a very large list or many processing steps inside of your list, because if they are processing intensive, then if we're creating a new collection, each time eagerly evaluating it, we can just start slowing down the process. So let's see what that kind of looks like if we were to use a sequence. Okay, so I've copied and pasted some code and I've removed the additional filter up here. Now we now have down here almost the same exact code. The only thing is difference is we have the sequence of, and what the sequence of is it returns a sequence, which is basically something that can be iterated over. And a sequence is returned to value through its iterator, the values are evaluated lazily. That's the key, values are evaluated lazily while lists are evaluated eagerly. So we've created a sequence of this, basically everything else is the exact same for both of these. Now, if we run this, we're gonna see the difference here in the output. And so we have the line that's separating down here, the top from the bottom. So up top we have new list was created, new list is created. We can tell that because all of the items were processed in sequential order before it moved on to the next map operation or the next operation inside of the chain. However, instead of a sequence we can see that the filter executed for the first value X and then X moved on to the next operation, which was for each, the same thing for Y. So as each item is coming out of the sequence, it's processing through the entire chain until it hits down here, hits the termination. And so each item is processed sequentially, so as a sequence. So if you have a very large list or you have a very high intensive chain of events that are being processed, it's recommending that you use a sequence because it's much more performing. Now, if you have 10 items and you're only performing a couple of operations on the list, should you use it, we don't know, what if it's 100, we don't know. As the saying goes, what gets measured gets managed. So how do we measure if something is slow or not? Well, let's go ahead and delete this code and let's go ahead and generate some code that has a very high number of values. Now the code that I'm showing you is actually from a gentleman named Benjamin. And if we go over to, I found this blog post quite a while ago and it's been the one I've relied on for years on the sequences. And he talks about all this stuff in here, and I'll provide a link to it. So I'm actually using the same exact example he's using to show how performant some of this stuff is, very, very useful. Thank you, Benjamin, just want them to give you credit for writing this phenomenal blog article. And I'm basically explaining a lot of what's covered in here, though he does go into some more detail with some other information. Okay, so let's go ahead and get some code to actually show how to measure things. So I have some code in here and this code does a couple of things. We're generating a sequence as we can do, which is built into the Kotlin standard library. So I'm generating a sequence, I'm gonna seed it with a value of one. And then for each iteration again, generate sequence is in the Kotlin center library and it basically says, you can give it a seed value, and then we'll invoke the next function for every single time you want something from the sequence. And so I'm gonna tell it to just increment that value and I want it to do it 15 million times. So I have a sequence of 50 million items and then I say, hey, turn that into a list. And so now I have this list of 50 million items, which is a very large list and it's gonna be processed on the laptop. So 50 million integers in the list. And then what I wanna do is filter it. So find anything that's dividable by three, and then average that out. And of course, we're gonna get a value back. So let's go ahead and grab that value. That's the result. And we can go ahead and put everything back up on a single line here, 'cause it kind of makes sense. And then we'll do is print the result, print line and we'll do it the result. And then what this measure method will do here, which is what you've probably been wondering about. It takes in a block and a block is just a unit of code. And this measure function is explained in another video, but basically shows how long it takes this chunk of code to execute. And so this is what's parsed in. And so let's go ahead and execute this. And then what we're gonna see at the end is how long it took and milliseconds to process this. This could take anywhere from eight to 20 seconds, depends on my machine today. I've seen it kind of range all over the place, depends on what's happening on my machine. And it's still running. We can tell this by the stop icon is active, which means there's something running. The green little dot there also helps us indicate that something's running. There we go, something came back. So we have the result, which is 2.5 to whatever power, et cetera, very large number. And it took us 18,000 milliseconds, so basically about 18 seconds to run this process. So, okay, so we have a list we're processing over the list and remember how we executed those things before. So let's go ahead and change this. And instead of making this a list, let's go ahead and make this just a sequence. And so to do that, I'm just gonna remove this list here. And instead of calling this a list, I'm gonna call this to change the name to a sequence. So now this is a sequence. So it's almost the same exact code, I've just changed the variable name and removed it from being a list. So let's rerun this to see how fast it is to process a very large list and do a couple of operations. Boom, we're already done. So 50 million items with a sequence took 628 milliseconds. And if we did it with a list, it took us almost 20 seconds to do. So it's orders of operations, much more efficient to use a sequence in this manner. At what point should you use a sequence, that's up to you to decide, should you be using a sequence? If it's 10 items, depends on how much processing you're doing on those 10 items. Are you doing 100 operations on it? Maybe it's important to you the sequence then. What if you have 100 million items and you're only doing two operations on it, like we're doing here and only doing a filter and an average. Should you use a sequencer, probably so. But what if you have 10 operations, it's not really doing much calculation and it's only maybe 10 items in the collection. Should you use a list maybe, maybe not, it's up to you to decide. I wouldn't focus on making sure that everything is performant, that's premature optimization. However, you can use this nice little handy measure block as we have here inside of your code, throw it inside of a utils folder or whatever and then measure something. If you find piece of your code, kind of being really slow, I highly recommend slapping the measure block around it. It'll give you a good rule of thumb of like, alright, is this taking a long time or where is this slow? If you find you're processing around a large list or list operations to be slow, at that point, it might be time to take something and make it a sequence, which it could be also very interesting. So let's assume that for whatever reason, we have a method. And this method is called get list of customers. And this list of customers comes from a database. And it's gonna return a list of data and this returns, let's call this integers for now. And then what we'll do is I'm just gonna do this right here, it's gonna return a list to that to list. We're just gonna pretend that this is actually is from a database. So I'm gonna pretend it's in the library and I get this out of here. Okay, whatever. So now I'm gonna have my changes to list. Actually it will leave a sequence. Actually, I'm gonna change it back to list and I'll show you why in a second. So we're gonna say this is list and say, we'll say val list equals get list of customers. So there we go. Now we have our list of customers. we're gonna perform some filtering on it and it's gonna run. Now, as we know, this is gonna take a long time because this list of customers here is a very, very large list. Now, again, we're generating this on the fly, so let's go ahead and pretend you don't see that. Let's assume that it just returns a list and we don't have control over that. But however, now that we have this large list, however it's been processed maybe on a background thread, but now we have it and we need to process it. We now have control of them. So this is a very large list. Let's go ahead and run this again. So we have that list of customers, actually, we can go ahead and take, let's assume that this is out here 'cause we don't wanna measure that component there. So we have our list of customers. That list of customers is gonna take probably eight or 10 seconds to generate because it has to turn it into a list. And most likely this block of code will probably take another eight or 10 seconds to execute as well. So perhaps this is on a background thread. We can't control that, that just is what it is. And we're maybe we're gonna get that data back. But now once it's off that background thread, we can control it, so for example, it takes eight seconds. So at that point, how could we speed this up? Well, because this is a list at this point in time, everything's running slow. Now, thankfully there's actually a nice method on the collection here called as sequence. And what this will do is will turn this, I'm gonna go ahead and break these into new lines here. This will turn this list into a sequence. And so what this will allow us to do is get some performance benefit as treating this list as a sequence. And then from this point on after this line, everything will be operated sequentially as a sequence would. So let's go ahead and rerun this. And as we're running it here, we're waiting. And of course, it's gonna take a while because we're assuming this is on a background thread. It's gonna take awhile eight or 10 seconds. And then once we get a result back, boom. Now this code has been measured. Previously, it took them as nine seconds to run. Now we turned it into a sequence via the as sequence helper method. And we have transformed this from a nine second operation into a 244 millisecond. 244 millisecond operation orders of magnitude faster just by slapping on as sequence. Now, of course, you're not going to get this benefit if your list is 10 items, you're not gonna see that drastic improvement. It's gonna be very negligible at that point. So follow the mantra of whatever gets measured gets managed. If you find slow piece in your code, see what's taking a long time. If it's a (indistinct) list or a map or something like that, perhaps you need to figure out a way to turn it into a sequence so you can get some performance benefit out of it. And of course, be sure to check out the blog link in the show notes. So big hat tip to Benjamin for demonstrating a perfect example. I tried to create my own, but his example was just so succinct and great. Thank you Benjamin for doing that. What's the difference between a list, a set, a map, and a sequence? Well, a list of data inside of Kotlin can be a list of anything. It can be a list of primitive values such as strings as we have here. So we have lists. We have some names, Donn Tushar, Cavita Evelyn, Felicia. This could also be a list of integers. So we could have a list of integers of one, two, 12, 23, 44, 66, et cetera. And they can be a list of integers. It could be a list of objects. So any time you need a list of data to work with, you're gonna wanna use a list. Now, as you can see here, the list right now is strongly typed to be a list of string. And again, if you need it to be a mutable list, you would type mutable list of which means this is a list that you could change. So the same thing goes for almost any of the collections inside of Kotlin. If you want it to be mutable, you'll actually slot the word mutable at the beginning. So when do you wanna use a set? Well, a set remember only include unique items. So in this case, I've set a creative set of these four items, Don, Tusha, Donn and Tusha. Now, when we actually print these out, if we were to print it, we would see that this set actually only includes two items because a set does not allow multiple duplicates of an item inside of there. So here, it's going to see that Donn is then presented twice so it will not add the second one. And the same thing for Tusha has been added another time. So it will not add a second one. And this result will only be in the set be Donn and Tusha. So if you need your items to be unique inside of basically you need a unique list, you're gonna wanna use a set. So what about a map? So when would you use a map? Well, a map is basically a mapping of one value to another, and I like to think of it as a key-value pair. And that's what this is, is a map right here with the helper is we have the key, which is a string and the value, which is a string. Now this does not mean that you have to always use string and string. So we can say map to, we could actually say map of, and I'm going to say one to Don and then we say two to Tusha. And what this does, it creates a map with the key being an integer and then the value being a string. So you can create a map of all different types of things. If you wanted to create it as an object, you could create it as its own custom object. If you had say a data class, it'd be person and we maybe had val name, it's a string. What you could do is you can make this map, turn to person, Donn to, maybe he does Android. And we wanna have a map of person Tushar, which then maps to let's say he does J2EE. And at that point, we now have a map where the key is a person object and the value of the string. And of course it could be flipped around. There's no rhyme or reason to what you would want. It's just a data structure that allows you to have a map and a key. So if you ever need to have a map and a key, you're gonna wanna use a map type data structure. All right, so when would you wanna use a sequence? Now sequence is basically going to be anytime you want something that's gonna be more highly performant. And this could be, if you have a very large list. So maybe 50 million items in a list or you have a bunch of items you need to process. So you have 100 items in a list and you need to process it through a bunch of different maps. You're gonna do list.map, and then you need to do another map for whatever reason, a map, and then you're gonna do it for a reason, you can do a flat map and then you're gonna go from there and you're gonna do a filter, or you can do an average, actually I'm not gonna work that way. But the new map won't work on a flat map because you have a list. And then so you have, you know, some averages that are, or whatever you get inside of here and you can filter and all different kinds of things. And let's say, assume all of these different things had very complex calculations that took a lot of time because lists and so forth are evaluated eagerly. A new list is created each time. Then there's actually a lesson that you can view that will show the performance metrics of actually using a sequence overlords lists. And so if you need performance, you'll wanna use a sequence. Now, you can also take a large list and turn it into a sequence here. You can also turn a map and turn it into a sequence as well. So if you have a large map or a large list and you need to process it, sequentially, you can just slap as sequence on there and it will process accordingly. And then you can make things very quickly. And then if for whatever reason, after you're done processing the sequence, you would like to turn that sequence into a list again, you can turn it into a list as well. Of course, you're gonna wanna measure the performance benefit. So sequence usually use for performants. So if you have large lists, large maps, or you're doing a lot of mapping operations, you're doing a lot of operations, you know, on the data structure itself, then you wanna go ahead and turn it into a sequence or use a sequence if possible. And of course you can always use generate sequence. So that's the difference between the list, set, map and sequence. Kotlin does not have a ternary operator. So if you're looking to do something like this, let's assume we want to get the length of the string, but we only wanna do it, if it's not null, perhaps we could say something like this. You might be used to doing like this. If name does not equal null then we're gonna do name.length, otherwise wanna do zero. So you may wanna do something like that. You'll see here, we have to use some wiggly things. It's not letting us do this. This is because the ternary operator does not exist in Kotlin. So to do this, you need to use a single line if statement. So if name's not equal to null you can do name.length, else zero. Now this will give you the length here so we can say print line length, and then you can run the code here and you'll see that the length is then returned. And for whatever reason, if this is null, we can return that. And then we'll get zero because it's handled accordingly. Now we get this nice little use here where we can replace it with the Elvis expression. If you know what that is, you can use that. But if you are looking for a ternary operator in Kotlin, it does not exist. So you wanna use a single line If Statement instead. It's very often that you could be working inside of some code and you may need to do something. For example, you may need to get the length of the name of a particular value, maybe of a string and you wanna say name.length. Now what you'll notice here, because we're working with a nullable string, that we cannot do this. Only safe calls can, this can happen. So we're gonna get back. We could change this to a safe call. This would give us a nullable integer. However, if we're working in an environment where we do not wanna work with nulls, this is not going to work for us. So there's a couple of things we could do. If we can change the data type to actually be non-nullable, that would be fantastic. However, sometimes data comes back from other libraries, such as Java libraries, where the nullable type is just going to be there and we can't do anything about it. However, we may understand at some point in time that we know for certain that this value is not going to be known. Now, this is something I usually don't recommend, but there's a way to do that. And we can use the double bang operator to tell Kotlin, look, I know what I'm doing. I know that a named value is not gonna be null, which we can see directly from here. There's no way on nothing in line five. That's making this not null. We're making this null. So we're basically telling Kotlin, don't worry, I know what I'm doing. Trust me, just go ahead and treat this as something that's not null. And then what we'll have happen is we'll go ahead and we can say print line length, and then we can run it. And then what will happen is we'll then have a int value, which is again, it's only going to be a regular integer, so it's not gonna be nullable and then we'll get the length of four. Now, the real challenge comes in here when you're telling Kotlin, hey, trust me, I know this is not gonna be null, but for some reason it ends up null. And this can end up showing up inside of your code multiple times. So if you find yourself doing something like this all over the place, you might wanna inspect your code to see if it's something is correct. Now on tests, you may have it a lot, but in this instance, perhaps I've said, hey, this should not be null. Maybe this code on line six is buried somewhere in a method somewhere. But what happen is when Kotlin says, hey, go ahead and just treat this as a nominal value and give me the length on it, and it's null, it'll throw a Kotlin and null pointer exception. So this will blow up your application. So you do have to be careful in this regard to see if you want to actually work with this. So again, if you know what you're doing and you wanna tell Kotlin about it, you can go ahead and say, hey, Kotlin, I know what I'm doing. Use the double bang operator. The same thing could go for a class. So if we have a person and then we have a vowel name and that's gonna be a string, we could actually say something like this. And we can have a nullable person. Let's say person, and that's gonna be a person that's nullable, again, maybe this comes back from an API and it could be empty or whatever. And so the person could be Don. At this point in time I could say, if I wanna kind of get the values of print ln, I wanna print the person's name. Well, this is not gonna work because again, person is nullable and Kotlin will say, hey, we're not sure if this is a safe call because well, person's nullable, and this could end up being a null pointer exception. So I could handle it this way. And if I did handle it this way, if we ran it, it would still work, we would get the person's name. It would be Don. However, if for some reason this was comment that out, and this was no we're now gonna go ahead and we run this again, it's gonna show up as null. But again, maybe we don't want that, we want it to say, hey, trust us, we know what we're doing. So you can use also the double bang operator inside of here. Again, if the value is null as it's happening here, you're going to get this Kotlin null pointer exception. So if we were to just remove this, again, it's going to work as we expect. But usually my recommendation is if you see yourself or wanting yourself to add these values all over the place, you might wanna figure out if there's a way where you can not use that or use something like required, not null which is in another video and link in the show notes below. In some Kotlin code bases, you may find yourself wanting to type the double bang operator to force a nullable value to not be nullable anymore. You're basically telling Kotlin, hey, trust me, I know what I'm doing. This can happen for regular primitive nullable types, such as the strings integers, et cetera, Boolean values, or it can also happen for the nullable objects such as a person object that we have here. Now, there's a way around this. If you don't wanna type the double bang operator, you can also use what is built in to the Kotlin standard library, which is called require not null, and require not null basically will turn this value, which if you look at it into a non nullable type, if it's null, though, it will throw in a legal argument section, otherwise returns the not null value. So if we run this here, what we'll see is it'll continue to run this fine because we have the length of the name. This has now been turned into a regular string, not a nullable string. However, if for whatever reason, this comes back from an API or database or something, and that value is null and we process it. What will then happen is Kotlin will throw an illegal argument exception saying, hey, the required value is null. Now, to be honest, this error message while helpful, this required value is null, in a large code base, this is not very helpful. This can be kind of like, okay, there was a null value somewhere that we shouldn't have got, where's it at? Okay, let's look at the stack trace, there's a stack trace. Let's go investigate it. Sometimes it helps to have some additional information inside of there. And so you can do that, there's actually an overload of this, which will allow you to provide a lazy message, which will be the result of that being called. And so it can be a function. And we're just gonna go ahead and use the lambda. And I'm gonna say something like the name should not be null, but it was, exclamation point. Now, if I run this, we'll still get an illegal argument exception, but we'll get the message the name should not be null but it was. And you could provide some additional information inside of here, perhaps that might help you debugging, if you were throwing in legal argument exception, and you have a crash handler on your application, if you're having an Android application, this information is gonna show up inside of your crash tracking utility. So you might be able to say, hey, here's the value that came in. You could throw additional information inside of here that might help you diagnose what it is. Sometimes, maybe a backend changed and they are sending a nullable value that was not supposed to be nullable, and you're not expecting it to be null and things blow up. And so you can just say require not null and that'll get you around it there. Now there's also, you can do the same thing for objects as well, not just primitive types. So we have this person here, perhaps we don't wanna use a double bang operator here. We can go ahead and say require not null. And again, it's just gonna take this object and make it to this not null type. And there we go. And if we run that everything should run accordingly, except we're gonna get that exception, so let's fix that. That's the exception from up here. So I would just say Don. And if we run this now, we'll see that we get the Don, which is down here in the bottom, which is from this print line down here. So we can actually just go ahead and get rid of that to make it easier. And then if for whatever reason this person was null, same thing happens here. So you can say require not null on an actual class, you'll get the same thing, the required value was null. Again, just like the other version, it's the same exact thing. You can put some other our message here. So hi friends. Not that you would wanna do this in production, but this basically oops, and that needs to be inside of a lambda. So that just needs to be there. And then it'll actually show up in there, so hi friends. You can put any message you want inside there. So person should not be null, here we go. And then once you run it and you'll see that the person should not be null. And that's how you can use a require not null inside of your application. So you may have some code in your application that looks like this, you have an object, that's a person. And for whatever reason, it comes back from the API as null or it doesn't. And then inside of your code, you're using the double bang operator to force the person object to not be null. So of course this wouldn't work. You could do something like this, this would also work. It's actually much cleaner in my opinion, this way than forcing it to be not null, because we could not run into situations. But as for whatever reason, let's say you do have that or you're using require not null. And require not null will basically turn that nullable person into a regular person. However, there's also check not null. And check not null basically does the exact same thing as required not null. And if we go to the implementation here, we'll actually see that check not null, which is down here at the bottom is basically the exact same as require not null. So let's go ahead and shrink this down. Let's see if we can get it all into the same screen. Very close. So require not null is up here. And then we have check not null, which is down here. And we could see it as a contract for require not null checks the value, we get the lazy message. And if we look down here, the code is exactly the same. So it's doing the same exact thing. It's basically checking to see if it's not null and requiring it if it's not null. And if it does, if it's there, it returns the value, otherwise it doesn't. So you can use either one of these check not null or require not null, depends on what you'd like to do. I have seen in multiple code bases where a check not null will be somewhere randomly in the code base, perhaps after an API call, just make sure that the users not null. And then at that point in time, the person might be a var if that's the case. So if that's the case, it might be var person equals check not null or person two. At that point in time, check not null. So you hadn't have a nullable version. So you can either use require not null or check not null. Let's go ahead and assume you have a list of names or some list of data that has come back from a database API call or anything like that. Here we're just creating a list of names and we have a couple of null values in here. Now it's very often that, for example, we now have a list front that is a nullable string. So a list of nullable strings and in our application, or maybe even a library that does not allow a list of nullable strings, all the values have to be present. So what can you do here? Well, there's a couple of things you could do. You could go through each one of these things. And so we could say you could filter them out. You can map them to a correct value. Or if you just want all of the items that are not null, you could say val names that are not null. And you could say names dot filter not null. It's a nice little help our method there. And then you can just go ahead and print line that to the screen, name that are not null. Let's going and fix that because it should say names. And if we go ahead and run this, what we'll see is we're gonna have all of the names and we are not going to have the null value. So very easy to filter out a list of all the non nullable types. So if you don't want nulls in your list and they are already there, you can filter it out with the easy filter not null method. One of the keys to understanding Kotlin is understanding the type hierarchy system. So this is a great diagram, which is actually used over here from this gentleman who created it, which is Marcos Sandoval. So, thank you, Marcos. This is a great diagram that illustrates the Kotlin type hierarchy system, which basically states that any non-billable type is always going to extend to any type and any nullable type will extend the nullable any type which we can see here. And then we have also nothing in there, et cetera. We get into that later. So Boolean, string, a class, you create unit number, everything extends any. So you can use any or the nullable any type instead of your functions as a parameter type. So this is very similar to in the Java world of using object. Now you can actually test this by writing a simple little program And we'll say Val age equals 32. And we could say something like this. If age is any, then we can actually say print is any. And that would tell us if this is any. Now this is how we can check for types inside of Kotlin. This is type checking. We're checking to see if age is of this particular type. Now I can change this to say, hey if age is a string and we're gonna see here, the incompatible types type string and into it already knows the compiler knows at this point in time that this is not going to work. So what we could say is I can change this to the type any. I could say if age is a string, then it would print that off. And if we run this, we'll see we get nothing back. So let's go ahead and add an else statement here. Print ln.any. Now, if we say not any, well, this is a string, so we're just gonna change this to it is, it is not. And so is it page, which has any, does it equal string? No, it's not. Okay, well, is it a int, let's run that, see is it an int? We see it is, we can change these to different values. So we'll say, is it a double? And we can run is it a double? It is not. So this allows us to start doing various different types of things. We could change this to because again, string. We'll use Donn actually is a string and we'll see that is not a double, again it still compiles. Is it a int? No, it's not because it's an int. But if we were to change this to string, then we would see that we can actually easily type check it. Now this also works for other, your own custom classes too. So we could say a data class say create an order. And maybe it has an amount which is an int. And then maybe we have just a regular class that is a person. And this person has a name, which is a string. So we can create an object here, say, val, I'll be J and we would call this. We're gonna say, hey, this is any, so we're kind of just, we're using the root object here. And we're saying that this is any. And we're gonna create this person and his name's gonna be, let's call it Bob, Bob. And then what we can say, let me get rid of this age thing. We'll say this object string. No, it's not, so it's not a string. And we see that it is not. However, it would be nice to see what kind of object it is. And we can do that pretty easily say print line, and we can just inspect the object, object.javaClass.name. And this will give us the name of the Java class that it is. So is this person a string? No, it's not, it's a person class. So let's change this back to a, let's go do a 16.0 let's see what that is. So is it a string, it's not, and we'll see, it looks like it is a double. And then we can also print a, we could change this to, you can change this back to Bob. And of course, this is gonna say it is, but let's go ahead and change this to int now. And if we run this, what we're gonna see now, is it gonna say it is not and the type is Java.line.string. Now we can change this around too. We can say, is this any. Now this is just gonna execute for everything, it's just gonna say it is. So it is, we could say 12. We're just gonna say that it is. And we could say for the data class order, which is gonna have an amount of 120 is object any, it is so any is the root object of anything. So this allows you to do some cool type checking inside of your application, because you may have an application that returns, you working with some different types and for whatever reason, it returns back in any which can happen for some various reasons. And so if, for example, let's go ahead and in a string. So val value and it's gonna be a string and we'll use a one expression, one value. And when we say the value is one, then we want it to return. Let's say return here two. And if we return two, we're gonna return a hello. If we say three, then we're gonna return something else, such as a true Boolean value. If we ask for four, then we're gonna return a double. And so we'll say 16.01. And then of course we have to provide an exhaustive else because this could end up being a certain value. So we can get rid of that, there we go. And now at this point, what we can do up here is we can just say, what we can do is if we were to say, get stuff in order to parsing them to number one there, what that's gonna do is any is gonna report to it is. But let's go ahead and change this to string. So if my method, maybe a library I'm working with returns, an object or any, or something like that, I can do the type checking with the is key word. And so it's not, so it's an integer. What about number two? So whatever this method returns is in any, it is. So now I can actually do this and do different type checking and perform various different operations based upon the type checking if I'm working with any type. Sometimes you have a method that returns any, and you need to cast that into a particular value. So maybe we know that we need this to be the age and it's gonna be int. And so we need to cast this into an integer. So we could say obj.as int, and when we run this, because the OBJ is in any value. If we run it, we'll go ahead and see that it did execute correctly and we forgot to print it. So we print this to the screen, we'll see that it was run as OBJ to 99. Now, for whatever reason, we were working with the string and we wanted to use a name. So let's just call this cast so it's easier. And this was a name and we'll call this, this was a string. We're expecting it to be a string. And we want this to be cast to a string. And of course we want maybe our method of assuming this is from like an API or something like that or some library that gives us back an any type, somewhat for some reason. We know that number two returns, hello, we can then cast that to a string. And then at this point in time going forward, we now have a string. And what that gives us is the ability to actually use different, for example, I have the length, I can do all different kinds of stuff with this as well. If for let's even take this a step further and say that I had a data class, a person, and that person has a name and that's a string. Then what I could do inside of here if we say five, I would then return back person with the name of Don. So this is gonna work because I have two, and I'm gonna return back to value of two, but I could also do something like this. I could say five. Now this of course is, if we look at object, OBJ, what do we have on it, what is it? It's any type as you can see here, it's any. So the things that we can do with it. If I know that when I parse a five in, I'm gonna get a person, I can't say any.name. Name doesn't exist because I'm working with an any type. So I need to cast that to have the type them, which I can work with it. So five let's go and cast that to a person. I need to make this person. And now I have a person object. And what that means is now I can say cast it. And now I have the name and I also have the copy method, which is built into data classes. And everything else that data class is also have along with it or any other class that you're working with. So now I can access the name. So I've basically turned this any type right here, any, I've turned it into a person. So now when I work with this person object, which is right here, I can see that it's a person and I can use the type system accordingly. So if I run this here, we'll see that we can run and we can get the object turned into a person. And that's how you can perform casting in Kotlin. Let's assume you have a method called get stuff, and it can return in any type, which is the root of all types in Kotlin for non nullable types. So if you parse into one, you get an integer, if you parse in two, you get a string, three, you get a Boolean value, four, you get a double and anything else, you get a false. So sometimes we need to perform various different casting. We need to cast it to a particular value. So I'm gonna create a new variable called cast it. And what I'm going to do is say OBJ as int. And so what I wanna do is I wanna get this. I wanna cast this value that I get back to an integer. And so when I try to do this, I'm gonna run this. And what we're going to see is we're gonna get an exception. And the exception is a class cast exception, basically stating you can't cast... Basically a string can not be cast to an integer. And that's because the value we got back from get stuff, we parsed it two in two came in executed this line of code. We returned the value of OBJ was hello. And we're trying to cast the string as an integer and Java says, nope, no dice, that can't happen. So we got a class cast exception to, how can we solve this? So we can't cast it this way. So what we could do is we could do some type checking. We could do if object and we change this to as is into, we could get back the OBJ else, maybe willing to return to zero, that that would work. We could run this, and this would work accordingly. We'd get back zero because object is not an integer. If it was, so if we came back here and we changed to a one, which again, would return us back in any value of one, we'd get back the one there. And actually who's go and change this to just what makes more sense to change it the 99. And so when we parse in one for the value of the get stuff method, we'll get back 99, which has hap executes this line right here. And since it is because an integer, we got this back, that makes sense. However, there's also another way that we can do this as well. Perhaps we're okay with working with some nullable types. One thing we can do is we can cast it and do a safe cast. And what the safecast is saved as an integer. And what this basically says, hey, I would like you to, as I'm gonna change this to two, done a second, we'll do that. I would like you to try to cast this object right here, this object as an integer. And so when this piece of code executes, if it can execute it as an a can do it, it'll return the integer, otherwise it'll return a nullable type. So if we re run this right now, we're gonna get back 99. That makes sense, because if we parse in one, we get back 99. So let me change this to two, which is gonna give me back a string. Now, if I run this, what we're gonna get back is null. And the reason for that is because this line of code right here that executed said, hey, this object is actually null. And so the smart cast said, hey, I recognize that was null. So I'm just gonna go ahead and give you back a null value at that point in time, we can continue on and our application didn't crash. And that's how you can use a smart cast. So this could be anything for Boolean, can I parse that to a Boolean? And if we run that, is that going to work? Let's see, no that's null as well, it's not true or false. So we can't do that. So anytime you need to cast something and be safe about it with a nullable type, you can use a smart cast, which is the word as with a question mark after it. You've probably heard the word generic tossed around inside of Kotlin various times. And the word generic just means that we're using a class or an implementation in a very generic manner. And so here we have an interface which has a list, and then it has this weird little parameter. Ignore the out for now. And it has this little E and that E is the type of elements that are contained within the list. And so this is a collection which extends collection, and it takes a type E. So what this allows us to do is have all the same operations, like we can some contains, is it empty, we can use get index. It allows us for high levels of code reuse and allows us to specify different types. So we still have type safety. So I can do that a list of strings, or I could do, let's call this list of strings, or I could say, val list of ints. And then I could basically either say a list int and as a list of, and I can say one, two, three, whatever. And I can have my list of integers. And now they're all both going to have same operations. List of strings, I'm gonna have the get operation, I'm gonna have a list of ints. I'm gonna have the same get operation. Now, a lot of these things are already built for you. So a lot of these generic operations and generic classes, such as a list and a map and a set, anything like that have already been built for you. And so it's very easy to use them. So if you want to use a map, you can specify a map and say map, and you can specify the types that you would like as well. So the key is gonna be a string and the value will be an int. And then perhaps you wanna create a map of that. And now you don't have to provide these type parameters, because it will be inferred if from the map creation, but here I'm just being very explicit about it. So I might map Donn to 32 and I might map Tushar to 42 and so forth. But now I have a map and that string is it's key and the value is its int. And this is all already built for us. So here's just using a map interface. And so there's a bunch of different maps that are implemented inside of the Kotlin collections library and all different kinds of things. So you can see, so we'll go map.entries and we can look inside of there and see all the various different entries. We can do different, you know, for different things, such as map.map, which is kind of weird sounding, but it's part of the maps function and allows us to transform items inside of there. But the key thing here is that all of that common functionality is buried inside of these generics. So you don't have to worry about building a list, just custom for strings or a custom list for integers. All that's buried behind the generics themselves. And the good thing is you can actually build your own generic classes, which I'll show you in another video. However, these are the bald basic built-in different types of generics. And very often when you're looking at the documentation, you're gonna notice these single letters. So E in this case is gonna be the type. Sometimes you'll see multiple different types. So if we go look at a map, you're gonna see different types in here, you're gonna see K and V. Now the letters, they don't have to be letters themselves, but they're using very good terminology here. K for key and V for value in this case. And so up here, the E I think is for element of the list. And so it was a very element. Now you could actually, if you're building your own generics, you can actually provide a full on word for the different type, if you would like to. But a lot of these things are just that way. So that's what generics are. They allow you to encapsulate common functionality across a large swath of code and folks can end up using them for various different things. So for example, here's a mutable list that extends another one. And all that's doing is saying, hey, we're gonna use all of the exact stuff from within the list and everything from within a mutable collection and we're gonna create mutable list. And by the way, here's how we're going to override this remove function and here's how we're gonna do all these other different types of things based upon this type of collection. So these are the generic kind of collections classes. There's a whole swath of things that are all generic inside of various different apps that you're gonna use instead of different libraries. HTTP libraries will have you returning a different component. If you're using reactive libraries, we'll be working with reactive streams of different types of data and so forth. And those keys and values and et cetera, are all performed and mapped inside of the various different generic classes themselves built by the developers. And so these are all the built-in ones, and here's how you can kind of understand what generics are at a very high level. Let's assume that you wanna create your own generic class. So the first thing you need to do is define a class and what we're gonna do is create a class called even list. And even list is going to give us, we're gonna take in a list, so here we'll say val. We'll say list, and this'll be a list of type T and then inside of here, we're gonna do something or we're gonna return so function. And I we'll say items. And then what we're gonna do is we're gonna return a list out of this, and it's gonna be the same type too. So notice how I'm not providing anything inside of here. So what this class is going to do for us, this is generic class. And I'm basically saying, look, this class is gonna hold a bunch of things and they're gonna be of type T. Even lists, it can be of type T, you don't know what it is. The constructor is going to take a list into it, and it's gonna be of type T. So basically if it's a list of strings, then it'll be even list of strings. And then what will happen is I wanna return the even items. And so I'm just gonna go ahead and return those even items. And to do that, I'm gonna say return list.filtered. Now, I need to put val on here so I can get access to it. Val.filtered index. And then what I'm going to do is parse into lambda and it's gonna give me an index and a value. And then from here, what I can do is say, index modulates two equal equals zero, which means, hey, if it's divisible by two, if it's a second item inside of there, then if it's divisible by two, so two zero, two, four, six, eight, 10, and then go ahead and return the items. So only return the even items in this list. So I have not actually specified that I'm working with strings or integers or anything. And here I'm using this type T. So how would I use this class, this is a generic class that only works with even items. And perhaps for whatever reason, I want this to be something I can use all over my application, because I am maybe building some type of game where I only work with even ones. And you know, of course I could build another one called odd. So to do this, I could say val result equals even list, not event list, even list. And I could specify a string here, I want it to be string. I need to parse in, I'm gonna parse in my list of strings. Now look, the IDE says, hey, we can remove the explicit type arguments because it's going to be inferred because you're parsing in the list of strings. And we already know that this list of strings is this type right here is a string. So we already know that you're a string over here. So you don't really need to tell us that it's a string. We kind of already figured that out for you, so you don't need to provide that information. So then what I can do is I have my list of strings. So now I have an event list of strings. And if I wanted to print out the actual even ones, I could say, result.items, which is gonna call this function here. It's gonna call the items, function. And wait, maybe we even wanna call this even, be a little more explicit here, we'll call it even items, which makes more sense, result.even items. And if we print this out, what we're gonna see here, and we're only using the strings right now, let's see Donn and Mary. Now the power of generics really comes in that you can reuse it with different types. So let's reuse this again, the same val results, let's say other result 'cause we need another variable and this one's gonna be event list. And this time we're gonna take in a list of ints not event, even list. So we're gonna take a list of integers in, and we're gonna print this out, I'm gonna say result.even items. And when we print this one out, we're now working with integers. And I used the wrong result. There we go. And if we use the other result, which is the proper one, we'll now see, we were able to reuse this class's exact functionality. I didn't have to write it twice, I'm now relying on a type, this called a type parameter, the type key to do some work for us. And if we can look at this value, look, if we go over here and actually do something with a value actually, we can see the value over here is actually type T. So I don't know what that type is. I could say the value is of type blah. Now I could do some, I could do as value as a string. So if I parse in an any into this, then we could do some type of stuff inside of here. Now that's gonna limit me on what I can do, but depends on my implementation of my generic class if that's what I wanna do. So here now I have an even class that will work exactly with what these and I could be. Again, we could even go a step further and we can go class person and we could easily do name string, and then we can create a list of people. So let's just do that, val people equals list of, and then let's do person. And now if I have these people and I wanna print it out, we're just gonna do this. We're gonna single line this right now. So I'm just gonna do even list and then I'm gonna parse in my people. And then I'm gonna say, I wanna get my even items. I'm just gonna print that out. And then what we're gonna see here is even with my own custom class, I'm getting that. And I can make a little bit nicer and just say data class, 'cause data classes will give you a nice little two string representation of your class already as we can see here. Now the two string is represented that two strings is built for you by default and data classes. So I can print out and everything here. So now I'm only receiving the even number. So I'm receiving Donn and Mary, but Bob has index number one and Felicia's index number three. So we're just gonna completely skip over that. Remember 'cause these are zero-based index. So remember that this is zero, this is one, this is two and this is three inside of array index 'cause we're zero-based. So this is why we're getting it even index. So the other cool thing about this too, is like, let's say that you don't really like the type T. I prefer to use the word T because it means type to me and it's a type system. I could also just say this right here. I could rename this type and say type like that. And now it could use the same thing. I could even call this something else. If I wanted to call this, we wanted to call this tiger. I call it tiger. It doesn't matter what I call it. This is just a tight parameter that I'm gonna be using. And if I look over here, now this might get confusing because I would never use the word tiger because tiger has an implicit meaning of an animal. So I see that this value is of type tiger, but that doesn't make any sense. So it's usually good to keep these tight parameters to be a very simple name that represents something. So it could be type. It could be sometimes people use action. If they're building something with actions and so forth. However, it's really easy just to use simple single letter types, because then when you're looking inside of perhaps your code and you're looking at the value of something, you can look over and say, Oh, that's gonna be of type T. Oh, okay, I don't know if that is, that's a type or I need to think about this more. So that's how you can build a very simple generic class in your very first generic class inside of Kotlin. Let's assume that you have a person and inside of your application, for whatever reason, you can't allow anyone to be under the age of 18. Perhaps it's an application for allowing people into particular restaurants or music events. So you wanna make sure that no one gets in, unless they're over 18. However, this is an exceptional case. So you wanna check to see if the person is over age 18. If they're basically under the age of 18 now, that should never happen. Perhaps these values are coming back from an API or somewhere else. And at this point you're like, you know what, if I get someone back less than the age of 18, this application just needs to basically blow up. There's something that should happen. And to do that, you can throw what's known as an exception and you use the throw keyword for that. And so we can throw an exception. And so we have an exception here in Kotlin. We provide an error message. User is not old enough. All right, now, at this point, if we run this application, what we're going to see is that this person Donnie who's 13. Little Donnie can't come in because the user is not old enough. Now the exception is built into the Kotlin standard library and all it really is the type alias for Java Lang exception. And what that means is this kind of just created a little shortcut for you, like a little link and said, hey, if you type exception, and this is a type in Kotlin what you're actually meaning is this actual Java language exception. There's a bunch of different exception types inside of Kotlin and a million more of them in various libraries. But the common ones are gonna see you as just regular exception, where you provide a message, a runtime exception and a legal argument, maybe provide an illegal argument. and a legal argument, maybe provide an illegal argument. So it got into a weird state that it doesn't know how to handle and it needs to get out. So that's an illegal state exception index out of bounds. You know, if you're working with an array or a list, an index out of bounds unsupported operation, you're doing something that's not built yet. Perhaps this could be very useful for a feature you're building out, but the function isn't done, but you need to make sure that you do something in the function so it compiles. You could just throw it on supported operation exception. And then if you accidentally hit that in testing or in code, it'll blow up. And of course there's various other exceptions inside of here, but this is how you can throw an exception inside of Kotlin. And it can be any other exception here. So if we wanted it to be a illegal argument exception, we could say the user is not old enough, user is too young, and then we could run that. And then of course the output would then show that we then got an illegal argument exception. You wanna use the correct exception for whatever you would like, for whatever you're wanting to do and you can throw that correct exception to that time. And if you're using any type of code and production, that's looking for these exceptions, you can start filtering on the types of exceptions that were thrown, and that's how you throw an exception and Kotlin. In Kotlin, and in many other JVM languages and most programming languages, you're not limited to the default set of exceptions. You can actually create your own set that you would need as well. So let's assume that you didn't want to use the illegal argument exception, but you wanted to create your own. So you can create a class and we'll call it illegal age, or it's called invalidate age exception. And usually you'll add the suffix exception to the end of an exception. So, you know what that type means. If I just typed in valid age, I don't know if this is an exception, if it's an object or what is it for, but if I say exception at the end, it's very easy to read for what it is. Now, this is a class I can provide anything I like. So maybe I wanna provide the age, which is an int. And then I'll also wanna provide the message, which is a string. And then I'm gonna go ahead and pars, make this. I can either make this just an exception, or you know what, since this has has something to do with an argument, I'm gonna extend illegal argument exception and then I need to parse in either no parameters or the message. And I'm gonna go ahead and create a message that's going to be based upon the other stuff. And I'll say in a valid age and then I'll go ahead and render the age variable. And then I'm gonna go ahead and then also append the message here. So now instead of using an illegal argument exception, I can say invalid age exception, throw new invalid age exception. And of course it can give me an error here because I don't have the age parsed in. So I'm gonna say P.age, which is the person's age. And now if I run this and what we're gonna see in the output is that an illegal age exception is thrown. So illegal age exception and valid age, 13. User is not old enough, user is too young. And so I'm parsing this message in, and this is which means invalid age exception is basically a child of illegal argument exception. And so now I've created my own exception. You can create as many exceptions as you would like. These can help you inside of your application when things are perhaps not working and your applications is crashing, you can see exactly what type of exceptions they are. Perhaps a particular part of your application is really difficult. So you may wanna create a couple of different exceptions and you track those inside of your application or inside of your crash tool, or maybe you're building a library and that library has very particular needs. And so you need to make sure that you returning certain exceptions from your application and giving exceptional situations. So that's how you can do it. You just have to go ahead and take class and extend exception or you can just even extend just regular exception and so forth, and then you can start building your own. So it depends on which one you want to extend. You need to make sure that you can extend it. So there is an exception, you can also extend runtime exception as well. So if you wanna make your own runtime exceptions, you can do that as well. So that's how you can create your own exceptions in Kotlin. Okay, let's assume we have some code here. We have a person object, and this side, this person object has a user with the name Donnie and age of 13 and a method called check age. And if the age is less than 18, it throws an exception. And maybe this, let's assume that this is in a different library somewhere, so we can't even control it. So this is beyond the scope of our things. We have to call into this library to do something. And for whatever reason, this library throws an exception, even though we may or may not agree with that on the way that the code is working level, sometimes that's just the reality of the situation. I've worked with many libraries in which I can't control or don't agree with how they handle something and they throw an exception. I don't want the application to blow up. So what I can do is I can wrap this in what's known as a try-catch. And try-catch is going to give us a ability to catch the exception. So basically what's gonna happen I wanna show you something. what's gonna happen is check age is gonna happen. And then that's gonna throw an exception so we can see the exception again. It's gonna throw this exception, then what's gonna happen is basically gonna short circuit right here. I could have a whole bunch code. Most like I probably will have a whole bunch of code down here and none of this code is gonna execute. Why, because this thing right here is doing an exception and I need to do something with that exception. And so here I can actually catch that exception and I can actually get rid of that if I want to. Catch the exception and then I can do something with it. Now I can decide to do print ln, caught the exception, et cetera. Now, when I run this, now the application is not going to crash, I got a typo there. And what we see is caught the exception. Again, we'll fix the type, we'll run it again. We see caught the exception, but we still didn't get all these values run. Now this is beneficial because if we don't have this, let's go ahead and comment this out. What can end up happening is our application could run. And then for whatever reason, it just blows up. And maybe we don't want it to blow up. Again, as a library or something, we get an exception and we wanna do something different. So we were like, oh wow, okay, this person's age is not 13. Okay, maybe then we kind of do some other type of code handling, set some variables and do anything like that. So that's how we can catch an exception inside of Kotlin. Now inside of here, if this were, if we're gonna do print line, exception was caught or say caught for now. If for some reason this person was 23. What we're gonna see now is the code is going to run and check age is gonna run, that's fine. And then all of these statements are gonna run. And this block of code right here is gonna be skipped. It's completely skipped. So what ends up happening is Kotlin says, all right, I'm gonna try to do all the stuff in here. And then if anything bad happens, I'm gonna go ahead and basically short circuit down into this little branch down here. Now that's great. If no errors happen, then all the code in here will successfully execute and then it'll continue down here. So we'll say for an ln after try catch, and this is known as a try-catch block. Up here, so we have a try catch. And so we'll say you done, done, done, done, done, done after try catch. And so that's how you can work and catch exceptions. You can also catch very particular exceptions. So maybe if we can see if this is an illegal argument exception, and what we need to do is change this again to something that will fail. So we'll set the age to 13, so it throws throw. And then we'll notice here is it all of a sudden our application is bombing, but we still have a try-catch here. What happened? Well, the reason is because what we're at throwing down here is an exception, but what we're checking for is a much more granular level of acception. Here, we're checking for illegal argument exception. We're checking, hey, the exception, we only wanna catch this if it's an illegal argument exception, otherwise just let it blow up. So this is very useful if we're trying to make sure that inside, maybe we have that custom exception. Again, we have class invalid name or valid age exception. And it's gonna parse in a message. And what it is, is it's an illegal argument exception, and we parse in the message there. So instead of throwing this regular exception, we're gonna throw it this one here, and we can see what happens here. And so, oh, looks like it caught the illegal argument exception. Well, why did it catch your legal argument exception if it's an invalid age exception, because invalid age exception is an illegal argument exception. Now, if we were to change this to invalid, let's say invalid class exceptions, which is just a different exception from the Java.io. It's not gonna work because it's not gonna catch it. It's saying invalid age exception. So a lot of times you may want to catch multiple exceptions at once, so you need to climb up the hierarchy of classes here. So the exception is some classes or form of code, throwable, excuse me, that indicates, et cetera, the things are throwable. So what you can do is just rely on just the default exception. So we can just delete this cause it'll use Kotlin's type alias. And we can see here that the exceptions kind of all root up towards the top. And if we use just a high level exception like this, then what we can do is actually provide a when statement. When EX, I can say is illegal argument exception, we can say print ln legal argument. When is, we might wanna say something like invalid age, then we wanna print something, print ln, invalid age, and then anything else we just wanna say, print ln else, something else. Now, if we run this, let me see invalid age. So if I were to remove this, let me say index out of bounds exceptions. So index out of bounds exception or using an abbreviation. And what we see is invalid age is still being thrown. So I'm gonna change this to just throw something else, throw in legal argument, exception inside of the check age method. And then we're gonna see that caught something else. So something else was caught down here. Now, a lot of times, what you wanna do is you wanna go ahead and re throw that exception because you are not sure what's happening here. So you wanna just say, hey, you know what, here is the example. We have the exceptions come in. And when the exception is an illegal age exception, do this. When it's an index out of bounds exception, do something else. Otherwise I don't know what to do with it, just throw this. Maybe someone further up the stack will catch it, but these are the only two exceptions I know how to handle inside of this try-catch block. 'Cause maybe this will throw an illegal age exception and maybe some other call down here that we haven't specified yet will issue an index out of bounds exception. And then at that point you can then start catching and handling multiple exceptions inside of your Kotlin program or rethrow it with the stack trace accordingly, and then you'll get the whole stack trace all the way outside the call stack. So if it's further up, you'll get the call stack as well. And that's how you can catch multiple different exceptions in Kotlin. Let's assume that you have some code here and you want to surround this with a try so you can make sure that it doesn't fail. You can also, instead of using a catch, you can use a finally here and you can say for ln finally. And perhaps let's have something that was supposed to execute after this. So it would say print ln after check age. So we should not see check age execute because if the user's age or the person's age is less than 18, then we're gonna throw an illegal argument exception. But this is the show you that in a try catch or a try finally, the finally block will always execute. So even if an exception occurs inside of this tribe lock, if an exception occurs anywhere in here, the finally block will always execute. So let's go ahead and execute that now, let's run that. And what you're gonna see is that finally, which is right here, finally is printed, but the exception is still happening, which is inside of check age. And then basically the program execution is stopping. How do we know that the program execution is stopping? It's pretty simple, the print line which is called after a try is not executing at all. So what ends up happening is this block is executing and now we're seeing the finely run here and finally will be run regardless what happens inside of try it. Now, this is very useful, let's say if you've opened a variable and it's perhaps reading a file stream or maybe an audio store, or audio or something like that, that's gonna take a lot of resources that you need to close to make sure there's no memory leak. Then maybe you would say, you would have some type of variable called file stream. And then inside of here, you might say, hey, regardless of what happens, if I'm getting an exception or I don't, I wanna close down that stream so I can kind of clean up after myself and not have memory leaks. So it's a very common place to clean up for anything that you need that's resource intensive or could possibly provide any type of memory leaks. You can put it inside of the finally area there. So if anything were to happen. Now, you can also combine a try finally with a try catch. So I could say catch exception with exception, and of course we can do this, so we can stack them. And this works, this is a very common pattern here. So again, you might wanna use finally to clean up after something that you've, again, maybe opened an audio source, video source, something that could leak memory, but you might wanna do something inside of this exception and say, print ln handled, or maybe you're gonna handle it a certain way. So instead of let's, it's going to catch the illegal argument exception. So let's go ahead and do illegal... Let's do a legal state exception. And if we run this, what we're gonna see is we're still going to get the finally that's called, but we had the illegal argument exception was thrown. So what ends up happening is check age called, the exception was thrown immediately. This finally block realized an exception was thrown. So finally it's printed and then finally the program says, hey, here's why we executed with this exception. Now, if we were to change this catch block to catch that illegal argument exception, the whole program execution changes. So we're gonna see that it's gonna execute this, but it's gonna throw an exception. So we're not going to see this line of code. Remember an illegal argument exception was called or thrown. The catch block sees that says, yes, I can handle that. It then prints out handled, which is printed out handled here. And then as always, the finally block will always be run. So we may need to clean up after some memory intensive stuff we'll then go ahead and execute the code inside of finally. And then after that, the program execution continues because the program didn't really crash, we handled it right here. And at that point it's executing after the try. So you can perform a try and a catch. And a finally you can stack them all on top of each other in Kotlin and that's how you do it. The difference between try, catch and try finally can be illustrated in a very simple example. Here, we have a try block inside the check age method. We check the variable for the class person. And if they are under the age of 18, we throw in the legal argument exception with the value of boom. And if we run this, what we're going to see is a few things happen. We see the word finally is printed to the output and then we see the reason why the application exited, which is the illegal argument exception which was thrown. So this is interesting because the print line was not executed, which we expect because it was short-circuited based upon the illegal argument exception was thrown. Every time this code is called, finally is guaranteed to be called. So finally it's going to be called and we print finally to the screen. And of course, because the application exited, we are not going to see after the try here. Now, how does this differ from a catch? Well, a catch allows you to catch and do something with it. Finally just executes at all point in time. So let's just go ahead and replace this, comment this out right now. And let's go ahead and actually, I'm gonna go down here and we'll comment this out. And now we'll say catch EX exception. So catch allows us to catch EX exception. And we can print line the exception message, actually we're gonna print caught just to be more clear. And once we execute this, we'll see the word caught and we are then after the try it. So what this allows us to do is this whole program has now executed though, except for this line of code here, because it was short-circuited via the check age because of the illegal argument exception that was thrown. So the catch, you can actually catch either a particular exception, or you can say if it's not a particular exception you can... Or if it is a particular exception, it can also not get short-circuited and still blow up the application. However, anytime using a finally, which it also can be combined, a finally is executed all the time. So regardless if we have an exception that is caught, we have finally this exception was not caught. So it terminated the program, finally was printed. However, if this is an illegal argument exception, we're going to see that the program execution continues on. We didn't crash the application. However, finally was still called. So regardless of what happens inside of the try, finally is always called while it's not guaranteed to always be called with the catch, because we could be filtering it based upon the type of the exception. Defining a type alias can be very useful in various different scenarios. It allows you to alias one particular type to another name type. So let's assume we have a user class here and has a username and perhaps an auth token, and perhaps the account class also has an auth token that is needed to request the account details. And for whatever reason, there is a order class, and it also requires an auth token as well. And it's a string. Now, eventually you start finding yourself, having the word auth token all over your entire application and realize that that could be a problem. Well, one thing you can do is actually type alias auth token into its own type, or maybe you can create your own type. So you could do that too. You could say something like this data class auth token. And what it is that just contains a token that the string. And then you could start replacing this with auth token, but then inside of any of your providers or anything else that's inside of your system, that's you wanna treat it like a string, well now you have to treat it like an object and it's kind of a pain, but you would just love to be able to call it auth token, because it would just make much more sense when you're reasoning about your application. So I'm gonna go ahead and put this back as string. So I wanna go and get rid of that. What you can do is actually perform a type alias and here's how you can do that. What you can do is usually I prefer to type these at the top of a file or in some type of common extensions area inside of my application. So I might have a file called extensions.kt. And I'll say type alias, and then it says, I wanna type alias the word account token, excuse me, auth token to equal string. And what that means now is I can use the word auth token anywhere. So this is also a string, this is also an auth token. But now it's much easier for me to reason about because now if I'm going to use my user class, user.auth token, I could see that it's an auth token type. And then it actually gives me a hint here saying, hey, that is actually a string behind the scenes. And so I can say auth token. And then again, I still have all the same operations that I would have on a string like length and so forth. But now it allows me to have much more meaning directly inside of my application. So if I'm creating a new order, a new order is going to take an auth token. So instead of it taking a string, it's an auth token. And so I could do one thing. I could actually provide the auth token and say user.auth token, that'll work, or if I just have a string, because auth tokens are string, I'll say, my auth token. And this will also work too because underneath the hood, Kotlin knows that auth token is a string. However, just for usability and type systems inside of your application, it makes it a lot easier sometimes with various different values to use type aliasing so you can actually get the actual types that you like without actually having to create a brand new one, because maybe that auth token is just a string, but you would just like to have its own type and you can do that with type aliases. And it's very easy to do just set type alias, what you wanna call the type alias equals, whatever the type is. One of the coolest things about Kotlin is the ability to add functions to existing classes already inside of the standard library or other existing libraries. Now, for example, what I mean by that is it would be great if a string class, which is what this name is. If we were to specified it's string, but it's already through implicit nature, we already know it's a string. It'd be great, if the string class had an initials method on it. And that would allow me to automatically just say name.initials. And for that name, it would automatically print the initials, so it would print DF. Well, we can do that in Kotlin and here's how we can do it. All right, so I've saved the time of actually typing the code for you and I'll walk you through it here. Now, what I've done is created a new function on this line here, and this function itself, we start off by saying the type that we want to extend, here is string and then we wanna say, here is the actual name of the method that we wanna create. And then of course, it's going to go ahead and it's going to return a string, which is we see here, returning string. And then at that point in time, we're gonna go ahead and go through all of this here. So here we have the values, we're just going to split it on an empty string. And then we're gonna grab the first initial using the sub string. I'll grab the first character off the first part of the array of the first item in the array. And then we're gonna grab the first character off the second item in the array. And then at that point, we're just gonna use some string interpolation and return them together. So now if I run this, what we're gonna see is we're gonna see DF is printed. So now any string that I have, so if I say val equals, I could even have the something like book that I'm reading, which is called, I read this book called "The daily stoic", say daily stoic, I could say print ln book dot initials. And what we would get back is DS. So of course, DS is printed before the other name and then printed. So if we move this up here would make a little more sense. So now we see DF and then DS is printed. So anything that has a string in it is going to be easily created. Now this is the string is not a class that we can control. This is not, if we look at string, this is part of the Kotlin standard library. That's part of Java, we can't do anything about that, but we're actually able to kind of slap on some additional methods for it using extension methods. So now what I usually prefer to do in this case, is I like to have a file and I'll call this extensions. And that's where I like to put my files, my extensions. And if I have a lot of them, I'll call string extensions. I'll have integer extensions, but in this case, I'll start off very simply with one file called extension that doesn't need to be inside of a package. It can be a top level function as we're seeing here. Now, at this point, I can call this from anywhere that I would like. So back of my main file, initials still works. So anywhere in my application now, I'm gonna be able to use this dot initials. Now the same thing can happen over here in extensions. Let's say I wanted to extend the integer class is adult, and this could return a Boolean value and all this is going to do... And we can actually single line this. And we'll say this now notice how he said this, because this is in regards to what type we are extending. So the extension function for string, we said this.split. Here we had this is for the integer. So this greater than or equal to 18. So is adult. So I can single line this one, this one's easy. Again, this could also be if I wanted to do it the other way, which you may be more familiar with seeing to make more sense. We can do it this way here. It says convert to expression body. So there we go as adult and I can even probably get rid of that. And so I can now really make this succinct. At this point, we are looking at, I don't know, 30 characters total from start to finish as adult. What this allows us to do is say, val age equals 35, and I say, print line, age.is adult. And you see how the code completion found them automatically. And we see that true came back. So now I was able to extend the integer class. I've extended the string class and then furthermore, I could also do this with, let's say another model. So I'm gonna create another file here. Call this one models. Inside the models form I'm gonna have a data class. Data class. Actually let's do a regular class. The class person is gonna have a val name string, a val age, and that's gonna be int. And then what I can do is I might have my person and I'll say, hey, today, I'm gonna be 89 years old. So Donn person age 89. Now the cool thing about this is if I had my full name in here, I could do something like this. I say person dot... Well from here, I could say, initials, person.name.initials. Now the problem that we're going to get here is that the name Donn, if we run this here, we're gonna get an exception because the initials is splitting on a string on a space. As we see here, this is what's happening here. Splitting on a space. And so there's actually only one item in the array. Therefore, it's going to be blowing up. So that's not gonna work right now. But if for some reason I had my name listed as Donn Felker, like that, I could then issue that and I would've then get back my correct initials. I would see DF again printed. So let's now go ahead and change this person class though. And let's kind of show how we can extend. Let's pretend that this class had a first name and then had a last name. This is a very common example that you're gonna run into. This person could be a user, it could be anything else. And it's usually going to be a class that you do not control. So we'll say Felker, we've made it my last name and then we'll see again, and I've got a little bit older since the last time we spoke, I'm 90 now. And so a lot of times, if I would want to print someone's name, I'm gonna say, of course we're gonna do string interpolation, cause it makes more sense, I'll say person.FirstName, and then I'll do a space and then I'll do person.lastName. And that works great. The problem is I find myself repeating myself over and over and over my application and I'm doing this all over the place. But I can't come in and modify the person class because it was in a library somewhere. So what I could do is I could be in my extension class, I'm in here, and I say fun person. I remember that person and I can say full name, full name. And I'm gonna go ahead and just inline this one as well. And all this is gonna be as this.firstName and I'm actually going to and string interpolate this. So we'll do the first name space, this dot last name, close that out. And now, instead of doing this, I have extended another person. So I'm gonna say person.fullName. I don't even need the quotes there actually. I can just get rid of all that. Now it's person.fullName. And if we run this, we'll see the same thing Donn Felker comes here. So if we say, you know, John Smith first and last name, John Smith. So we've actually, again, we're assuming that this person classes in some other library somewhere, we don't have control over it, but we do wanna use it. And we wanna make it easier to use. We can create an extension function called full name. We can create any type of other function that we wanna throw on to those types such as we're building on top of the built-in primitive types that are inside of the type system, such as string and int or even custom types that maybe we have control over or we don't have control over. And we would like to provide extension functions to them. A lot of times you might find an extension function, very useful in a particular domain of your application, maybe in a couple of modules. And you might put that extension function over there, but that's how you can create extension functions in Kotlin. Let's say that you have a string in Kotlin and you wanna print it out. But what happens if this string was from an expensive operation, what would you do then? You perhaps might wanna use a lazy keyword inside of Kotlin. And so this allows lazy evaluation inside of Kotlin. So what this will do is this doesn't look any different than what we had before. Now, if I run this, what we're gonna see here is that we have Donn's printed. Now however, something interesting happens here, print line inside of here, and I'm gonna say computed. And then what I'm gonna do is I'm gonna print this twice. And so what we're gonna see is two different things. We should see Donn printed twice, but we should also see computed, but we're only going to see computed as printed once yet the second Donn is here as well. So I printed both the names as done here, but we only see computed showing up once. And the reason why is because the first time the call to this name has been called, Kotlin will then go ahead and evaluate and execute the code that's inside of here. And this could be a very long running operation. And then at that point Kotlin will remember the computed value and then we'll return it each subsequent time that it's called. So we'll not actually execute it again. So if I run this, we'll see computed and then Don will be printed three times. So a good way to emulate this is actually, if we just did a sleep function. And we'll do sleep, I'm gonna sleep for three seconds and sleep is just part of the Java library. So three, not 3000, there we go. This is simulating a long running operation. Maybe we had to go off into a background session or we had to do some type of really complicated number crunching and to return this value. And this value here is as Don. So the first time it's gonna happen, we're gonna see print computed, and then it's gonna wait 3000 milliseconds or three seconds. And then we're gonna see Donn printed most likely three times really fast. So let's run this here down here in the output window, you're gonna see computed it's waiting and then boom, all three of them were computed. So what ended up happening was the first time this executed, all this was being executed on this first line. So the first time this code was executed, it was executed right here. Now each subsequent call, which happens, which ended up happening here and here, it got the remembered value. So as soon as it came out, this came out, said, all right, well, the new calculated value is X. And then what ends up happening is each time the subsequent calls are made that calculated value is remembered and it's not executed anymore. So on the first execution when you're using by lazy, it'll execute this code and then drop it into here. So that's how you can use by lazy to remember computed values so things are a little bit faster and so you can have a little bit more performance. And if you want the value remembered inside of your application in the future. In Kotlin, you can have lazy property valuation, and you can also just have a lazy block in which you can state some type of expensive operation needs to be lazy evaluated. And all you have to really do is provide some type of block in here. And you can either write your code inside of here, and you can put your code here, or maybe you need to call an actual method as I'm doing here to perform some expensive operation. And so what I have here is I have a method that returns an integer. And when it's the first time it's executed, or every time it's executed, this method is gonna call and print out this word computed. And then it's gonna sleep for one second and then it's gonna return a randomized integer or somewhat random because that's just the nature of the random class, but it's gonna randomize based upon the current time milliseconds and et cetera. So it's gonna give us a different random number each time. So what we wanna see here is what happens when we print this out. So all we're gonna do is have a lazy result, which is an integer. We're gonna print it and then we actually wanna see, there's actually a property on this and it's called is initialized. And so we're gonna run this and what we'll see is that the first one when we print the result, it says lazy value not initialized yet. And I said, is this initialized and it's false. And so we have a couple of other options on here, result.initialized or get value or just value itself. So it's print this line here, so print line result.value. And then what we're gonna see here is we're of course gonna see non initialize, it's gonna be false. And is gonna say computed and then it returned us a number here based upon the randomization. Now to verify that the lazy evaluation is working and lazily evaluating meaning, it's going to remember its future values, let's execute this a few times to see what happens. If for some reason, lazy is not doing its job, then we should see computed printed three times and we should see every time 1000 milliseconds sleeps, so about a second in between each one of these values. So if we run this, what we'll see is again, we'll see the top two values printed computed, boom. And then all three of these came out almost the exact same time, very quickly. And what that means is the first time that this value was computed inside of this lazy evaluation, Kotlin then remembered it. So for each subsequent time, we received the same value. And then of course, because we weren't sleeping, the milliseconds would have been different here, which means we would got a most likely a different number in one of these values. So we can tell here with a high level of certainty that Kotlin is returning basically a cached value of that which been cached for this lazy operation. So if we know that we want that same value over and over, and we don't wanna call a particular method or function or class, because it's very expensive, more than once we can wrap it in a lazy block and it will be only executed the first time. And then at that point, all future invocations of that operation will then return the cached value, which is cache via the Kotlin lazy operator, which we can see here creates a new lazy instance of the specified initialization function. So that's how you can use the lazy block in Kotlin to help speed up your application and be a little more performant. When creating files instead of your project, you want to think about organization and where to put these files. Files are usually organized into various different packages inside of your project and the project explorer on the left-hand side. And the SRC directory, you can create a new package by going to new package, and then you can enter a package. So one common one that you see a lot used, instead of example projects is com.example. As we create com.example, we see we have kind of what looks like a new folder with a dot in it, and that signifies it as a package. And let's say we wanted to create another package inside of the example. We could say, we wanna create another one. We could say com.example and we see here that the directory already exists. And why does it say directory? We'll get to that in a second. Let's say we wanna create a models package, we wanna create a models package. And so we said com.example.models. And so it's kind of been pre pended here. Now, as we see these dots and it kind of looks like a backwards domain name and that's kind of what it is. Now the interesting part about how these files are stored in the disc is if we look at them inside of our file explorer and I'm on a Mac, so I'm using finder, you'll see, we have models, an example com. And if we go backwards, we basically gonna come up here to our Castro column, our SRC folder, which kind of maps to this folder over here, we see our IO our com and see how we have io.Caster package over here. So com example models, each one of the items that are separated between the separators, which are dots, periods is actually a new folder. So let's say for whatever reason, I wanted to create a file inside of the models project. What I would do is they file new and inside of here, I might wanna create a person, it's called a customer class. And it's customer, you notice how it automatically adds the value package at the top. And this is the package declaration that lets you know what package is file is in. So here, this is inside of the package com.example.models. And I can have my class and maybe it would have a regular name and a string for a customer. And those are all kinds of common things we might have here. And then we might have another customer or we might have another value inside of there. And now let's go, we're over here to our other file here. Let's say we have something else. And let's say we have in cast or we have something known as a video file and that's a video class, we'll say class video and it has a URL. That's a string that's associated with that URL. And then inside of there, we have some other things. But maybe it also has some customers that have watched it. So we might have a function, say something like fun. And it would be customers who viewed this and perhaps this goes to a database. And this database is gonna go return a list of customers of customers. Now we're gonna see is we have a list of customers and we see right here IntelliJ has found based upon us typing and code completion that a customer class have been found. Now, if I hit tab, which I'm going to do, automatically, what happens is something is imported. This import is basically saying, look, we wanna use a customer from a different package. And we're gonna use this from the com.example.model's package. Now of course I could just hear, I could just hit return just to get the compiler to be happy. I could return them to the list. So we're returning a customer. So what we are using it inside of this video class, on the side, we're using something from different package over here. Now we could also have additional packages. For example, I wanna show you another trick and go over here. And let's say, for example, in models, we happened to come to the file system and type in here and we'll say util. Now, if we go back over here, look what happens. All of a sudden IntelliJ recognize we have another folder in there, hey, there's actually some other stuff in here. And a lot of times people will actually create things in here that are perhaps view, we call it model extension or something like that. Will create some extensions files in here for anything that's going to be like an extension function or anything like that, create extensions. And this where they'll path all of their extensions, et cetera, or so forth and it looks like it was misnamed there. So we'll say extensions.kt not a big deal. So we have multiple different packages inside of here. And so you can have multiple packages inside of your projects. So, here we have a couple of them. You may import a different package from another library altogether. So if we're inside of our models here and we wanted to import something else from a different project, as long as we have imported inside of whatever build tool we're using, we can go ahead and reference that just as we had referenced to inside of our video file over here. Now, if for whatever reason you don't, you would just create a file and we don't put it inside of a package. So let's call this and say, I'm gonna call it a favorite. We'll call it a favorite class. And if you're gonna favorite something, say class favorite, and maybe it has an IDE that you did for whatever reason. This class is in this right here, it's in the SRC directory. It does not have at all, it does not have a package. So the default package, it has no name. So at this point, it's part of the default package of the application and the default package of the application is nothing, there's no name for it. So here, you're just kind of sitting at the top level of your application. This is not recommended to put files here at the top level here. It's always good to have a place to put them inside of your application. So where should you put them, maybe you don't have a good idea. A lot of times if you're just working with a sample app and you want someplace to put them, I recommend just using com.example or org.example. And that puts them in kind of like an example namespace, just so it gives you a little bit a level of organization, or maybe if you have your own website, like I do, what I will do is I'll put them in my own package. And so you can see, I have a Caster one there, but I also have a Donn Felker one. So I'll say Donn Felker, which is my website. And I might say something like models. If it's something has to do with models, it has to do with IO, it might have to do with input output, if it has to do with, I'm building some calculation stuff or math or anything like that, I might have it inside of there. Or if I'm gonna build some services, I have com.Donn Felker.services. And now I have some packages in here. And if you notice com now has realized, hey, well, we have a couple of packages inside of here that also are from the same root TLD, which is the com, let me create a file here. And inside of here, we'll have something like a customer service, maybe it does something special for customers, or let's just say it's a tweet service for whatever reason I'm connecting to Twitter. So there, we got a tweet service. And so this is just to illustrate that you can have multiple different packages inside of here and the tweet services inside of com.Donn Felker services. Example over here we have the models, we have models in here and packages give you a way to organize your code into logical groupings. So a lot of times you'll see a lot of code that's related, maybe it's with file L IO together, database code will be together. You'll see services together and various different projects will have a different way. So it depends on the project you're working on. Some will be kind of grouped together by function, some will be grouped together by feature, kind of depends on what it is. But packages allow you to group your various different pieces of your application and your classes into different locations. And again, if it's not part of a particular package and it's at the root level, perhaps just right here in the source directory, it's part of the default package, which has no name at all. And usually this isn't recommended when you're developing a real production application. And that's how you work with packages inside of Kotlin. Kotlin has type inference. Let's talk about exactly what that means. Now at a real high level, what that really means is that Kotlin can infer a particular type of variable. Now Kotlin is a strongly typed language. That means that each variable has a particular type. The competitor's gonna check those types and we have a lot of competitor things that the competitor does that makes our life a lot easier. And one of those things is type checking. However, in traditional languages, such as Java, you have to inform the compiler, here's the type that I'm going to use. And then you can start using the variable. Kotlin has type inference. So here we have a variable or value by the name of name. And it contains the value of Don and we have another value here, which contains the value of age. And this could also be a var, it doesn't matter if it's a val or a var, Kotlin one for both. And the old we can see here just through the helps inside of IntelliJ is that Kotlin has already inferred that the name variable here is a string. And the reason how it's doing that is it's looking on the right hand side of the equal sign and saying, well, you have omitted declare a variable and you want me to call it a name? Okay, and then you wanna assign the value Don and I already know the value Donn is a string. And someone gonna put a string inside of the name. So that means that most likely that name is a string. So I'm gonna infer the name of the string. And the same thing goes for over here, you will want the value 33 and 33 is integer, and you wanna shove it into the name, excuse me, into the age variable. Well, I'm gonna assume that that age variable is actually gonna be an integer. And as we can see here from the help that compiler is actually getting this right. And this does a few things, it allows us to not have to declare the variable. Now we could, of course, just say, hey, this is gonna be a string, but we're actually kind of just writing a bunch of code that we don't really need to write. So we don't really don't have to do this. Now that's really nice thing about us instead of Kotlin, with variables, we can infer them automatically. Now there is a caveat here. If you remember late initializations who have a var, and we could say maybe your favorite food. You could say something like this. Well, I'm going to do this later and again, late init means, hey, Kotlin, don't worry about this yet. I'm going to go ahead and populate the food variable. I'm just not gonna do it yet, I'm gonna do it somewhere later down the line, but don't worry I got this covered. I'll handle it. So that's what we're telling Kotlin at this point in time. However, Kotlin says, well, I can infer the type of the variable if you give it to me during the initialization, which is what we're doing here, we're allowing the initialization of the variable to occur and therefore type inference can take place. So names like, okay, that's gonna be a strain 'cause you're basically setting it equal to a string. In age is gonna be equivalent to an integer because well, you're setting it equal to 33. But late in it food, I don't know that's gonna be yet because somewhere further down the line here in our application, who knows if it's inside of an if statement, inside of a catch block, who knows where it's at. But you're gonna set it somewhere and I don't know what it is yet, so you have to tell me. So in this instance, I actually have to define the type for the compiler to be happy. So I cannot infer a type with a late initialized variable. Well, this also works too if you have... For example, let's say we wanna use a name that's reversed. And we have a method which we have down here at the bottom. And this method right here, we'll just take in a string and we reverse it. Now, of course we could do that by hand, but this is just to illustrate the concept that you can have a function that also return something. And we'll take that, we'll take in the name. And what will happen as Kotlin will then go ahead and take a look at this function here and say, all right, well, you wanna use name reverse. Okay, well I'm gonna go look at this function name, reversed and then I'm gonna see, oh, a name reversed actually turns a string, oh, okay. So in that case, I'm just gonna go ahead and make reversed a string. So again, kind of as real quick, just kind of Kotlin says, I'm gonna go look at this function here. I see it as a string and then, okay, cool. It looks like I'm gonna go ahead and make this reverse value a string. So pretty simple. And what is doing behind the scenes, so you can do that. Kotlin will also do some inferring too, So, let's say we have two prices, so price equals 10. Now this is gonna be an integer and I've priced two. And whoops, so this might be price two and this could be val as well. So 20.01. And then I say, all right, print line price one. I'd say print line price two. If we look at what the competitor is telling us here is it, well, price is an integer and price two is a double. And then what happens if we initialize another one? And we say, this is a total number. The total is gonna be the price plus price two. Now what's gonna happen here. We're combining two different types. Well, this is a double and this is an integer. Well, Kotlin is like, well, what am I gonna do here? Well, Kotlin is smart enough to know the compiler is well, you're getting an integer and a double. So most likely you want the precision of this to be a double because otherwise you would lose some precision if we just stuck with an integer, meaning that we would end up with, you know, what would this be, it would be 30 most likely. So at this point in time, total is gonna end up being a double, which we can see here from the IDE help. So Kotlin is then doing type inference based upon a calculation of an integer and a double. Now in other languages, you might have to do particular types of casting to make sure that you don't lose any precision, because if you didn't do the casting, you might have precision problems, meaning you're losing decimals, et cetera. But Kotlin is smart enough to figure some of these things out here. So this is basically an essence, what type of inference is. And so you can get inference when you are initializing variables and then different types of returned, et cetera, are going to be inferred, such as the function name is here. And Kotlin will both go basically look throughout the execution path as a code set, all right, what is being returned here? Okay, it's being set into this other value up here. Okay, that sounds good. So it looks like reversed is that point is now going to be a string and we can go ahead and infer that for you. For some reason, though, if we are using again, a late init and we're gonna go ahead and use a var and have our favorite food, we will have to actually declare the type when we declare it because Kotlin is not smart enough to know, hey, I don't know what this is gonna be. And because it's a strongly typed language, we can't just kind of let this type just kind of be floating around and nowhere and we don't know what it is. So you do have to declare the type here, if it's gonna be late initialized. And that's how type inference works in Kotlin. Okay, let's talk about lambda functions. Now, lambda in Kotlin is defined as the following. And these can look very confusing the first few times you see them. So if we're gonna build our own lambda function so you can understand what they are. And basically functions are functions that are, I'm not gonna get really mathematical about you and high order functions and everything like that, though I really advise you to go read the documentation. Lambda functions are basically little functions that you can create and start parsing around inside of your application and allows you to be very functional in nature. So enough of that let's get into implementation. So a lambda is defined as having a name of a lambda, and then you have its input types, which are gonna be the parameters that are parsed in, and then it's gonna have a return value. And then inside of that, you're gonna actually have some arguments that you get to provide the names for, and you have to provide their types again, that's gonna be the input type over here, so we can go and change this to input types so it makes more sense and put type. And then you're gonna have the body of your actual Lambda function which does its work. Now, a lot of type inference will happen with this, which we can get into in a second, but let's go and create a first one. And the first one we're gonna call is the end result is what we wanna be able to do is say greeter. We wanna be able to do this, and I wanna be able to parse in my name. And what I want to happen is I want hello Don to come out somehow. So how are we gonna do that? So this is a very rudimentary example of what you could normally write a regular function for, but this illustrates the lambda functions. And so what we would say is it's a val, it's a greeter and we do colon and then we need to provide the input types. So remember, let's look at our example again, we have greeter and inside of the greeter function, we wanna be able to parse in the word Don. And so here, what we need to do is say, hey, we need to parse in an input type. That input type is gonna be a string. And then what do we expect the return type of greeter to be, remember what we wanted it to be Donn Felker is what we want it to come out of it. So we expect a return type to be a string. Now we need to start defining what our lambda expression looks like, a lambda function. And so in here, we actually are gonna have the parameters. So our first parameter here, which is parsing is Donn. That's a string. Let's go ahead and give it a name so we can work with it. So let's just call it name. And of course we know it's gonna be a string. So again, we have to provide it right here. That's the one of the arguments and provide its input type. And now we actually provide the code body. And so now this is the actual body of code in which we can do some stuff. So here, what we can say is, hello and then we can use string interpolation and we'll say name. And what ends up happening is the last value that's inside of this lambda expression of this block of code is the return type. So whatever this type is, is going to be the return type. So if this was multiple lines, which it could be what we can put these on different lines and we can have multiple lines, whatever is the last one here I could say, val foo equals zero, it doesn't really do anything. And then we get this nice little lambda notation here. It says, hey, this is the return value of our lambda. So whatever this type is gonna be the return value. So it's a string. So if I said, hey, I want the length of the string, we're gonna get a compiler error because it's gonna say we are requiring a string. Again, this is the return type of the lambda, but you're actually giving us an integer because this is the last line on here. We're not seeing return anywhere, but by default, the last value is the value returned. So since we can single line this, we'll do that now, it's pretty easy. And we'll get everything back on one line. And then what we can do is we can go ahead and execute greeter and we'll go ahead and parse name Donn. And so it looks like a regular function. And we can see here when we did that, we created the new greeter here. We could see that it's going to take in a string. Let's go ahead and run it. And then what we're gonna see is that nothing happens down here in the output window. We're expecting something to show up here, but nothing did. And the reason why is because greeter is a function that takes a string and return to string, it doesn't do anything with it. So we actually have to actually do something with it. So let's go and print that return value, which we could just do this too just so you're aware. We could actually say greeting equals greeter like that. And then we could say print line greeting. And then when we run this, what we're gonna see is the output finally at that function since say, hello Donn. So again, to kind of cover this here, we have the name of the lambda, which is coming right here. The name of the lambda, we'll do a little simple line here. So it's the name of the lambda then we have the input type. Then we have the return type and then we're having the arguments and their corresponding input, the corresponding type was a string. And then this little section over here is the code body, which comes right here, that's the code body. So that's how all that's mapping right here. So now let's take this a little bit further though. Let's say for some reason we wanted to be able to say, like we said, we have Donn Felker here, but instead of saying, hello, Donn. So we got that wrong. So there was a bug in our code. How are we gonna fix this?. This actually should say, hello, Donn Felker. Okay, well, that's easy enough. We could just say hello, you know, we just put Donn Felker in there and that would work. But what if we wanted to bill accept two different names because in our database, everything is separated out. So we need to have this greeter and it's built to two values. So again, we need to come into our expression and say, all right, it's gonna take an input type of string and another input type of string. So it's gonna have two. Think of this, like the when you write a regular function like this or a function, my function. And then inside of this function, you have some stuff and it's gonna be name, string foo whatever it is, last name, string. Well, notice how this, the way I like to think about it is these parentheses right here. it almost like maps directly to that print. So you kind of map there that prints, they maps there and basically says, hey, look, there's a string here and there's a string here. And that's how I of visualize what these parentheses up here are. So this is gonna take in some type of string is one parameter. The next type of in that next parameter is gonna be string. And the whole lambda expression is still just gonna return a string. Now we get a problem over here. It says, hey, we're expecting two parameters of type string and string. Remember we were taking a two now, but over here, this is the list of arguments. Remember, this is where the list of arguments map in. And if there's more than one, then we need to put a comma here, just like we would traditionally in a regular function. So I'm gonna rename this as first name, 'cause I can name the same thing I want. And then I'm gonna say a last name string. And now I'm starting to run out of a little bit room here so I could shrink this down a little bit. But what I have over here, I need to change this to first name and then last name. And I'll do string interpolation again. And now we have a lambda expression that's gonna take in to values. So you see right here, it says, hey, we're waiting for the second value P two, which can be a second parameter. So I'll take a Donn Felker, there we go. And I'm gonna go ahead and run this again and what we're gonna and what we're gonna see on the greeting now is it Donn Felker's returned because we're parsing in a string here, Donn and on our string for Felker. These basically, if you take a look at how this maps, this one is mapping to this one, and this one is mapping to this one. That's how they're mapping right there. And we can see that the first strings we're calling that one first name, the second string, we're calling that last name. And then at that point was since we're inside of this lambda expression, we have access to those variables and we do whatever we want with them. Now, if for whatever reason we wanted to get kind of do some hokey pokiness stuff, some hokey stuff in here, we could, we could actually do some different things. So let's go a val modified first F name. Actually first name is spelled out. And what we could just say his first name.two upper case for whatever reason. And I'm gonna say val modified last name. We can say, last name, not owner exception. Last name, last name that two lowercase for whatever reason. And then we can go ahead and replace this with the two modified ones, modified first name, modified last name. And now if we run this, now, what we're gonna see is that we have a lambda expression with multiple lines. Hello, Donn Felker. So we've capitalized and done different things here. Okay, that's cool. So we see that the, again, the last line here is the one that's returning the type here and that's the type that's going to be basically this lambda is saying, all right, look, we have a return type of string and that's mapping to right here. So string, which is right here is mapping to this and the last line of the lambda expression, whatever its type is, it needs to match this up here. So it's go and get rid of that. So let's just say for whatever reason we wanted to say greeter needs to return an integer value and it could be the number of characters inside of this greeting. I don't know, for whatever reason. We've changed this integer, which means now that this lambda expression needs to return an integer, but down here, the last line is what's going to be returned in rest last line is returning a string. So this is not gonna work. So what we can do is we can just go over here and may just return the length of this name. If we run this, now what's gonna be returned. It's still gonna take in a first and last name and it's gonna concatenate hello on there, but then we're gonna grab the link and we're gonna print the link. That's what greeter's gonna do at this point in time. Okay, that sounds cool, but maybe that's not what we really want. So let's take this back to string. Now let's assume that we really like what it does, but we just want greeter to actually just to display the greeting, so we don't wanna have to do this. We don't wanna have to print this. We just want, as soon as the call greeter to automatically just print line hello, Donn Felker. So how can we do that? Well, this return value is what the is going to be returned from the lambda expression. So we can change it to unit. Unit is basically like void in Java, which means, hey, we're not gonna return anything, we're just gonna do something, but we're not gonna return any value. So here we're just gonna go ahead and return unit, which has nothing. And then what we can do here is we can say print line, inside of here and we can just do that. And then what's gonna happen now when we run this, what we're gonna see is we're gonna call the first name, the last name that'd be parsed in and then we're just gonna print line. There it goes, hello, Donn Felker. So we can do all different kinds of stuff. And there's gonna be multiple lines, could be hundreds of lines of, you know. Of course I don't recommend that if you wanna make your code easily read and so forth. Because Kotlin has type inference, you can actually clean this up quite a bit. We don't need a lot of this stuff in here. So what we can tell Kotlin is because Kotlin can look inside of this lambda expression and say, well, we know we have one parameter, we know we have another parameter. Okay, so this first parameter is a string. We know the second parameter is a string. Okay, we can do that, Kotlin's figuring that out for us. And then is looking at this last line down here and Kotlin saying, well, this lambda expression is not returning anything. So it's actually gonna be unit so it can infer the return type and it can infer the parameter list. So we can actually do due to type inferences, just go ahead and get rid of that. So now we have a very succinct or much more sustained lambda expression. So if we wanna do the simple one, so we wanna do it just as simple one, greet, we could've done something like this. We could have said name, string and then it gets to this print line, hello name. And there, we have our very simple little greet. So of course we have our greeter that does a bunch of different stuff, modify some variables, which we'll move up here just so is obvious. And then we have the very simple version right here, greet. If we print these, of course, we got the spelling wrong. And if we print these, what we're gonna see here is we have both of these printing out values. Now lambda expressions are not limited to one or two or three different types. You can parse in many different types into your lambda expressions. If for some reason, when you're writing your lambda expressions, and for some reason Kotlin cannot do perform type inference, you will have to provide these hints. And sometimes you'll run into weird situations where based upon something that you're doing, you're going to have to tell Kotlin, hey, by the way, here's how we're gonna go ahead and do all this stuff. It's gonna be unit or whatever and so forth. So you'll have to provide all information. And this is the blueprint up here. This is the blueprint that you just need to remember, that you're gonna have lambda name, then you're provided input type, the return type equals whatever this lambda is. And it's gonna have your arguments, which is gonna have one to many of them. You have the body of it, et cetera. And that's understanding lambda expressions. Using lambda expressions in your code can be very beneficial, especially if you decide to take lambda expressions as a function parameter. So it's assumed that you have a function and you want to make something repeat, actually it's call it line logger. And you want something to kind of log various different lines over and over and over. And so what you could do is you could take a message. There is a string. And then perhaps if you wanted some logs to very basically kind of look like this, you want a bunch of these kind of lines separating your logs just for maybe 'cause you have really chatty logs, you could do something like this. You could say repeat five times and then you parse it on little lambda expression. It's a print line and then let's go ahead and put in one, two, three, four, four, five, six, seven, eight of these lines. And let's do that twice and then what we'll do is do print line our message. Now this works pretty well. So anytime we wanna use this, we can say line logger and to say hello there. And then if we run this down in our output window, what we're gonna see down here is we're gonna see that hello there is printed out. Maybe this is useful to kind of help break up your logs for a visual aspect or whatever. But sometimes that can be not what we wanna do and there's a way we can actually make this much more in tune with being able to do anything we really want inside of here. What if we wanna provide not one message, but we wanna provide two messages. So let's go message two, two string. Now we're gonna come in here. we're gonna say print line message two. We are in this again, we get our message one, message two. It looks like we're missing message two up here. Of course, we forgot to add that to the parameter list. Hello again and when we run this this time, we're gonna see hello again. Now the problem is as we continue to grow this and our requirements increase, what we're gonna run into is a situation where we're just becoming overloaded here. And what we really wanna do is be able to have a way for us to print this kind of this block at the top and the block in the bottom, and then whatever we want right here in the middle. And so yeah, we could build our own strengths. We'll just leave it at one string. We could build our own string up here and I could do this. I can kind of maybe kind of do this like a little weird. I could do this thing up here where I do like a new line thing and I kind of got there, but then it starts getting really hacky as soon as I wanna get anything else kinda done in there. But there's a way we can solve this with lambda expressions. And so what we can do is what we're gonna do is I'm gonna call this block. And this is again, this is the name of the variable, it's called block. And then what type of it's gonna be, it's gonna be a lambda expression. It's not gonna have any parameters and it's just gonna go return a unit. So again, remember, let's go ahead and take a brief review here. We have the input type, which is right here. That's gonna be, there's no nothing input type, so it's no parameters. And then the lambda has a return type. And what does that return type, the return type is gonna be unit, so it's not gonna do anything. And again, the name of it is just called block. And the reason why we're calling it block here is because it can be a block of code. And so this is a block and so I can get rid of this. Now, what I can do is just invoke this block since this is basically just a function. I'm saying, hey, this is a function I wanna do. And so what I can do now is I can get rid of this code here and I can actually just do this. Now, of course you wondering where did the parentheses go? So I could just do exactly this. And then I need to put my code block in there. But what you're gonna see here is this little squiggly and say, hey, you can go ahead and move the lambda argument out of the parentheses. It's just a convention that is allowed inside of Kotlin. So now I can just say anything I want here. I say print line message one. I can say two, I can say three. I could do something like this, I could run this. I could even put my own loop inside of here. I could say repeat five times I could do my own lambda express, you know, 'cause that repeat thing takes on its own land expression. Repeat as part of the Kotlin standard library, by the way. And if we run that again, what we'll see now is we'll see message one, two, three, four, five, repeated right here in the middle, all separated by our top and our bottom parts of our line. So what this has allowed us to do is create a much more extendable function. So we now have a function called line logger that I can pars in whatever I want. I can do 20 different logs and hearing do 10 different logs. And it'll help me be able to find perhaps my logs instead of a logger. That's a very naive and simple example, but it shows you the power of basically delegating the responsibility back to the caller. So I'm yielding this call. So right here, I'm gonna be yielding whatever's happening here to execute what's gonna happen here. So what's, let's think about this for a second. First thing that happens is we called line logger, line lager said, hey, no problem, cool. I have a block. And then the first line of code executes and says, all right, well, I'm gonna print this thing five times. And then now after that, what I have now is I have this little block of code. And this little block of code right here, I need to run. Then that little block of code is this stuff right here. It's whatever's in between these two brackets up here and that's gonna run that block of code. And then after that's done, it's gonna go ahead and return back down and it's gonna run the next three, next five iterations of this repeat loop. So it's basically going inside of here, running this, hopping back out, doing something out here and then hopping back in and finishing up down here. And so it's a very simple way that you can actually implement a lambda expression inside of a function. Now let's also create maybe another lambda expression here. Let's call this one repeater. So repeater we'll wanna say, maybe you wanna do something over and over. Kind of like very similar to like a loop would have. So let's do the same thing. Let's create the same signature here, but this time I'm gonna say repeat five times. And then what we're gonna do is we're gonna call the block five times over. So then what we could do is we could do something like this and we're gonna call, I'm just gonna comment this out for now 'cause we don't wanna see that inside of the output. So we'll say repeater, and then it's gonna take it out in a block. And so inside of here, I can just say, print ln, hello. And what's gonna happen is I'm gonna run this, repeater is gonna run and it's gonna print it five times in a row. But now of course, we're trying to make our application a lot more user-friendly. So we wanna be able to provide how many times we think these things should repeat. And so what we can do is we can go ahead and say, all right, one or five in here. And so I'm saying number five, but there's no way we can tell repeater to do that, but we can. So we can go down here and say, how many times do we want you to repeat, and that's an integer value. And I can take the energy value and just drop it right into this repeat function. Again, we're kind of duplicating what's happening here, but I'm basically telling, hey, I want our little repeater to run five times and then what's gonna end up happening is it's gonna run five times. Now for whatever reason later on, I decided I want this repeater to run three times or 13 times, I'll run this here, our little repeater thing will run 13 times. Now, as I start thinking about it, eventually I realized it would be really great if there was a way that I can get to what iteration I was on, because I realized that when I am gonna repeat five times, I wanna say hello three times. So hello, three times. And then I wanna say goodbye two times for whatever reason. So how would I do that, so I wanna say print line, I'll say goodbye. But how am I gonna say that on the first couple of iterations, I don't know what that is. So what I need to do is expose that value to the block. So basically I needed to tell the block, hey, you need to be able to get some value. Now, remember this value, this right here is the input type. There's no input types here, we didn't provide any parameters. It's still gonna return a unit, so it's not gonna do anything. But what I wanted to change is to say, hey, you know what, I want this block. So again, this chunk of code, I want it to be able to accept a parameter and this parameter is gonna be an integer value. And now immediately, what you'll notice right here, we have a squiggly error saying, hey, there's something wrong. And what that means is that we need to parse a value in there. Now, little did you know that the repeat function that is built into the standard library actually it gives us an index, we just were not using it before. And so I'm just gonna go ahead and use it now. And so I'm gonna parse an index and every time the repeat function fires off, it's gonna give us an index. I'm gonna send that index directly back to our block and that's gonna get called inside of here. And so let's go ahead and use our index. There's our index and now we can do something with it. So what we can do in here is we can say, if index is less than three, go ahead and do this. And else we're gonna go ahead and print this. So now we run this, what we're gonna see here is hello, hello, hello, goodbye, goodbye. Because what's happening is the index is being print. So it's go ahead and print line on the index. For each iteration of this loop, we can see down here. Each time, so remember what we're calling repeat, which is built into the Kotlin center library just tells, hey, repeat this little function however many times we tell at the time, we told it five. And each time iterate is gonna call this block. And this block, all this block is, is this chunk of code here, this is the block. And then what it's gonna do is gonna parse its current index in here, which is what we decided to do here. And now we can decide to start iterating inside of here and perhaps perform some logic. And if it's less than three, of course, we're just gonna print hello. Otherwise we're gonna print and goodbye. Now this can be done for any number of things, we could decide to use the index. We decided not to use the index or whatever. And again, if we don't wanna use it, we don't have to, we can just kind of get rid of it. Again we'd have to make sure we're not using the variable, but here we are. So this is how you can go ahead and parse a variable into a lambda function and basically receive that variable back into the block itself. So we've created a function called repeater. We're gonna tell it how many times it needs to do something. And then we're saying, hey, every time you do something, we want you to call a particular function, this function we're gonna call it block. It's gonna take in an integer, I don't care what the integer is, but it's gonna take an integer and it's not gonna return anything. And then inside of our function, we say, hey, we're gonna use the built-in function called repeat. It's gonna repeat. And then every time it's gonna give an index and we're gonna parse that index into here. Now I could parse any number I want here. If I want it through the whole time, I can just parse number three, it doesn't matter. It doesn't have to be the index. I could be three times 1000, it doesn't matter. All this block, all this sick function signature is saying is this block is expecting its first parameter to be an integer and it's not going to return anything. That's all it's saying. So it doesn't matter if this value right here is the index, or if it's a random number, that's up for you to decide. Here, we're parsing in the index because we wanna know when we're using a repeater function, what index we are. And when we're repeating, are we the fifth iteration, et cetera, or the 20th iteration, what are we doing at that point in time. Okay, so now we have that repeater. So let's go ahead and comment this out. So let's assume we wanted to have to do something a little different. So let's say we have, what's called a function called Derby announcer. This Derby announcer function is gonna take in a lambda expression. This lambda expression will look like this. There'll be a block of code, it's gonna take in a string. So this first parameter is gonna be a string and then it's actually gonna return a string. So this lambda expression is gonna return a string and this announcer is spelled wrong. And so what we'll do here is let me fill this out. Okay, we're back soon and have to see me tight. So the Derby announcer function does a few things. It has a lambda expression, it's a block. That block takes in a string as a parameter, and then it returns a string. And then what we're gonna do here is this gonna be like a home run Derby announcer. And these are different names of perhaps some baseball players. So you McGuire, can say, go Honeycutt, Davis, Dolly Weiss, et cetera. And then what we're gonna do is we're gonna randomly choose a player's name and then to the screen, we're gonna put the next player's name. The next player is whatever this random word is. No it's gonna be, you know, we can change this to random player. At that point, this will print to the screen. And then what we wanna do is maybe we wanna have some type of log that we wanna print to the screen, but we don't know what that's gonna be, or print some other type of thing we could call it a log, could call it something from the announcer, announcer topic, announcer message, do that. But we don't know what that's gonna be 'cause it could vary between announcer. And so each announcer could be a little bit different. And so what we wanna do is allow the announcer to say what they wanna say. And so we're gonna delegate that back up to the block. And remember we're gonna parse into the random player that we chose, we're gonna parse back up to them. But this block also returns a string, remember. So if we look at this block, it takes in a string right here, it takes in a string and then it returns a string. So if we were to implement this, what we could do is we would say Derby announcer, and we're just gonna parse him a lambda expression. Again, this is gonna take in a player is what's gonna be sent in. So it's gonna take a string. We could say, this is a string, or we could use type inference. And then we can go ahead and do something. And now we need to return something out of here. And we can say something like this, player is a great asset to the team. And this is the value that's gonna be returned, remember 'cause it's the last value in the lambda expression. It's what's going to be returned inside of here. Now, if I did length, we're gonna run into a problem. Why, because we're expecting a string, but we're giving it back an integer. So, okay, now we have this. So the Derby announcer is gonna say something. The Derby announcer's gonna say this player is a great asset to the team. So now what's gonna happen if we run this is it the Derby announcer. We don't know what player we're gonna get, but here we get the next player is Consaco. Consaco is a great asset to the team. And again, the way this happens is Derby announcer gets called, we have a list of players. We grab a random player off of that list, we then print something to the screen. We then take that random player, we send it to the block, which is this player here. We then do something with it, so this could be any string. So it depends on the Derby announcer, whoever's using the Derby announcer lambda expression. It could be a number of things. I mean, it could be going out to a database, could be going to a web service. It could be going to a Megatron at a big ballpark to degenerate stuff. And then once that is happens, we go ahead and take that value and we send that value of whatever message we have, we're gonna send it back through the lambda expression saying, here's what we wanna return. The lemon expression takes that and then does something with it and here, it's just gonna print it to the screen. So we're basically coming in here, hopping back out, doing something, grabbing the value outside of this external function of this lambda expression inside of here, bringing it back inside of this function and then doing something with it again. So we're allowing it to like yield some control. This line right here is allowing us to yield some control to some outside caller, which is really powerful. And this enables you to do some really powerful things. And it doesn't have to be a string, it could be any types of things. It doesn't have to return a string, it could be returning all different kinds of things. And so this is how you can use lambda expressions by parsing them as parameters, as arguments into other functions. You can create your own lambed expressions to parse around. You can create your own functions that require lambda expressions. And then you can delegate behavior back to up to the caller. You may be developing an API and you know that you wanted to get something from the end user, but you don't know what, but you wanna give them a little bit of data. Here I'm saying this is my announcer. Perhaps I'm creating some code for a baseball field. And this announcer is gonna be, you know, get a random player for whatever reason. There is a home run Derby. And so we're gonna get the random player, who knows what they're gonna say from there, but I need to get that information back because maybe I need to take this information and give it to a teleprompter. Maybe it needs to show up on a teleprompter, I don't know. So all different kinds of things you can do, but you can delegate this stuff pretty easily using lambda expressions. Let's assume that you're creating a lorem ipsum generator, which is Latin text that's typically used in a lot of copywriting when you're not sure what you wanna write and you're designing some texts. And when you're doing this, here's for example, the code, we have a bunch of Latin words here, and we want to randomly get a bunch of words. And we have a function here that basically says lorem ipsum takes in the number of times we want, number of basically words or loops we wanna do and then basically it does a repeat. And what happens is we will do something a number of times. So it's five times, it'll repeat this five times giving us the index of each time. And then what we do is we look at the list of Latin words to get a random word. And then we send that back to whatever the block is here. In that case, it's going to be this caller here and you're gonna get back to the index. So basically the current iteration. So if it's the first one, it would be number one, if it's the fifth iteration, it'd be five. And then whatever the random word is. And at that point, you can do whatever you wanna do with this random words from this lorem ipsum text. So let's say you do that and you run this now, and then you have this text here. And of course this is gonna change each time because everything is kind of random. Now notice one thing here is that we're not really using this index parameter. And so we do need the word parameter. So what we can actually do in this case is a couple of things to clean this up. First of all, due to type inference in Kotlin, we can get rid of the types. So alrighty, Kotlin is gonna know. Sounds good, we already know that the first type here is gonna be an integer because that's declared right here. And the second type is going to be a string because that's declared there. So we don't need you to define the types for us, we can do that for us. Next by default, there is a kind of standard that when a parameter is not used, but we do need to declare it here for the purposes of the compiler, you can use a underscore. And that basically tells Kotlin, hey, we know that there's a variable here, but just go ahead and ignore that. And so if we run this again, we can execute it and that would work just fine. Now, another interesting thing is if for some reason we didn't wanna do anything with it here, say, we didn't wanna use word either. Now we also get this here. We can rename both of these to underscore, and that's kind of the default mechanism and the default nomenclature for variables inside of a lambda expression that are not used. So basically this means unused. So anytime you see an underscore inside of a lambda expression that you're using or in code somewhere, it means whatever this variable is, it's not being used, so therefore it's been turned into an underscore. It's very common that a Kotlin lambda expression only has one parameter as the repeat function does here, which is built into the Kotlin standard library as shown here. It has one value and that's the index that is currently iterating upon. So if we were to run this right now and we told it to repeat 10 times, we would see that this is iteration zero through nine. Again, this is zero base indexing. So we have 10 iterations here. So this is printed 10 times. Now, anytime you're working with a lambda expression that has one parameter the compiler is usually smart enough to figure out in almost all of the instances, the types. So it can move the type that's one can prove me here, but you can actually improve this a little bit further by actually completely removing the parameter all together and on a single on a lambda expression with one parameter, by default Kotlin we'll call it it. So it can infer the type of the actual parameter. So if it's a single parameter in this case, repeat only has a single parameter, it'll show as it. Now the same thing happens for many other things like the map, instead of the collections of map, you're gonna have filter, you're gonna have a bunch of other things of that nature. And it only has one parameter, you can reference it with the word it and Kotlin will automatically map it to that. So now if we run this again, we'll get the same exact result, but we save herself a number of characters when we're coding and over the course of a long weeks and months of times, it's a lot of code and less cognitive load for you to read in the future. And you can just replace all the extra code with the word it and make sure you include the dollar sign if you're using anything in regards to string interpolation. Otherwise, if you didn't wanna use string interpolation and you were going to use something like this, you could then go and say it, plus maybe one, let's say you wanted to do that. And you're gonna print that line. So it again is just a variable. And now we get one through 10 because we added one to the index. And that's how you can use the it keyword in Kotlin in lambda expressions. Let's talk about how you can call Kotlin classes from within Java. So we'll talk about the JVM interrupt here. So let's assume we have a couple of files here. One on the left-hand side, we have a customer model. All it is a Kotlin data class that contains the customer and it has a function in it. And basically the function checks if the customer has a long, we have maybe some type of service. So we have a couple of packages here. And inside of this application, we have some Kotlin, some Java for whatever reason. It has to be Java, or it's just maybe a very large file that we haven't converted to Kotlin yet. And you need to call into some Kotlin, maybe that was written. Now you can actually do that. So here we're in a Java file and let's say that we need to interact with the customer. And so let's have a method called process customer. So we'll say public void process customer. And then what we need to do is we need to work with the customer, so we need to take a parameter here. So we're gonna say customer and there is customer, so we'll do it just like Java has it open, closed brackets. We need to import this. Now again, this is a Kotlin class. So if we look, go through the definition, we're gonna see that now we're back in Kotlin. And that's it, now we are able to work with this customer. If we wanna do something with the customer, we can say customer dot, and then we have all of the methods that are available to us. So customer has long name, that's a Boolean value. That's over here, that's this method right here. If we want to get the customer's name, we don't have get name, now this is interesting. So get name, actually goes to name, but we never defined a get name method. Now Kotlin takes care of that forest underneath the hood basically creates a get name and set name for us. But if we look there is no set name, and the reason why is because this is a val. So this is a read only. So we cannot reassign this value, it's a read only type. If for whatever reason, this needed to be writable, needed to be mutated, we could then change this if we had access to the Kotlin code. And then at that point, Kotlin will give us the set name and we can parse in the value, whatever the new name is. Now, at this point, you can call into Kotlin very easily. You can say customer.customer has long name, and you can do anything that you would really like to do at this point in time. The only thing you really have to really worry about with these classes is the different types. And you can know that the getters and setters are generated for you automatically for through Kotlin because inside of Kotlin, we don't need to actually generate the getters and setters because we just will access it directly off of the name properties. We can just set the name equal to something. And that's how you can call very simple objects in Kotlin. Let's assume that we have this Java class and we need to call it from Kotlin. Well, that's pretty easy to do. This class has a couple of options. One is we can get a customer from a social profile. So this would assume that this was going out to some social media API, and it would get the customer, and then returned customer back. Maybe we would look it up by their social media handle. And this method basically returns a list of process customers, which doing some processing in the background. The implementation is not important, but what is, is the types and how it's being called. And then we have a Kotlin file called customer facade, following a very common facade pattern where maybe it's interacting with a bunch of different services to perform some work on some customer objects. And lastly, of course, we have the customer object, which just has customers name and similar methods in here, which are negligible at this point. So inside of customer facade, let's assume that we need to call into the customer service, which is Java. How would we do that? Well, let's assume we wanted to parse it via the constructor. So what we can say, this is customer service, and now we are working directly with the customer service and we can say something like this. We wanna get the customer, customer. And then we can say equals customer service.customer from social, and then maybe we'll parse in user name. This is an example username. Here we are now calling into Java from Kotlin, but there's something interesting to note here, is if you look at this right here, you'll see the customer has an exclamation point at the end. And what that means is that it is a platform type. And this means right here, that T could be null or it could not be null. So we're not sure, you have to decide for yourself because you're calling into Kotlin. So there's a couple of things you could do here. Now, of course, if I were to just type inside of my application, I would just type in customer.name I could do this, I could say print line customer, not name. And that should work. As long as the customer is not know about. However, there is a chance that this customer object could be null. And if it does come back null, I am going to get a null pointer exception. So couple of ways to handle this inside of our Kotlin code, we could of course, just go ahead and use the double bang operator that will force it to not be null. However, that is a little bit of a code smell. We could use require not null, that will work as well, but again, this will crash the application. Sometimes that's the only thing you can do when you're calling into a library you don't have control over. Then at this point you're saying, well, I know the customer is not gonna be null because I'm requiring it not to be null. you could write this in a try-catch. If you have access though, to the actual Java code, you can go to this implementation and you can slap on an annotation. You can slap on the not null annotation, and you could re various different ones from the JSR to I'm using JetBrains version here, there's one for Android as well. And what this will do is tell Kotlin that, hey, trust us, we know what we're doing. If we return from this, it's not gonna be null. So now if you notice the exclamation point went away. Kotlin saying, well, you are telling me via this annotation that whoever calls into customer from social will get back a customer object and that customer object will not be null. So we're gonna go ahead and trust you and then we can go ahead and execute accordingly. And at that point in time, I can do whatever I want. Now, this also can be a little bit further here. Let's take a look at the other method. The other method is processed customers. So process customers here, let's say val customers, you can say a customer service.processed customers. Maybe you're processing them to update account details or whatever. Now notice the same thing. Again, a platform type is being returned here. We have the exclamation points that are determined. Now we have two of them. We have a list and we have the customers. Now what this is saying is like, hey, caller, Kotlin folks, we're not sure if this is a read only list, we're not sure if this is a mutable list. We're not sure if this is null, we're not sure if this is nullable, we don't really know yet. Because you're calling into a platform type, we're not sure what it is. And so you can start slapping on additional annotations on here, but for example, you can also put a not null annotation here. That is not null annotation and again, we'll remove that saying, hey, at this point, this is a list that's not gonna be null. So therefore, if we do perform some operations on the list, which we get at this point, which is nice, we can actually map over this list, which is interesting because we're calling into Kotlin, we're getting back in a array list and then, and then array list, we're getting it back inside of Kotlin. And now we can start performing using our Kotlin standard library collection utilities. Now this makes life a lot easier. I can perform filtering I can perform all different types of things directly inside of Kotlin after I've called into Java. So a bunch of different stuff you can do. One thing you just wanna be aware of is when you are calling into these various platform types that you check for nullables and make sure that you don't have no pointer exceptions. Let's assume that we have this extension function inside of a string extensions.kt file. And this extension function extends this string class and allows us to grab the initials. So if we were to have something like this, have a string that says Donn Felker, the output would then equal DF. It would just grab those initials. So it's not a very robust function. Actually it could be quite error prone, but for simplicity sake, this is what it does. If you parse in a string with that separated by space, you'll get back the initials and let's just assume it the name. Now, if you wanna call this from Kotlin, of course, we're very easy, we can do that. So with let's assume we have a customer here and the customer has a name property. So again, if we go look at what a customer is, it's we have a customer class. So it's a very simple data class. Now, if we have a name, a name as a string as we can see here, and we can just get the initials off of the name, so that's pretty easy in Kotlin. So now we have the initials, we can do whatever we want and make sense. However, there are times when you would like to call these extinction functions from a Java file. The question is, can you do it? So let's assume that we have this Java file that processes customers, and in this method, customer from social. Assume, we are retrieving a customer profile from a social API, and here we have the customer object. Now we wanna use user's name. So of course we have get named, which is generated for us. Now if we were to type initials, we're gonna see that it doesn't exist because extension functions don't exist in Java, but we can still use that code that we'd wrote in Kotlin as an extension function. And previously, if you've worked inside of Java, you've probably written a lot of helper functions and they've been inside of basically static classes. And so what we'll go ahead and do is the string extensions file actually generates a file for you. So this is a top of a function it's not inside of a class at all, but it does generate a file for you. And so if we type string extensions, you'll see string extensions and a kt at the end. String of tensions kt and then there's a static method called initials, you parse in a string, and now you can get your initials. So you can say string initials equals. And this will call into if I go to the definition, calls into this extension function. So this is how you can call into an extension function inside of Kotlin. So any extinction function you have, you'll wanna look at the actual file name. Now there is a way to modify the file name and using the JVM name annotation. And I'll show you that in another video, but if you wanna call into any of your extensions. So if this was just called extensions.kt, then in that case, it would just be extensions kt. So basically slaps the two letter kt at the end. And then at that point in time, you can access it just as if you did with a regular helper function that you may have traditionally built inside of Java. And that's how you call an extension can function from Java. Calling an extinction function from Java works, but let's be honest, it looks a little bit clunky here. There's this weird kt thing that we have at the end, which we didn't specify there. Inside of the string extensions file, it just looks like this. We never specified a kt in here. So having kt riddled throughout your code base can be kind of a weird feeling and kind of a code smell. Thankfully, the folks over who developed Kotlin thought of this. And what you can do is actually change the name of the actual file is generated. And what you can do is add this annotation called file, JVM name and provide the name of the file. So here, I would wanna say string extensions, or I could say string utils, so say a tree extensions. Let's save that and we'll go back to where I'm calling it from Java. And then immediately we noticed that our previous call in here is not working anymore. So if we were just typed string extensions, now we just see string extensions.initials. So I can go ahead and fix this by just ruin the kt. Now I don't even really know that I'm using Kotlin at all. It's not basically polluting my code base inside of my Java files. Now there may be various reasons why you can't move this to Kotlin, and if that's the case, this might be a very good use to you. Or if you're exposing your extension functions to outside callers in other JVM languages, you may wanna go ahead and provide a JVM name. So it's nice and extensible and readable and doesn't pollute their code as well. Now you can name this, anything you want. It doesn't have to be whatever the file name is. If I want this to be happy clown file, happy clown stuff. I'm gonna go over to customer service and we're gonna see that this doesn't work. I'm gonna have to replace this with happy clown stuff. And so I can give that JVM file name whatever I want, that's what that's gonna be. And so that wouldn't work anymore. So of course we would never wanna name something like that, but we can go and take it back to string extensions. We'll save that, again that's not gonna work the string extensions. So anytime you are working with something like the Kotlin file that has a lot of top level functions, which this is a top level function that's not inside of a class. It's just basically sitting inside of a file somewhere at the top level function. And you need to expose those to other JVM languages, such as Java, and you want it to have a nice pretty name. Go ahead and slap the JVM name annotation on there at the top of the file, and it will change the name. So therefore when you call it from another language, it'll look nice and won't pollute the code. Quick little hack I wanna show you. Instead of any file, you can put a top level function called main and inside of this main function anywhere you want, you can print line or do whatever you want. And what you can do is type code here. Now I can actually run this directly here inside of the IDE and it will execute this code. Now I do have another main file down here, which is where my main application would normally start. However, if I'm buried somewhere in my application and I would like to test out a quick idea, maybe I'm kind of working with the red I wanna play with. I can just run this directly right here and I can hit run. I can do debugging everything. And if I hit run down in the output window, we'll see foo. So this can be anywhere, I can take this here. I can go over to a customer class. I can put a function over here and I can do something here and I can run something from here. So anywhere you put a main on top of main function, you can execute it with inside of the IDE for quick feedback loop inside of IntelliJ. Sometimes you wanna interact with your code and a session where you can kind of play with it. One way you can do that is with the Kotlin repl, which is a real evil print loop. And to do that again, that's through tools, Kotlin, Kotlin repl, and once everything is loaded, then you can start typing code into here. So for example, let's assume that we wanted to go ahead and build a string. So we say val name equals Donn. And then I use command enter to enter into a new line. I can say name.length, command line. It'll tell me if they integer it's four characters long. I can do other things such as name that substring and I'll get the nice IntelliSense in here or code completion. And we go with the first character, there is D. I can also use, for example, some of the classes I have. I have a customer class here, which is a data class. I can use that inside of my repl, 'cause it was loaded, so I can say customer. So I can go down tab, and what will happen is you'll see it actually imported customer there for me. And so I'm gonna say val customer equals customer Donn enter. And so had to import it because if I don't import it, I won't know about it inside of the repl. So I can say customer and if I hit enter on a customer, it'll tell me the type of the customer and then what the two string value is. And so I can say customer.name. I can also do customer.name.initials. Now notice this I'm actually calling in a extension function that I have, it's called initials and we we have an exception there. And the reason why we got an exception is because the string value of customer, it does not have a space in it. So if I were to redefine my customer, so we'd do another customer, customer two equals Howard Parr. I had my full name with a space then the initials function would work. And I didn't tell it to execute yet, I just pressed the enter, and I say, initial customer two.name.initials. Now, if I enter here, we'll see that it does execute properly. So you can actually interact and do all different types of things inside of the Kotlin repl. And again, that's access via the tools, Kotlin. Kotlin repl. So it's a good place to come around and play around with some code that you're maybe testing, trying to figure out if it's going to work or not. Maybe you're trying to understand a certain interaction with the library and you don't wanna fire up an application. The repl is a great place where you can do that. And when you're done, you can go ahead and over here and hit the close button and then you're out of the repl. So let's go ahead and create a actual Kotlin simple calculator. To do that, you open IntelliJ, you'll click create new project. You're gonna wanna select Kotlin from the left-hand side, then JVM idea. You wanna give it a name, I want to say simple calculator. You can you leave these default settings enabled and hit finish. This is gonna go ahead and create a new project for us. On the left-hand side if the project window is not open, go ahead and click the project window to open it, the project explorer. And instead of SRC, we're just gonna create a new file. So you can right click or command in. So right click new cotton file class. I wanna call it main.kt. Now it's an empty file here. Now at this point in time, we needed to go ahead and fill this out so we can say fun main. That's gonna be the main function, open and close. And now we have a main function. Now typically a main function will have some type of parameters it can be parsed to it from a command line. So that's gonna be var Argh, Argh string, which means that there could be many variable amount of arguments that are parsed in. It's been basically great out 'cause they're not used right now. So none at this point in time, we're ready to start building our application. And the first thing we can do is provide some type of basic output and we'll say, please enter and arith. Please enter an arithmetic problem. And if we save this and then we click this little run button now and hit run main kt, will know this is going to build down here, and then down at the bottom, the run window will open and it'll say, please enter an arithmetic problem. At this point in time, you've actually created your first Kotlin program, though all it does really is write some text to a console and exit, and we can see that the program has exited. So basically the execution of program started the main function. It printed this, had nothing left to do and it exited. So that is the beginning of our application. So we've built the first version of our application, but we need to receive some input from the user. And to do that, we can use the read line function that's built into kotlin.io. And his is built into the platform. And this allows us to read some input from the user on the command line. And then what we can do is say print line, let's say you entered the value that you entered using string interpolation. And if we go ahead and run this now in the output window, down below, what you're going to see down here is that please enter your arithmetic problem. Let's see one plus two and then we'll see over here that you entered one plus two, and then of course the application exited at that point in time. So this works great, we can now get some values from the user. But just like a regular calculator, a calculator is not going to stop after you enter one problem, so we need to enter multiple problems. And this is a perfect opportunity to use a wile loop. So what we're gonna say is, while while is not null, then we can go ahead and grab some values. And so it we'll say here is we need to get the input again, and then let's go ahead and print this here. So we did val, we need to make this a var now, 'cause we're gonna be resetting it every time. Now let's go ahead and print line what we know you entered input. Now we're gonna run into a problem here because we haven't told a way for the while line to exit. And if we know anything about the read line, what read line says is it will return a read or null if the input stream is redirected to the file. We're not redirecting to a file, so we're not going to get null back. So if I press enter, we're probably gonna get a line feed inside of here. So what we need to do is have a way to short circuit out of the wild loop. Otherwise this chunk of code will just continually run on forever, there's no way for us to get out of it. So if we were to run this now, let's go and see what that looks like. Let's run it and that's enter a problem. One plus two, okay, two plus three, five divided by 10. If I enter, we can't get out of this, we're stuck. So we need to wait it to short suit circuit out. So I'm gonna stop the program. So we do need to actually figure out a way to get out of this. So what we can do is we can check to see if the input is null. So if the input does not equal to null, that's what we're doing, but let's also check to see if it's empty. So, and the input is not blank. So if we go look at the implementation of that, returns true if it's not empty and contains some characters except the white space characters. So we wanna make sure it's not blank it just contain something. So if that's the case, then this piece of code will just continue executing. And this will be the main in a moment, this would be the main section of our calculator app. So let's go ahead and run this again to see what this looks like. And once we run this, we'll see, okay, one plus two, there we go, we got two plus three, we got five divided by 10, so we can keep entering things over and over. However, now if I press enter, what we're gonna see is that the process finished right there. So it exited out of the application, because what it made it exit out is basically it is blank. So therefore it only continues if it's not blank. Therefore the application said, well, it's blank. And at that point it exited. So now you can do a couple of things here. So if you wanted to say goodbye, you could pretty much do that right here. You could say print ln goodbye. And then you would know that the proper program exited. So we could do this very easily. You'd hit run here and now we can enter that arithmetic problems, one plus two, three, 33 plus two and an enter and I was like, goodbye. And then it exits. So we know that the actual program is exiting. Of course, we do know that already from here, but when we compile it later, it would be nice to have some user feedback to say, hey, we're done with the program. So now we can actually create our calculator inside of this piece here, because we can now process many different problems. One after another. Each time we're gonna get some new input via this read line method, which then will change. And then we can process it, which we'll end up doing right in here. Now that we have the user input, we need to decide what to do with that user input. So when you kind of need to break it apart into a couple of sections. If we have a problem that's entered like this, we need three different components. We need the first number when the operator and we need the second number. So we're gonna need three different types of variables from that one string. And we can do that very easily, so it's go and get rid of this. We can do it very easily by declaring another variable called values. And what we're going to do is we're gonna split the string on a space. And what that will do is it'll give us a list of strings that we can go ahead and check on. So let's go ahead and take a look at each one of these strings. So we'll say values zero. I'm gonna duplicate this line a few times, one and two. Now of course, this is not gonna be error proof. If for some reason we do not get enough values in here, we could crash the program, which we'll see here in a second. If we run the main program, what we'll see here, please enter arithmetic problem one plus two. We'll then see that we have all of the operators now separated, so we have the number one, number two. If we were to do five plus four, we would get those broken apart. But if I only put five in here, we're gonna get an index out of bounds exception right here on the 10th line, because I'm just pressing press five in there is split on a string, so it's not finding this and saying, hey, there is nothing inside of here. It looks like by found a string or or a new line or something there. So we can't find anything in that regard. So we're gonna go ahead and don't acception. So this isn't really foolproof, but it does give us the ability to actually start performing our operation. Now, the one that's really important to us right now is actually this first one. And this is because this is what's gonna give us our plus, our minus our multiplication or our divide operator. And then at that point, we can decide how we wanna progress in our application. And so, based upon if this value is a plus sign, well, then at that point, I wanna do add something together. And so it's gonna say we're actually doing print line. There we go. If the value is a plus sign, then we're gonna go on and go ahead and add it together. Otherwise, you know, we can say else if values is values one equals equals minus, then we can do something else. So we could continue this path down of else if. And this will work, there's nothing wrong with this. We could do this, this'll work. However, it does get a little clunky. This is a perfect opportunity for us to use a when statement. So I'm gonna say when values one, so check that value one. And when you say is plus I wanna do something, so, say print line, when is. And it is not gonna be needed here because we're actually just using it to directly against the value. So we'll say, plus, we can say minus that's it print line, subtract. I'll go ahead and duplicate these real quick. And then we can go ahead and of course we'll say multiply and divide. It'd be pretty easy and divide. And then for whatever reason, if we don't have something that matches, we might wanna say, throw legal argument exception. Let me say invalid operator and we'll just go ahead and parse in whatever that value is. So parse it in and values one. And then that's gonna allow us to go and get rid of this down here. This will allow us to actually see and do to something different based upon each one of these input types. So let's go ahead and run this program. We'll put a few of the arithmetic problems in here. So one plus one, so it looks like we have add. So I have one minus four, subtract. We have six times seven, multiply, 42 divided by 10 is divide. And let's go ahead and just throw something crazy in there. Let's do like for example, maybe we wanna do powers of, but we haven't implemented it. We could do 10 to the power of five. Boom, we have an exception invalid operator. And we add D to the power of operator, which is normally used in map instead of programs. So here, our calculator is a simple calculator. It's only gonna process these different types of inputs. So now let's go ahead and implement each one of these and I'll be right back while I do that. So you don't have to see me type. Okay, now we're back. So I've done this very easily, I've taken the first value. I convert it to a double and take the second value and I converted to a double. I've converted everything to doubles here so we don't lose any precision. Now there's one thing I would like to do, that would like to clean this up a little bit. So what I'm gonna do here is let me go ahead and say, I'm gonna refactor this, and I'm just gonna say, extract this into a variable. And it's gonna ask me to replace two occurrences because it's occurring two places. Sure and we call this operator, just so it reads a little bit better. It's actually saying we can move it into the declaration of the when. And if we were to do that, here's what it would look like. I could just move that into the declaration there. I don't like the way that looks, it looks a little bit, it's too much for me to read. This is a lot easier for me to read, so I'm gonna leave it like that. So I'm the operator. And then what I could also say is something like this. I could go ahead and extract this good refractor it. I can extract this into a variable and it does occur four times. So we were repeating ourselves, which could be problematic for updating our code later. So I'm gonna call this LHS for left-hand side. I'm gonna do the same thing over here. I'm gonna go out and extract a variable. We're gonna call right-hand side RHS for all four occurrences. So now we have our ever input and we've got our operator, our left hand side and our right hand side. And then this is pretty easy to see. So when the operator is a plus sign, we're gonna go ahead and print out the... We're gonna convert all these to doubles and say, all right, so we don't lose any precision which can happen sometimes in multiplication and division, depending upon what you're doing. And then we're gonna go ahead and actually perform the operation. And then what we're gonna do at that point in time is we're gonna go and print that value and then we're gonna go ahead and do a read line. So let's go and run this to see what happens. So I've entered arithmetic problem one plus one, we get two. Two times five and we see an index right out of our exception. That's because we didn't put a space there. So we do have a cup, some input problems that we probably do need to process. So let's go and see if we can get you to one of these to run accordingly. So we already did a plus let's go ahead and do a subtraction minus six. Okay, that makes sense, eight times seven, 56. Let's do 60 divided by ten, six. So all that makes sense. So our application does work now, we now have a application, which is a simple calculator that shows us how to do adding, subtracting, multiplying, dividing. Our application works fine at this point, but we do need to provide some type of validation. For example, let's go ahead and run our application. And let's assume that we make a mistake during typing, which could be very common. We say one plus, and we forget to enter, what, let's say one plus one works, but we do one plus and we forget to enter the additional value and we hit enter. We receive an index out of bounds exception. So what we need to do is check that we have the proper number of arguments. So what we can do is say, if values dot size is less than three, 'cause we need at least three parameters, then we're gonna throw an illegal argument exception, let's say invalid input, expected value plus value received. And then whatever the input was. So now if we run this, what will happen is we're not gonna get index out of bounds exception. What will happen is we can still enter one plus one. We'll still get that. But if we enter one plus something, we forget to enter the additional value, we'll now get an legal argument, exception that was thrown. It's an invalid input, expected value, blah, blah, blah. It was this particular value. Now you could decide to crash the program if that's what you'd like to do, or you can decide to skip this completely so we could change this. So instead of having it throw an exception, we could actually just have it say print line. We print line of idle less than three else. And then we can wrap everything else since I have an else here. And what will happen now, when we run the application, run us and we'll go one plus one is two. We do one, does it age two plus something, we make a mistake, enter, invalid input, expected value of up plus value of received two plus. Okay, let's try it again, two plus three. Okay, it works. So now our application is a crashing. We've performed some level of validation. We're providing the feedback to the user and we can continue using the app and it works as we would expect. But we also have another problem. The other problem is gonna be let's go ahead and run this. What happens if for whatever reason, a user enters one plus two and then the next time they say one plus dog, what's gonna happen there. We get another exception from the program and the program crashes and a calculator doesn't know how to handle the word string dog. And so it says number format exception for input string dog. And if we take a look at line number 18, it's happening right here. And the reason why it's happening is on the right hand side, we're trying to convert that value to RHS value, which came from here, which is the second, the third item in the list. We're trying to convert it to a double and that's where the problem occurs. So we need to do something here. So one quick thing that we can easily do, which works very well because at this point, this isn't an illegal argument. We shouldn't be able to handle this inside of our application. What you could do is you would say to double or null. And then what we're gonna do here is we're gonna do the Elvis operator. And the Elvis operator says, hey, if something happens here and this is null, the I want you to return another value. And so this is gonna say, throw legal argument exception, invalid input. And then we'll go ahead and actually just go ahead and render that input values zero. Now what's gonna happen here is if this value right here can be converted to a double, it will be converted to a double and it will be returned here. Otherwise, if it can not be returned to a double, a null will be returned. At that point, the Elvis operator will interrupt and say, hey, on the left hand side over here, this is null, so we need to do something on the right-hand side. And then we're telling it, hey, if you encounter null, throw an illegal argument exception. So basically we're trying to parse it. We couldn't parse it as a double. So therefore it was returned as a null, and at that point, and then short-circuited to the right-hand side over here. So we'll do the same thing over here to double or null. And I'm just gonna copy this to save some typing. There we go, and then this'll be value one. Now, if you could see over here, all these are grayed out, 'cause we're already casting these two a double. I can actually remove these redundant calls. So boom, we'll just go ahead and remove these real fast, I'll be right back. Okay, now I'm back, we have our very succinct and clean version of our calculation. And so very nice here, very succinct here, but what's happening and we can actually print things directly to the screen. So if we were to run it now say one plus two, we'll get back three, if I say one plus dog, we're gonna get an illegal argument exception and valid input. Now, of course, this is crashing the application. So there's a couple of things that you could do here and in which I would challenge you that we're not gonna do, that you could do on your own to challenge yourself, is remove the illegal argument exception. And you can either decide to make it a zero. So you could just do something like this that would make it a zero so that it would be a double or zero, which wouldn't work. So because as you see here, the type inference has given you an example, or you can go ahead and say double or null. At that point in time, it could be a double and the same thing down here and then check to see if you have nulls for either one of them. If you do, perhaps provide a message to the user and then let the application continue to keep going. Here, though we're just gonna keep this where it's gonna continue to short circuit. So at this point in time, we now have an application that tells the user that they're invalid input is occurring for their first or their second value, otherwise it's gonna continue on. Now, there's one last little thing that I would like to do here in this program to clean it up just a little bit 'cause we have a little bit of duplication here with this print line. And what we can do is we can actually turn a value from a when clause. So I'm gonna say val result equals when, and then we're just gonna get rid of these print line statements and get rid of the other parentheses on the other end. And now we have a double that we can use. And then what we can use is just go ahead and print line the result. And this really cleans up our when clause. Now we can actually see, it's really easy to read and it's very easy to follow. We have an operator, we have left-hand side right-hand side. Based upon the operator if it's plus minus multiplication or divide, we're gonna perform those operations with the left hand side in the right hand side. Otherwise we don't support whatever operation is thrown and we'll basically throw an exception. And then it finally, once that result is returned, we'll go ahead and print the results. So let's go ahead and run the application again, just to see what it looks like. So one plus two, there's three, three plus five, 10 times seven, 70, 90 divided by six, 15. So now our application works. We've added some validation in here to accept the only valid chunks of input. We've made sure that we can only accept certain parameters and we're providing feedback to the user. And then of course, when we're done, we can just hit enter and the application will say goodbye, that we're completely done and then the process exits. And you've now written your first calculator app. Now the next thing is how to deploy it so other people can use it. You've built your first application and it runs within the IDE. However you would like to share with others, to do that, you can build a jar file. What you'll need to do is go to file, project structure. You wanna make sure that you have the artifacts item selected on the left-hand side, you'll hit the plus icon, select jar from modules with dependencies to make sure your module is selected. In our case, simple calculator's the one that I want. Then I'm going to select the main file, which has main kt in our case, we'll click, okay. Now at this point you can press apply and okay, you're now ready to build your jar file. To do so, you'll go to build menu and then build artifacts and you'll get the pop up and just click build. And you'll notice the build is happening down here in the bottom and actually it's already done. And what will happen is some files be placed into the outfielder, so let's go and expand those out. You'll see artifacts, simple calculator jar. And there is our jar file that can be executed on the command line. So you'd be able to actually send us to someone else and they could execute it, so let's do that. I'm gonna right click and open this area in the terminal down here. And this is gonna open up a terminal directly in this folder path here so I can see out artifacts, simple calculator jar. So what we see here is this exact folder that's right here. So we are now in the terminal of this location. We can see LS and I can run this jar file by typing Java, jar, simple calculator.jar. Hit enter and now our program is executing. Please enter arithmetic problem. One plus two is three, four plus five is this. 40 divided by seven is that value. Eight times 22 is this value. Hit enter and of course we get to exit. So now you have built your first Java application, compiled it down to a jar file, which then you can take and send to anybody else and they can execute this program on their computer. Now be aware if you do send a jar file over email, most likely most email providers are probably going to strip that file out as it can be considered unexecutable. So you'll need to get it to them a different way, maybe through a file sharing service of some sorts. So that's how you can build a jar file simply through IntelliJ and then you'll get your jar file. Congratulations on creating your first application. Playing with Kotlin and Simon IDE is gonna be the best way for you to learn. However, sometimes you can also learn directly on the web. And to do that, the folks over JetBrains have set up play.Kotlinlane.org. and here you can log in for free, not even log in just access, play.kotlin.org and start writing code right here in the browser. So now what we can do is we'll say hello from Donn, for example, and I can run this code. And what we'll see, it'll run and compile, and we'll see the output. Now, one of the coolest things we can do inside of here, we can write a bunch of code and then you can also share it. So you can click share and you can copy this link or you can directly embed it, send it to medium. And so let's say I've taken this and I've sent it to somebody else. So I have a new incognito window open here. If I paste this, what we'll see here is we have play.Kotlinlane.org and automatically loads the code that I've written. So hello from Donn has been pasted inside here. So actually, if you actually take that URL that you saw here in the video and pasted it in, you should see this directly from me right here in your browser. Now this is a great place to play around. There's also the hands-on and there are some examples in koans. Now Kotlin koans are great. Koans are a series of set of exercises that are allow you to get familiar with the actual Kotlin programming language. And the nice thing about it is they actually give you a test that you can actually have to basically make pars. So you have to actually fix the code and make these things parse in real time. And you can do it right here on your browser. They give you progress that you can follow along the way. So if you learn Kotlin for me and you're watching these videos and you've picked up quite a bit, and you wanna take your exercises a little bit further and challenge yourself, I advise you go to Kotlin. Go to play.Kotlinlane.org. You can play in the round the playground, and you can also visit the Kotlin koans and explore other examples as well. I hope you enjoy. Woo, you made it. Congratulations on finishing the Kotlin programming language course. It was over nine hours of content you just watched. You should feel proud of yourself for finishing this. This is quite the accomplishment. You now should have a solid fundamental understanding of the Kotlin programming language, and you shouldn't be able to go start providing value at your company's project, your own personal project. It doesn't matter if you're a developer, engineer, scientist, doctor, or whatever. I hope you have received a lot of value from this course. I hope you have learned Kotlin and I hope it produces a ton of value in your career moving forward. And I wish you nothing, but the best going forward. Thanks again for watching the video and I'll catch you next time.
Info
Channel: Donn Felker - Freelancing for Software Developers
Views: 32,673
Rating: 4.9905734 out of 5
Keywords: kotlin tutorial, kotlin tutorial for beginners, kotlin tutorial for java developers, kotlin tutorial for beginners android studio, kotlin tutorial for android developers, kotlin tutorial 2021, kotlin 2021, learn kotlin 2021, kotlin course android, kotlin course by google, kotlin full course, free kotlin course, free kotlin android course, free kotlin tutorial, kotlin free code camp, kotlin programming language tutorial, what is kotlin programming language, kotlin course
Id: wuiT4T_LJQo
Channel Id: undefined
Length: 580min 51sec (34851 seconds)
Published: Fri Mar 19 2021
Related Videos
Note
Please note that this website is currently a work in progress! Lots of interesting data and statistics to come.