 Hi, this is Christian. In this tutorial, I want to show you how and why converting one object to another object is so important in Java using this process called widening and narrowing. So if you are still new to Java, I hope this video will help you understand conversion work and also how to convert objects and when we deal with inheritance. So first of all, here I have a list of all the data types that deal with numbers in Java. And there are seven types as you can see here. A list here from the smallest size, which is a byte, all the way to the double, which is the largest size. So when you convert from the smaller size to the largest size, we call that upcasting or widening. And if you go to the opposite direction from large to small, we call that downcasting or narrowing. And so a smaller size will always fit in the larger size. So for example, I put here, the byte takes about, you know, a one byte, which is 8 bits. I can fit all these 8 bits inside a short, which just takes two bytes. Okay, if you're going down from the short to the byte, well, it doesn't fit, so therefore you're going to lose some data. Okay, so therefore you have to cast or type cast from the short down to the byte and therefore you're going to lose some data or you might get a really different result because if any number that is larger than the byte can hold, then it will break the program. Okay, so let's do an example here. So let's take the integer i. So when you do upcasting or widening, then the conversion happens automatically. You just don't see it. So this is the same as saying I'm going to cast the byte to the int type. Okay, however, if I do the other way around, say if I go and reassign that with the double, for example, right, you see a double is much larger than the integer. So therefore it doesn't work. And so in this case, you have to downcast from the double down to integer or something that the integer can store either an integer or something lower the integer. Okay, so you will have to explicitly cast this to the int type in order to fit into the integer. It doesn't have to be int. It could be, you know, byte. But remember that with the smaller you downcast, the more data you're going to lose. So that may be a problem. But anyways, just to show you that it does work as long as the type is equal to or less than the type that you are assigning to. And that's the whole logic, the whole idea here. And so up here I had three functions, overloading methods, right, the same function name, different return type doesn't matter, but you have different unique signatures. Now, these are, you know, useful in this case, but also could pose a problem, especially we have ambiguous data. Okay, so for example, if I go down here and I want to call the function, any one of those I got to pass in, I see a byte will be a be a short and end. Okay, and then if I want to print this out, of course, I would just put P here, I have a function to print this out. And by the way, okay, so like, I think down here at the bottom, yeah, I print this out. And this I just put here just to show that I use all the variables. So I don't have that, you know, ugly yellow squiggly line underneath these letters. So here if you print this out, and you're going to see that the result will have what I expect, which is a three, let me put on the right side over here. Okay, so I got a three, which is perfect. So it works fine because it knows that the byte in this short and integer fits into this signature. All right, if I go int, int, int, then I put three I's here, right, and then it will call that one. And then again, there's no problem there. However, if I go, for example, if I go short, short, short, right, anything smaller than int, you see that it's a problem now is the problem is ambiguity. Okay, so it's ambiguous. It does not know which one to call because short does not fit here. So this won't work, but short can fit in all these slots, and short can also fit in all these slots here. So therefore, these two functions, you know, pose a problem. So therefore, just be careful when you do this, you have to deal with ambiguity. But I just want to show you that typecasting can allow you to make this work. So to make this work, you really can, you know, change this function to a single function, make it all double, right, all double. And if you don't really care about, you know, the result, all these do is just basically return the sum of one more. So for example, if I turn this off, okay, and if I do it correctly, and just use that single one here every turn double I used to double the largest size, then it'll work in every case. It may not serve your purpose, but it will serve your case. If you want to use a integer back instead of double, you can cast these, right, you can cast these, I want these to be converted to end like that. And now you have your integer type, but you're calling the same function that feeds in all the data types here you invoke ambiguity. Okay, so that's another thing I want to share with you. Okay, so now let's move on to the classes. Okay, the classes, in order for this to work, you have to understand inheritance. Okay, inheritance, let's take a quick look at what that means. So what I have here is an example of inheritance. Here I have a class called person, a one-coin employee and in the manager. And over here on the right side, I have a person and a client. Okay, so the arrows here just mean they're pointing to the parent class. Okay. On the left side, this diagram kind of same on the right side, it's just the size of these classes. What I want to talk about size is I'm talking about the number of attributes and methods a class can contain. So the person class can contain only anything that is always less than or equal to the employees and so forth. So I put here the sizes of those classes here. And if you look at the different layout, like the triangle here, I put numbers in here, one, five, ten, just to show you that different sizes. Okay. So if you look at this perspective, that's how it is. Let's look at the another perspective. This is also inheritance, but now we are talking about widening and narrowing, or I guess if you think about the hierarchy, then the ranking. So the person is the highest in the rank, so therefore it's going to be larger than as you go down. It's the opposite of the size. So I want to show you here is that this inverted triangle here to show you the ranking from the largest down to the smallest. Okay. So when we convert data or objects in this case from the smallest up to the largest in ranking, it's the same process. We call it widening. And when you convert from the larger and ranking down to the smaller, it's called narrowing. Exactly how it works with the numbers we just did earlier. Okay. So if you understand this logic, you will have a good understanding why it works in Java when you deal with inheritance and also dynamic polymorphism and dynamic binding in Java. Let's take a look at how this works in Java. So back here in the IDE, I'm just going to explain a little bit what happens here. So here I have three classes down the bottom. Class A, B, and C. A is the parent class. Class B extends A, the child class of B, M of A, and C is the child class of B. So it's the three generations here. Okay. Now each function, each class has a function called print. These are known as overriding functions because you're overriding the parent class function here and B is overriding A's function up here. So this is what's known as the dynamic binding or sometimes it's just dynamic polymorphism. So up here I have three functions, the same name, different signatures. So again, this is known as the method overloading. Okay. These are known as static binding. So anyway, so when I call up here the function calls, I create three objects of their own type. I call the function show and I pass that type to it and Java will know which type to call because it's going to match the same that signature. So when I run this program, it should print A, B, C as expected over here. Okay. So what is upcasting, downcasting mean? Just like the integer and flow index that we did earlier say that one thing that you cannot do in Java is notice that in a number over here, if I do, like I can go from double down to int, right? No problem. Like here, this is the double. I convert to an int. No problem here. An object, you can't do that though. I cannot go and put B is equal to A. Okay. And then, oh, okay, one way I can do that, right? Downcast A to B. It appears to be finding the IDE. However, when you run it, it's going to say you can't do that. Okay. You can see that it says you cannot cast. It's kind of hard to see, but you cannot cast A to B. Okay. So this one here doesn't work that way. The only way it works is if this A object was created using B's constructor or B's class in that case, then it's fine. So if I go up here and change this to a B, then it's okay because now you will see that it prints both B's functions and you don't get a function anymore. When you do it this way, we have a higher class that uses a lower class to its institution object. This object is a reference to the higher class. So the type is going to be A. But what this happens is that any function that in the B class has that overrides the A class will be bound to that object. So therefore, that's why when I call the print function down here, when I create an object, this function now binds to the object A and not this function here. Anything that is not bound here is not overridden here. It will be accessible like the bet will be accessible, but the rest will be accessible to the A object now. So now if I print it, you're going to get the same thing, right? B, B, C. So you can't print A anymore because we lost the connection right here. So because B is the same level as B down here, therefore I can, you know, downcast A to B like this. You have to downcast so you can just do this. This won't work either, right? You have to downcast because the type is still A type. Because of this reference, though, it'll work. As long as this class is the same as the class I'm downcasting to, then it's okay. Now if I try to do, say if I do C instead, right? Will it still work? Sure it does because C is lower than B, right? So again, if you run it, you're going to get the output of C instead of B installed because initially it's C. So now C has the binding to the print function. When I reassigned downcast, you know, A to B, initially it's still bound to C. So again, it's still intact. And then if I have another one here, C is equal to A, right? Again, you have to downcast to that level. Okay, it must be C. So now they're all C's like that. And upcasting, you don't have to do conversion. So let's say A is equal to one of these. Let me do this. A can be anything. A can be B, no problem, as you can see, right? No problem here, even though A is up here. And also could be C as well. But once you do that, it bounds to that instance, right? That function. That's called the dynamic binding. Okay, so upcasting is automatic. Downcasting, you might have to explicitly do that. And this is a really important example. As you can see, even though I upcast, downcast, they will still work, right? I could have so that I can have only one function turn this off. And I just passed the highest class to this. I mean, I used the highest class here. It will capture all three types. It doesn't matter what type it is. People will see the issues still work just fine. No problem. So using this example here, let me show you something a little bit more practical and another example. Let me close these. And let me open this program here. I'm going to show the person class, the employee class, the manager class, client, and the main program here. The helper is just a function that a class that contains all the functions and the program. I want to use like print details and display menus and things like that. But the person class is the highest class. It has three fields and has a function to print the details. Okay, I am basically overriding that default two string. The employee class is the subclass of the person and it had three additional fields. And again, overriding the two string function. The manager is the subclass of the employee, has one additional field and then again, two string overrides everything here in this class. The client class extends the person. So it's the same level as the employee, the both extended person. And so it has two additional fields, project and budget. Again, the overriding methods here. And then finally, the program starts here. Okay, so it's just really simple program basically to ask what type of employee or person I want to enter data. Let me run this, you can see. And so on the right side, you can see here I can select, you know, E for employee and then I can enter first name, you know, last name and so forth. And then just put some salaries here and then done, right? And then go to go to the next one and so forth. If I quit, then it shows the data enter for each of those types. Okay, that's all this one does. Now what I want to show here or the benefit of using upcasting, double casting of all these inheritance thing is when you enter data, you write your code. Let's go down here to show those functions. So I have the function to enter the, yeah, let's not do this one yet. I want to show you why that was important here. I have here a function called enter employee data to enter data for employee. You print last name, first name, email, so forth. I have one for the manager. I pass the manager to this function. I get information here in the same for the client. Okay, so you can see that there is a redundancy here because these three fields are exactly the same in all three functions. Okay, so that's redundant. And then you look at further, you see that these three fields are also repeated in the manager class two or function two. Okay, so there's a lot of redundancy here. So you may be thinking how can you write a single function that will remedy this problem. Okay, and you may be tempted to do something like this before I show the other one. So this function here, as you can see, I call enter data. I pass an employee manager and client, and I do an if block. If is not known, then use the employee to enter its data, the M for manager, and for that. And so by doing that way, then I can turn these three off because they're repeated. Then I will use the enter data, a pass and the employee object. Then I use just no value for the other two slots. Okay, and do the same down here. So again, put it here, turn these three off, and then I put here no, and the employee here. And then one more, this will be manager, sorry. One more down here, these three will be gone, and I put it here, and I have, you know, no, and the last one is the client. Okay, so if I run it, it should still work just like before. So you go employee, right, no problem. And just like that. So I do a client, right, see for client, project would be whatever it is. Okay, so it works fine. But I'm not doing anything different here pretty much. I basically just remove these six lines or right to another function, they are still here. It actually added more lines to it now, right. And I use this, you know, if block here. What if I have another object, like user, then I have to go in here, add another one, if user is not empty, whatever it is, then do a U, that something, something again. So you can see that doesn't really help to solve the problem. You just want to have it so that one function can enter these data all together. And that is where this function comes to the picture here. It's up here. This is the interpersonal data. I use the person class because it's the highest in the hierarchy. And this person class has three fields that are common to all the other subclasses. So you put it here. By doing this way, I don't have to enter all those information again. So now, instead of calling the enter data, I'm going to call the interpersonal data and then just enter, pass in the employee to that slot. It'll work because it'll fit inside the person, right. Same thing down here. I'm going to change this to just personal data and then pass in the manager object. And then, yes, well, as you can see, no problem, right. And then what's even better is, again, I mentioned earlier that these three fields are repeating again here in the manager. So what you can do is, OK, well, I can call from the manager class. It's a subclass of the employee class. So I can call the employee data and set up the interpersonal data. Call that instead. And I move that down here. And then we turn all these three off. So now you see there are only two lines in here because this is the only class. This probably is a unique to the manager. I call the employee data a passing manager and then it goes to employee slot, just a variable name. It passes it to interpersonal data and then I enter these fields are common to both employee and manager and so on. So now you can see how many lines of code I have eliminated, right. If I put like three here and I have six here, so that's nine. And then 12. And then minus three here, of course, there's still nine lines. So now it works better because I can add more and more object and I keep passing to the superclass and use its data instead. So instead of having to reenter these many, many times. And if I run, it should still work just like before. I hope. Let's see. So E, let's go. Right. That's fine. And then for manager, just put M here. Okay. And then bonus and then for client budget. And then there it is. And the print message, the result, you have each of those type. Okay. So the print function, I use something kind of similar. So up here when I print the result, notice I call the same function print details, print details, but I pass in a different type. Okay. Different object. And if I go to the helper function over here, you see that the function here takes a person array of that person type, persons. And whatever I pass to it will fit into this slot. And so you just have one function to print all the data you need in here. Okay. So I hope that this will give you a really good understanding of how, you know, upcasting though casting works and also how dynamic polymorphism works and dynamic binding works in Java. If you have any questions, please feel free to post comments below or any suggestions are always welcome. Thank you for watching. I'll see you guys in the next video.