1z0-808 Oracle Java SE 8 Programmer – Polymorphism part 2
- Benefits of Polymorphism
So to help solidify the concept, let’s look at yet another example. Let’s imagine that we’re part of a very large software team and we’ve all been tasked with developing an accounting system and this is for an It company. So one of the things that we’re going to have to know about in the system are the different kinds of employees. And so for our system we really have three different the kinds of employees. We have managers, instructors and programmers. So we come up with a hierarchy here of a person at the very top an employee extends person and then manager, instructor and programmer extend employee. So pretty simple object hierarchy but makes logical sense. Each of the different employees is going to be paid a little differently. And so this is where overriding methods is really going to shine.
We’re going to see the power of overriding methods. We’ll also have some kind of component that cuts a check for each of these employees and that’ll be the pay service. So because this is such a large project, we’re going to split up the work amongst the team. Some team members are going to create the employee subclasses and they’re going to have to figure out how that employee should be paid. So what they do is they start looking at their hierarchy and they say why don’t we override a method called pay that’s been defined in the employee class and that way each of the different subtypes can determine how they’re paid. And that’s great. Some other people decide that they are going to create the pay service class.
So there’s a lot of ways that the pay service class could be built. Maybe we have a cut check method for instructors, a cut check method for programmers, a cut check method for managers. But of course we know better. We’re aware of polymorphism so we can create this kind of a method cut check employee. And what we’re saying polymorphically is that we’ll accept an employee or any subtype and what’s a subtype of an employee? Well, programmer, instructor and manager. And then what we’re doing is we’re going to send a message, a single message and then that message is going to be handled differently depending on the subclass. So in every case an employee will be passed a cut check and we’ll say pay, what is your pay? Tell us and we’ll cut the check for you.
Wouldn’t that be great? We could just start determining our own pay but just go with the example for a second. So we say e pay and that gives us some sort of value and we cut the check. There’s a phrase you’ll hear in software called roughly speaking, it’s called separate the things that change from the things that stay the same. So how are we doing that here? Well, what changes how we pay is not what changes. We’re always going to be paying the same way. We ask an employee for their salary. We say what’s your pay? And then we cut the check that stays the same. What changes is what the actual pay is, the implementation of the pay method. So again, that’s the power of overriding methods. We can send one message and depending on what kind of employee has been passed into this method, they can react differently.
So the developer of the employee classes, they’re going to have to override that pay method to determine how each employee will be paid and it has to be the exact same interface, the exact same method. Otherwise we would have to write ourselves in the pay system. We would have to write different methods for each different employee type. So they’re all going to have to agree on that method pay, they’re all going to have to override it. So now here is what we’ve got for our employees. Now the very top we’ve got our public class employee which extends person and they define the method pay. And so right now we don’t really have what would be a generic employee. So we’re just setting up this method for the interface.
We’re setting this up because if we did not have the method pay an employee, then in our cut check method we couldn’t call pay. Now think about that, what we were doing in cut check, let’s jump back a little bit here. In cut check we were saying we take an employee and so then we’re going to call the method pay. And remember the rule about polymorphism which is when we have a reference variable, we can only send messages to it that are available to this type, that are available to employee. So what’s available to employee? Is pay available to employee? Well, jumping back to that code, here is the pay. So there it is. Yeah, I mean it’s there we can call that method. Now obviously if this was the method that we were calling directly, everyone would pay zero and no one would be happy.
So instead every class that extends employee is overriding pay and they’re coming up with their own computation of how they determine what their pay is. So let’s say then someone creates a manager, passes it into my method and I say e, pay. Well, it’s going to call this version of Pay and so on. Now, the one thing that I’m not super happy about here is what if someone made the instructor and forgot to override pay? Or maybe they made a little mistake and made it a capital P. Okay, that’s a different method. Remember that everything’s case sensitive in Java. So if they then created an instructor instantiate an instructor object, passed it into our cut check method and we called Pay.
The dynamic method dispatching would come in here and say instructor, do you have pay all lowercase? And the answer would be no, I’ve got Pay with a capital P. So we’d say no, that’s not what we’re looking for and it would go up to employee and say do you have Pay? And the answer would be yes, we do. And what is it? It’s zero. And now we cut a check for $0. That could be a problem. But in the next section we’ll talk about abstract classes and interfaces and we’ll see how we can kind of get around this issue. But for now we’ll leave it. Another benefit of this polymorphism is really the flexibility. So now we can actually change a pay method without changing any other code and that’s another software Idiom, I guess another software best practice in that we should try to isolate our code so that when we make changes we’re only making them in one place.
Anytime we open up some code and make changes there’s the chance that we introduced a bug if we don’t touch it that limits and reduces the amount of bugs for that particular system. So now when I want to change the pay for some particular object then I can go directly into that line of code and change that salary only. In other words, I could go to the programmers and just change how a programmer is paid and I wouldn’t affect any other type. So here I’m going into my programmer and now I go into the Pay and I just changed what it is. Did I affect the manager? No. Did I affect the consultant or sorry, instructor? No, it didn’t affect them either. So everything’s nice and isolated in this way.
How about pay service? Did I have to change the pay service? Nope, didn’t have to change that either. Everything is nice and compartmentalized so we can just fix the code that we need to fix. In addition, let’s say we get a brand new kind of an employee. It’s going to be really easy to fit them into the system. Let’s say a Tech Writer position was added. So now we create this Tech Writer we say that they extend employee, we override the pay method and now they just work in the cutcheck system. We didn’t have to make any changes to cut check. It just works with this new Tech Writer and that’s what I mean when I say you’re future proofing your application. We are making sure that as we add new employees they’ll just work in the system.
- Object Type Casting
Several sections ago we talked about casting as it relates to primitive data types and we saw that it was what we needed to do if we had a value that was larger than the reference or larger than the variable that we were trying to store it into. So if I had a byte variable and I was working with an int, then I would need to cast that into a byte and we did that with the parentheses. We can do the same thing with objects. And the idea of the objects is that we take a more general type and cast it into a more specific type. So let me show you an example. I’ve got a person p equals new employee. Now remember, the rule of polymorphism says that while we are working with that p variable, we can only call methods and look at variables that are available to the person type, not the employee.
Even though at runtime we’re really sending the message to the employee, it’s like we got blinders on. We can only see the things available to person. So I can do things like p Kit description and if employee happens to have overridden Kit description, it will behave like an employee. But again, we can only send messages or invoke methods that are available to the person type. So what if I wanted them to treat p like an employee? Well, this is what we can do, we can cast it and it looks just like casting our primitives. The only difference is we say the type. So I’d say p and to the left of it we would cast it into an employee. And now I can store that in a variable and work with that variable and it’s an employee.
Now, the compiler will not complain if I say things like e title or e promote. All of that will work. There’s an example I’m setting e dot title to instructor and it’s legal because title is something that’s available to any types or any objects that are of type employee. So the reason we did this typecast is really to get functionality back into the reference so that we can treat this employee we created as an actual employee. But like any kind of a variable, if we don’t really need to keep the variable around for future work that we need done, why keep a variable around at all? So, one of the things we can do is shorten our code a little bit. So in this case here I’m creating the same thing person p equals new employee calling out p get description.
But rather than casting p into an employee and keeping that employee e variable around, I’ll just do the cast and the title switch all in one line. And so what we do here is walk you through this. We take our reference p and we cast it to employee. Now, if we didn’t have the extra set of parentheses surrounding the whole cast, then this would still fail, because what would happen is P title would execute first. So you have to think about your operations and the precedence of operations. So without the parentheses, p title would be executed before the cast and that would fail. So we put the whole cast in a set of parentheses to ensure it happens first, and then we set the title to instructor, and then that’s it.
P is back to being just a normal person object, even though, again, at Runtime, the messages we send will be received by an employee.You do have to be pretty careful, though, when you are casting, which is similar to primitives. We saw that if you cast a really, really big number into something small, that you would get you’d have a problem. You wouldn’t get the value that you would expect. Well, what happens here? Let’s say I’ve got person, p equals new person, and now I say, let’s cast P to employee. Casting doesn’t actually change the object. It changes the reference. It just says, I want you to know what you’re really pointing to. That’s all that it’s doing. But it doesn’t change the actual Runtime object.
So when we try to do this at compile time, it actually says, okay, because what’s happening is the compiler trusts you. It says, listen, you’re telling me P is an employee. I’m good with that. Let’s move on. But at Runtime, now when it tries to actually do the cast, it’ll look and it’ll say, wait a second, p is not referring to an employee. P is referring to a person. I can’t do this compilation, and you will get an exception. You will get an exception called a class cast exception. There it is. The above code will generate a class cast exception, and we’ll talk about exceptions a little later in this course. One of the ways that we can do what’s called a typesafe casting is we can check to make sure that it’s legal.
So there is another operator, it’s called instance of. It’s a boolean operator, and it just returns true or false. And what you do is you take your reference, you say instance of, and then you check a type. Now, since it’s returning a boolean, we can put this in an if statement. So if P instance of employee, we now know that it is an instance of an employee, then we will do the cast. And we know we’re safe because we checked it before we tried to do the cast. And that will prevent the class cast exception. The instance operator is used for a lot of things, not just casting. In fact, we can use it to do some conditional code based on the type of an object.
So let’s say that in our example, the CEO of the company has decided that everyone gets a bonus. It’s a one time bonus, but it’s not available to managers. It’s available to everyone else. So what we could do is use that instance of operator to make sure that we’re not giving it to a manager. And so now our cutcheck method could look like this. Here we’re checking is e an instance of a manager that’s going to be true or false. This exclamation point, the bang, as they call it, that means we want the opposite. So if it’s not an instance of a manager, then we will award the special bonus. Otherwise then we just keep doing what we were going to do.
- The Object Class
We can see another example of the power of polymorphism from two very commonly overridden methods. One is the twostring which is used to convert objects to strings. And that should seem pretty familiar. Everyone that’s done the labs here has overridden the twostring method. There’s also a method called equals that’s used to compare to objects. So both of these methods to string and equals, they’re defined in a class called Java lang object. So yeah, there is in fact a class called object. Now here’s the interesting thing of how that’s applied in Java. Everything in Java, except for primitives, is an object, literally. And what we mean that by that is that if you were to go up the inheritance chain for your classes, you would see that at the very, very top is Java laying object.
Everything eventually extends that class. And how is that possible? You’ve written lots of classes where you didn’t extend Java or you didn’t extend object. And what happens is that even if you don’t extend it directly, as long as you don’t extend anything, then you automatically extend Java lying object. So think back to the My Date class that you created or the car. We weren’t extending anything. So underneath the covers we were in fact extending Java lying object. So let’s look at this example of a person class. We’ve got this public class person. We’re not saying that we’re extending anything, but that’s really the same thing as saying public class person extends object. So therefore every Java object is in fact an object with a capital O, whether you like it or not.
So since every class is going to extend object, that means every class inherits from object as well. And two of the methods that they inherit are two string and equals. So whenever we implement those methods, what we’re really doing is overriding the methods. So even our My Date class gets these methods and they can be used with any of our My Date instances. Let’s look at a couple of examples. Here’s our main method. And in our main we’re creating two My dates. So even if you didn’t override two string or equals, they’re available and I could say party two string and see what it prints out. And party equals tax due. So we’ll look at each of these and see what happens.
What? And you don’t override the methods. Now let’s again take a quick look at what we’re passing in here. So we’re passing in the same date, april 15, 2000 to both party and tax due. So what do you think is going to happen when we say party dot equals tax due? Clearly, equals is a method that’s going to test equality. It’ll return true or false? Will it return true? Will it return false also? What do you think is going to happen when we say two string? Again, imagine that we haven’t overridden the method two string. Well, if we haven’t overridden the methods, they’re not really going to do a whole lot. By default, two string is just going to create this string with the name of the type, the add symbol, and then a hash code.
Hash code is often used to look up the object reference on the heap. So for example, if our My Date is at memory address 1234 FF one, depending on the Jvm implementation, the hash code might be that address or it might be something else. It just depends on the implementation. The equals, on the other hand, is just going to be defaulting to the equals equals operator. So we have two my date objects. Since they’re actually different My Date objects, what’s going to happen when we call the equals? Well, in that case it would return false. So this is what we get. We’ve got the name of the class, My Date at and then whatever its hash code is. And the comparison of the two objects is false because even though they had the same April 15, 2008, they were two separate objects, right? They were different objects.
So are they equal? Not by the default equals equals operator, no. So we tend to override these methods. Overriding two string is pretty easy. Everyone here that’s done, the lab has done just that. In the case of My Date, we probably are just going to grab all the instance variables, the day, the month, the year, concatenate it into a nice date string with using the slash and return. That whenever people ask for the two string of our My Date. There it is, we’ve got our My Date and there’s our public string two string method where we’re concatenating month, day and year. So that’s something that everyone that’s done the lab, you’ve already done that. So now when we call the method two string on the date object, it’s a little bit more friendly.
Here’s an example. We’re creating the Tax Do object and then we do a system out print line taxdu two string. We get a nice date that’s formatted as April 15, 2017. As an interesting side note, every time we’re using the concatenation operator, that plus and we concatenate with some sort of a reference, that what it is doing, is it’s calling the two string for us. So here we’ve got a my date called Java Release. The date is March 18, 2040 2014. And so when we concatenate a string with that Java release and we print out this new string, what we’ll get is Java se eight was released on and there’s our two string format. And that really demonstrates the power of polymorphism right there.
That plus the concatenation operator. Java knows that if we’re passing in an object reference that it is perfectly safe to call two string because one or two scenarios have happened. One, we’ve overwritten two string, in which case we’ll give a nicely formatted string to be used when we’re concatenated with another string or even if we haven’t overwritten the method. It’s been defined in objects. So at least somebody can answer the question, what’s your two string? And that’s exactly what’s happening. The String class knows it can safely call two string on any object because every object is of type java lang object. And the power of overriding methods is that we can now override the two string and generate any kind of a two string that’s appropriate for our type.
And I keep using this term future proofing. The String class was, in a way, future proof. They had no idea that a My Date object would work or ever exist. And they had no idea that we would override the two string method. But they said, it doesn’t matter if you do or not. We know we can call two string and somebody will answer the method. So that’s two string. Let’s look at equals. Equals is a little bit more work involved, as we saw comparing two objects using equals equals as saying, are these the same object on the heap? And the only time it’s going to return true is if they are actually the same object in memory. So when we created the two My Date objects, even though they have the same dates, they were not equal in terms of equals equals that would return false.
They are different objects. So what we can do is we can override the equals method and use something that’s a little bit more meaningful. So here’s the method. It’s public bullying equals and it accepts an object. Now, why does it accept an object? Because when this method was created, they had no idea who would be overriding it and what type would be passed in. So once we get an object passed into this method, we’re going to have to cast it to the actual type we want to test against. So this is in my date? You can imagine I’ve got two My Date objects. I’m going to pass one of the My Date references into the other’s equals method.
So here’s the equals method and it accepts an object o, checking to see if O is an instance of My date. And if it is an instance of My date, then we’ll do the cast. And then we’re checking to see that the day that we just got passed in is the same as our day. Checking to see if the month that got passed in is the same as our month and the same thing for the year. And if it is, then we’re equivalent. We may not be the actual same object, but we have equal values. And so in that case, we’ll return true otherwise, since we set up our bullying result as false by default, then it would return false otherwise. So now that we’ve overridden two string and equals, we can see it in action here. We’re creating the two parties. Excuse me? The two my date objects.
One is a party one is a tax due, they’re of the same date. If I check to see if parties equal equal to tax due, the answer is no, that’s false. They are two different objects. But if we say if party equals and we pass in the second object to the first. So here we are taking one my date calling equals on it passing in the tax due. That’ll be true. And so it’ll say the dates have the same day, month and year. And below you can see that that was the output. One last side note whenever you override equals, it is considered necessary, it’s part of the contract that you should override the hash code method as well. So the hash code method is something that’s defined in Java lang object.
And what it does is it returns some kind of an int to identify the object. It’s used very frequently when you’re storing objects in hash data structures. So we’ll be talking about collections a little bit later and I’ll show you a couple of hash data structures at that time. Later we’ll see that that hash code is used by these structures. Basically to say are these two objects in the same compartment? Doesn’t necessarily mean that they’re the same object. If I’ve got two objects and they have the same hash code, that doesn’t mean they’re necessarily the same object.
It just means that they’re in the same compartment. So the general rule is if two objects are considered equal based on the equals method, then they really should return the same hash code when you call the hash code on each of the different objects. But what’s interesting is that the converse is not true. So if I’ve got two different objects, they don’t actually have to return different hash codes, though they typically do. So there’s the method we would override public and hash code. We’d have some sort of a computation of how to generate the hash code and return that. And in fact, let me show you an example of a common use of hash code.
We would often have to implement a fairly complex algorithm to generate the hash code. But in Java seven there was a class called objects and that’s plural objects. And it has a static method called hash and you can pass in a bunch of variables into it and it will then generate the hash code for you. So the way that we use that is we figure what are the different things that we are comparing in the equals method and with the ones that we’re comparing, then we pass those same variables into objects hash. So here’s a common implementation these days. Public int hash code return and we call objects hash. And then we grab all the values that we tested in the equals method just to keep them in sync.
- Polymorphism Lab
Polymorphism is a feature that makes our applications much more flexible. And so in the previous lab, we created our good, our liquid and our solid. Now we’re going to use polymorphism in our order to be able to say we’ll accept any kind of a good. So the instructions for this lab are in the resources section for this lecture. It is lab ten polymorphism PDF. If you have any questions, let me know. And also remember that there are solutions in the lab instructions near the end. And there’s also a bonus past the lab solution.