Dart Classes Explained I - All Fields, Methods, Constructors, Operators, Getters/Setters & Singleton

Video Statistics and Information

Video
Captions Word Cloud
Reddit Comments
Captions
hey what is going on everyone i'm wicked welcome back to my dart from novice to expert complete course i hope you're having a great day in this tutorial we're going to hop on the flatterly train and move over to what's probably one of the most important stations of our dart language tour and that is dart classes the entire amount of concepts that need to be presented inside this topic is huge therefore i had to find myself splitting this tutorial into two major parts don't worry at the end of both of these parts you'll feel absolutely confident in your acquired skills and knowledge regarding dart classes so without further ado let's get right into it beforehand though i want to send a token of appreciation to everyone supporting me as official youtube members especially to diana and michael aka aza on discord if you want to become a member all you have to do is to click the join button right next to my channel and pick your desire membership having that said let's continue with our tutorial now we have already played a little bit with classes and objects in previous tutorials we definitely learned that dart is a true object oriented programming language meaning that everything in dart is mainly an object an object has been instantiated from a class therefore classes are definitely something you really really need to understand before proceeding to other topics the truth is that in order to understand everything about dart classes we need much more than scratching the surface of their concepts having this in mind let's get to work by switching our view to the vs code editor so here's the most straightforward and basic class you could ever create in dart it's an absolutely empty class nothing's inside of it right well not really you'd be surprised at how many things hide underneath this simple innocent class as you can see if we go ahead and instantiate an object from it and assign it to a variable it works perfectly fine knowing that in order to be instantiated every class needs to have a constructor method this means that this empty class even though it has no visible constructor inside of it has indeed a default simple constructor that permits its instantiation another thing to be known about this default constructor is that it's not constant meaning that if we create another object of the same type and compare them by using the identical method you'll see that our program prints false since both a1 and a2 variables hold references to different objects in memory as you might remember from previous tutorials we learned that every class except null is a subclass of the object class every class you create inside dart will actually invisibly extend the base object class this means you don't have to explicitly write class a extends object it extends it by default having this in mind our a class will be able to access the fields and methods present inside the object class and it can even override some of the methods as we'll see up next this is thanks to the basic oop concept of inheritance i hope you're pretty familiarized with it as you can see inside the object class there is this hash code field and since our class invisibly extends the object class we can actually check what's the value of the hashcode for our object same things applies to the runtime type field so having this observed those fields are not actually located inside our class but since our class invisibly extends the object class they can be accessed from there every object also comes with a tostring method which is actually called whenever you want to perhaps print it or interpolate it inside a string by default this method is located as we can see inside the object class and it's set to return a string saying that it's an instance of a specific type and what did we learn in the dedicated tutorial about functions that those methods can be overridden in any subclass as a result we can override the tostring method to return a string like this is a and you can notice that by default it also calls the tostring method from the base object class by using the super keyword the super keyword represents the base class from which another class is extending we can leave it or delete it if we don't want to use the object class implementation of tostring now as a result of overriding this method when calling a dot tostring this is the method that's going to be accessed and not the one from inside the object class same thing applies to the no such method too and also as we saw in the previous tutorials the equal equal operator can be overridden since it's already declared inside the object class but can be specifically programmed to do whatever we want inheritance via the extend keyword in dart is a really important and advanced topic to be discussed we're not going to cover it today and we'll talk about it in detail in the second tutorial on dart classes where it's going to make the most sense now let's actually move on to discussing the main components that you'll find inside a dark class instance variables and methods for ease of understanding will frequently refer to instance variables as fields so everywhere you'll hear the word field you should know that i'm actually referring to instance variables what i'm going to write now are all kinds of ways on how you can declare an instance variable inside your class note that's definitely recommended to explicitly specify the types of your fields inside a class so don't use var keyword here to infer the type i highly recommend you to rewatch the null safety and dark variables videos in order to understand this tutorial as i won't explain over and over again the same implementation rules what i did is i created 12 integer fields some of them are more special than others basically this shows every way on how you can declare an instance variable inside dart note that i have not integrated class constructors into the equation yet as i want to discuss all the scenarios with and without them so what we're interested in right now is how we can access and initialize those fields without a constructor and there are two rules you should understand regarding this any fields you create inside a class will have a default getter method automatically generated for them this means you can access their values wherever you want by using the dot operator the wherever you want part of the sentence narrows down to the current file library if the name of the variable is prefixed by an underscore this denotes that the variable is private and can only be accessed from inside the file slash library in which it was declared one example for this would be the underscore private variable i declared at first as you can see if i create another file and try to access it in there it won't work but inside the file where it was created it definitely does work perfectly fine on the other hand the second rule states that only non-final fields and late final fields without initializers also generate a default setter method in short normal final fields cannot be set outside the class they can only be set inside constructors and we'll talk about constructors a little bit later on let's start with the first rule and see it in action we'll go ahead and access all of these fields by using the dot operator and print their values note that if we run the program we'll right away receive a runtime error this is because we marked the d field as late and we haven't initialized it to a value before accessing it in the print statement if you don't understand why this is happening make sure you check out my in-depth tutorial on dart null safety the e field also needs to be initialized before use because it's also set to be late and initialized when declared however the difference between e and d is that e is also marked as final meaning that it can only be set once during all of its lifetime setting it multiple times will also result in a runtime error having this in mind this means that the f instance variable is already set and we won't be able to set it again later on other fields that need to be set before use are the static h and j fields being static means that this field exists without an instance of a so in order to access any of these static fields we can do it without creating an instance of a just like this a quick tip i can give you in order to understand these variables faster is to start and read their modifiers from left to right take the a variable for example we know that it's of a nullable integer type that means it can be retrieved and assigned as many times as possible and can be left unassigned since it will be by default assigned to no when the object is instantiated on the other hand if we take a look at the j field we see at first that it's a static field then that means it can be accessed without any instantiation of the class then we see it's late we won't have to set it where we declared it but we must set it before we access it later on in the code then we also know it's final this actually narrows the amount of time we can set it being final means we can only set it once then we see it's non-nullable so dart can't assign it a default null value and cannot ever hold no therefore again we'll need to set it by ourselves somehow last but not least if we take a look at the f variable we can see it's late so we will make sure to set it before using it later on then we see it's final so we can only set it once then we can see it's of a non-nullable integer type so we have to set it to a value by ourselves since dart can't assign it to null by default and we can also see that we have actually assigned it to a value at declaration even though it's marked late this means as we've seen in previous tutorials that the field is lazily initialized meaning that it will only get created when accessed for the first time moving on to the second rule regarding setters we can be sure that the underscore private a b d g h and i fields can be assigned or set multiple times around the code because they're not final late final variables not being initialized when declared can only be set once simple final variables can only be initialized at declaration or as we're about to see up next inside the constructor i'd probably say 95 percent of the time the only kind of field you'll ever use inside the class are standard final ones and a little bit of late final ones as you might know from an op perspective it's not really recommended to modify the fields of a class outside the class so ideally what you want to have is as many final fields as possible that are assigned only once inside the constructor as we're about to see up next so we finish talking about the dart instance variable for now now it's time to move over to the other component you'll find a lot of time inside dart classes and these are methods methods are actually functions defined inside a class and one of the most important methods you should know is the constructor of a class as you have previously seen any class you create in dart comes with a default constructor a function with no arguments no body and the same name as the class in our example is just something plain like this this is the invisible part we couldn't see and now it's explicitly coded right here what we can deduce from this is that a constructor is a method that is called whenever the class is instantiated into an object you see these parentheses here remember they were used so that you can call a function this time we use them to call the constructor function from inside the class now we can substitute the default constructor which besides creating the object instance doesn't really help us that much at the moment with another constructor that's going to bring more value to us there are several ways on how we can create a constructor the fastest way would be by using the dart data class generator extension but in order to deeply understand the concept of a constructor this time we're only gonna create it manually so as we said a constructor is more like a special function as a result a constructor benefits from all the goodies dart functions come with plus some extra features we're going to see up next in order to create a constructor for a class first and foremost you need to create a function header having the same name as the class perhaps just exactly like the default constructor what's pretty neat about this constructor in the first place is that it doesn't have any function body this is because constructors don't necessarily have to have any functional body they can easily exist in this header form just like in here however this doesn't mean you can't create a constructor body just like this as you can see when adding a body you don't have to use the semicolon anymore since dart treats it like an ordinary function in this case another important key difference between a constructor and a function is that constructors don't have return types they are simply instantiating an object of the same type as the class name is that simple now constructors were created so that the fields inside a class could be rapidly initialized inside of it in a more organized fashion note one important key aspect of this behavior when instantiating an object from a class you need to know that first and foremost the fields will be initialized with the values provided at declaration and only that the constructor of the class will be called this is important to know because you can actually set the fields you want to initialize inside the constructor with the already existing values that were assigned to the other fields before the constructor was called for example inside the constructor body you could assign the d field to the value of c since that's been initialized before the constructor got called now that we understand that let's get to all the paths we could take in order to initialize all instance variables of a class inside its constructor note that static variables cannot be set inside a constructor of a class since they exist without the class being instantiated they are class-wide accessible since this constructor is a function we can actually denote function parameters inside of it so for example would have a couple of parameters and we would need to assign our fields to these parameters inside the function body right now that since the constructor parameters are named roughly the same as our fields in order to help dart differentiate between which ones are what we're using the this keyword this keyword is mainly just like saying this class so this dot a is equal to saying the a field from inside this class note that dart is smart enough to know that we're talking about the field a if for example we would have had an x parameter in the constructor that would get assigned to a so this keyword is only used where there's ambiguity in terms of a name conflict apart from this you should definitely omit the this keyword also note that we need to unassign our normal final variables since we're not initializing them at declaration anymore right but rather inside the constructor of our class now we have a problem with our c field note that this is a standard non-late final field nothing fancy unfortunately what we know from the tutorial on null safety is that non-knowable non-late final fields must be assigned before the constructor's body so we cannot initialize it here and now you might ask yourself well if you can't initialize it at declaration and you can't initialize it in the constructor body where can you initialize it at the end as i said the constructor of a class is a special kind of function with special features one of them is called the initializer list the initializer list is located right after the parameters before the constructor body and can be accessed by using the colon operator just like this these assignments are actually executed before the body of the constructor is called therefore it's safe to set our c field here by assigning it to the c parameter now if we go ahead and take a look at where we're instantiating our alpha object the constructor expects the parameters we declared inside the constructor's header if we type them and run the program as you can see there will be no errors what's interesting about this initializer is that absolutely every field can be initialized here based on the parameters declared into the header of the constructor you can initialize all your parameters here and get rid of your constructor body completely note that in this case you won't be able to set other fields to the already initialized fields value the only way you could do that is inside the constructor body so we can run the program again and as you can see we receive no errors we can actually see all of the values from inside the class much faster than printing each and every one of them we can overwrite the tostring method as we have previously seen and print all of the class fields inside of it if we run the program again we can notice that our values got successfully initialized just as we wanted inside the constructor so we have currently seen how we can initialize our fields inside the constructor body and inside the initializer list right it's great that you actually know all of these because right now i'm able to tell you that there is an absolutely much simpler way on how you can do it without any parameters constructor body or initializer list you see dart comes with a really nice syntactic sugar when it comes to the field you need to set inside the constructor of a class instead of having to manually enumerate some parameters then having to assign the fields to those parameters and making sure not to have naming ambiguity and to treat the final fields right you could just simply do the following inside the parameters list of our constructor we can enumerate all of our fields we want to initialize by using the this keyword just like this this dot a this dot b this dot c and so on and so forth this way we're actually sending the fields directly as parameters to the constructor so that when we'll create a new instance of the class the values are going to be automatically assigned to the fields just take a look at how simple it is right now to initialize everything note that by doing this we're also able to initialize other fields inside the initializer list based on those we already set up inside the parameters list and for certain fields that are not simply final we can do the same inside the constructor body just as we saw before what's even more amazing is that since this constructor is at its core a function these positional required parameters here can be switched to named parameters by surrounding them as we learned with curly brackets note that at this moment all named parameters are optional by default so we need to make sure we set the ones that are final and can't hold the default value of null to be required by annotating them with the required keyword one limitation for private fields prefixed by an underscore is that they cannot be placed inside a named parameters list so we'll have to set them as normal positional ones before the named parameters list now if we switch our view to where we're calling the constructor we can see that we're able to set the fields of our class much more efficiently just like this this just looks like you're setting them out inside the constructor making the code way more readable and easier to understand most of the time in dart and flutter you won't have all of these kinds of fields inside a class but rather a bunch of different final fields fields that you're going to initialize in the constructor just as i showed you before of course there will be some exceptions in which you'll probably use some of the other tricks i showed you today but most of the time i have found myself working with plain simple final fields some of them late some of them not late and implementing the code based on those rules i explained earlier it's finally time to move on to another important topic related to constructors well until now we are aware that we have a default constructor offered by dart when we create a new class and that we can actually replace this constructor with one of our own in which we can initialize all fields we declared inside the class what you need to know is that well you can have an unlimited number of constructors for your class but wait you said that a constructor's name must have the same name as the class how can we have multiple constructors then well dart has this feature called named constructors in which by using a dot operator you can set up a constructor named as you'd like for example let's say we have this basic class right now containing two final fields x and y we have our own standard constructor in which we're initializing the fields to specific values but say we want a constructor that can directly initialize them to zero all you have to do is to get the name of our class and post fix it with a dot plus the name of your specialized constructor i'll call it zero since this is all it does this constructor now has the same properties as the one above but what we want to do as we said is to initialize our x and y values with 0 by default so we're totally free to use the initializer list for this purpose just as we previously saw if we try to instantiate an alpha 0 object this time we can do it by calling our newly created constructor by typing in a 0. that's all it takes and if we run the program you'll see that it works indeed as expected what i can tell you from experience is that you'll mostly find specialized named constructors when trying to parse json data into one of your classes say for example we have a json which as we learned in the dart built-in types tutorial can be actually represented into a map so then we can create a named constructor for our class named from json and pass our json map as an input parameter then having this json we can use it in our initializer list to assign our fields just like this now all we need to do is to create another object of type a this time by using the a dot from json named constructor we'll pass a random json as a parameter and as you can see if we run the program it works just as it should be of course i should be treating some exceptions that may occur if there's no x or y inside the map but this tutorial is getting really long so i'll leave them like this having named constructors sorted out another important feature of dart class constructors is that they can redirect their calls to other constructors from inside the class for example say we want a functionality so that we can have 0x and 0y objects meaning that we can only set the y or x values respectively since the x and y's will be by default 0. as a result we can create two named constructors one called 0x and one called 0y the 0x will take only an y parameter and will call the original constructor by setting only the y value since x will have a default value of 0. the 0y on the other hand will do the opposite it will only take an x parameter and call the original constructor by setting only the x value since y will be by default set to 0. as you can probably tell those redirecting constructors can be achieved by using the column operator if we go ahead and create two objects with both of these constructors we can see that they both work indeed as expected now looking back over previous tutorials when we discuss the difference between const and final i sort of introduce you to how you can create a compile-time constant object by converting its constructor into a const one say we have this specific scenario a cartesian coordinate system on which we want to represent a couple of points on so each point will have an x value corresponding to the x axis and a y value corresponding to the y axis now you might imagine that whenever we'll instantiate points like this if we have 10 points having the same x and y-values we don't want 10 different objects that won't be efficient at all we want 10 variables linked to a unique object in memory in order to achieve that we need to mark the constructor of our class as being const note that cost constructors will only work with final fields so that's why we try to use as many final fields as possible in order to optimize our code as much as possible now as you can see if we create two points having the same x and y coordinates and call the identical method on them we'll see that it returns true meaning that the variables point to the same constant point in memory one more tweak we could add is to create an origin point inside our class since this is still a constant point we'll have to declare it cost but remember that the only fields that can be constant in a class are static fields so we'll also have to declare it static i want you to note one really important fact though if you're not going to mention the cost keyword when you're trying to create a new object by preceding the constructor or here instead of var then the object that's going to be created will not be constant you really need to pay attention to this so that you won't waste so much time trying to optimize the code for nothing just because of a small mistake as with another example let's say we have a list of points depending on your scenario if you have multiple cast constructors on the right side of the assignment like in our case it's more optimal to only write it once in the left part this will make sure you will still create constant objects even though their constructor is not preceded by const having this said it's time to move over to another really important topic related to dart class constructors and that is factory constructors until now all of our constructors returned an instance of the class we declare them in right i mean this is the sole purpose of a constructor to construct right indeed it is but what if we want to have more fine control over how it can construct these instances let's say for example that we want a constructor that will generate a random point instance based on whether a boolean is positive field is true or false if it's true then it will generate a point having both x and y values positive and identical if not well the opposite so if we try to do this with a normal constructor let's go ahead and create a new named constructor and name it random after writing all the logic you'll end up noticing that you can't return anything inside a normal constructor so what can we do in this case we'll just have to convert our constructor into a factory constructor by preceding its name with a factory keyword so as you can see the factory keyword relaxes the rules of the normal constructor by letting us have access to the return statement however note that a factory constructor doesn't have access to this keyword so you cannot initialize fields inside of it note that thanks to this approach we can now return things that aren't directly related to the creation of this class instance so we don't always have to write return point here we can have a return origin which we know is a field containing an instance of a point or we can return another constructor that we know is going to end up in constructing a point instance this is the kind of freedom factory constructors give us note that at the end the evaluation of a factory constructor must always end up in returning an instance of the same class it was declared in or perhaps one of its extending classes so you can't have for example a return string here since string isn't actually the point class and string doesn't extend the point class factory constructors can also be a fantastic way to implement the singleton pattern for a class denoting that there should be only one instance of a specific class created in the entire program so let's go ahead and see how we can implement that by using factory constructors we'll go ahead and create a class then the default constructor of a class needs to be a factory one inside of this constructor we would only need to return the only one unique instance that can exist this is going to be a private final static field since it will be only assigned once this instance field is going to be assigned to a private constructor setup for our class so whenever we'll create two singleton classes by using the factory constructor dart will only return the unique instance of them and we can check that by using the identical operator now you might be wondering what's the difference between this and only writing a constant constructor for this class well it's simple as i told you before creating an object just like this even though the constructor is named cost won't create two identical constant objects by default you need to prefix it with the const keyword whereas with the singleton it will create only one object and all variables will reference it if needed having all of these said there is a little bit more to talk about how constructors behave in the context of class inheritance when one class extends other one but this advanced topic will be covered in the second part of this tutorial about classes now that we discussed the most important method you need to learn and understand let's move over constructors and talk about the rest of the methods you'll be able to code inside a dart class we'll start with instance methods i'm sure you already know what instance methods are they are literally normal functions serving specific purposes in a class there's not a lot to talk about them since i pretty much covered everything you need to know in the dedicated tutorial on dart functions so if you want to refresh your memory go ahead and rewatch that tutorial once more other methods that we have discussed in the past are operators operators are actually instance methods with special names so instead of writing the sum of two points like p1 dot add p2 we can create this plus operator and print p1 plus p2 directly just like we were adding two integers the same applies to the minus operator and with the other operators you can override or create inside any of our classes moving on let's talk a little bit about getters and setters which surprisingly are also methods that can be found inside a dart class note that we discussed them at the beginning of this tutorial what you need to understand is that whenever you access a field from a class you're actually calling a getter function on that field you might be used from other programming languages to create a setter and a getter for each instance variable you declare in dart each field inside the class has an implicit getter just like every class has its implicit constructor it's invisible but it exists we have also discussed that in some of the scenarios you can also set a variable outside the class this was achieved by having a default implicit setter method so for our x and y fields for example have two getters looking exactly like this but they're as i said invisible what we can do is that we can create additional getters and setters that can use the already existing fields to calculate different values just like this say for example we have a car class containing an age field we can use a setter on the manufacturer here so that when we'll set the ear of the car to something we can actually also set the age by subtracting the ear from the current here there are some moments when some getters and setters might bring more value to the code so you should definitely spend a little bit more time practicing with them and moving on as expected since you can declare static fields you can also find static methods inside any dart classes similar to static fields static methods exist without needing to instantiate any objects from that class as a result of that static methods can interact with the static fields inside your class and they won't have access to this keyword therefore instead of calculating the distance between two points as we did before by using a classic instance method we can use a static method and place both points as parameters then follow the same algorithm as before as you can see if we go right ahead and test the functionality of this static method it's amazing that we have two methods performing the same algorithm thus giving the user more flexibility to achieve what they want and i think this is pretty much all i wanted you to learn from this tutorial on dart classes in the next tutorial we'll move over to the next part on this topic in which we'll discuss more advanced concepts related to dart classes so i'm looking forward to seeing each and every one of you up next as always if you like this tutorial don't forget to smash that like button subscribe to my channel and share the video with all of your friends and colleagues in pursuit of top tier development until next time as always take care wicked is out bye bye
Info
Channel: Flutterly
Views: 15,397
Rating: undefined out of 5
Keywords: dart, dart tutorial, dart classes, dart fields, dart instance variables, dart methods, dart constructors, dart named constructors, dart default constructors, dart redirecting constructors, dart factory constructors, dart operators, dart getter, dart setter, dart static fields, dart static methods, dart singleton, dart singleton pattern
Id: 7rfehxYBukk
Channel Id: undefined
Length: 37min 15sec (2235 seconds)
Published: Mon Jul 26 2021
Related Videos
Note
Please note that this website is currently a work in progress! Lots of interesting data and statistics to come.