[music]. In this segment, I want to continue our study of subclassing with two classes that override methods of the superclass in interesting ways. And particularly, the second one will be much more interesting, because it will use the key idea of dynamic dispatch which is at the heart of object-oriented programming. So let me show you the code. We'll do most of it that way. Here is this class Point that we're familiar with and have been using. Remember, it has getters and setters for x and y, and has two different ways of computing the distance from the origin. The first way just directly accesses instance variables x and y. The second way uses the objects own getter methods by calling self dot x and, and self dot y with no arguments. So that's a Point class. Now let's consider a rather controversial subclass. I'll explain why in just a second, which are three-dimensional points. So in this subclass, we, in addition to having an x-coordinate and a y-coordinate, have a z coordinate, which is for the height to a point in three-dimensional space. So I just add getter methods for z and setter methods as well. And then, I change via overriding the initialize method to actually require three arguments. I used super here so that the initialized method in the Point class can be used to initialize x and y, and then, I set at z equal to the z argument. Okay? So now, I also need to override distFromOrigin and distFromOrigin2. These need to do different things for a three-dimensional point. It turns out the distance from the origin in three-dimensional space is the square root of x squared plus y squared plus z squared. I could implement those directly, and maybe I could do that. It would be even shorter here, but I decided to show super in a similar way. So, for distance from origin, I could get the distance in terms of the x and y-coordinate by calling super here, that will call the distFromOrigin method. In the superclass, let that be in this local variable d, and then take the square root of d times d plus at z times at z and that would get me the proper distance. For distFromorigin2 I continue this approach of using as helper methods, my own getter methods. And so, I end up with a similar thing, except I call distFromOrigin2 in the super class. And then, add to that the result of calling the z method, calling the z method, multiplying them together and then I take the square root. So why is this controversial? Oh, people have been arguing about this for decades. You could certainly make an argument that it's a hack. It's an abusive subclassing to treat three-dimensional points as points, because they really are a different thing. It's not clear that a point in three-dimensional space is a point in two-dimensional space. They're kind of fundamentally different things, whereas other people argue no, no, no that makes perfect sense. First of all, you get lots of code reuse and we like code reuse. And moreover, you can think of it as a two-dimensional plane. Maybe it's the projection of the point down onto the xy plane. It's the equivalent of treating the z coordinate as zero. Other people would argue that if that's how you feel about it, that's not how these distFromOrigin methods behave. They do not return the distance from the origin of that projected point, they return it in three-dimensional space and you can argue about this all day long. I just want to argue here that, even if this is poor style, it does help us learn the semantics of overwriting. That when I create an instance of 3D point. I inherit the methods xx equal y and y equal. I override the methods initialize distFromOrgoin, distFromOrigin2. And I add the methods z and z equal. And that is the definition of the methods defined for a 3D point. So it's a good example of how overriding occurs. In fact, I want to argue that it's a fairly simple example. Okay. Because with the examples so far, an object like in the instance of 3D point is really not that different from a function closure. A function closure just has one method calling, call me, whereas a 3D point has, I don't know, eight or nine methods distFromOrigin2 z equals and so on. Fine. The object has explicit instance variables at x, at y, at z, whereas a closure has all those things in the environment that it's allowed to use as it's sort of private state. Fine, that's a moderate difference. We have this inheritance which let us inherit things from the superclass, but that's, as I've shown it so far, really just code reuse convenience. But there's a huge difference between subclassing and closures, like in functional languages, which is what I'm about to show you and that is there are ways to use overriding. That can make a method you inherit from the superclass, still call different methods in the subclass, and this is the thing that object-oriented programming does differently than none object languages. And so I want to emphasize that point, and I will do that, pardon the pun, emphasize that point, sorry with this final class, PolarPoint. So if you remember your high school geometry? If not, bear with me. A PolarPoint is not represented by an x-coordinate or y-coordinate. It's represented by a radius, a distance from the origin r, and a theta, the angle of, of the point from, from flat. So you represent it with an r and a theta. So here I am having Polar, PolarPoint subclass point. But I'm going to override a bunch. I'm going to override just about everything I'm going to have an initialize method that instead of taking an x and a y, takes an r and a theta. And initializes different instance variables. Add r and add theta. Now, this is actually interesting. My instances of PolarPoint won't even have instance variables x and y. In most object-oriented programming languages, it, it would have them, but not use them. Here, because of how Ruby does instance variables it doesn't even have them, I'm just using different instance variables in my subclass. So it turns out that I need to override xy, x equal, and y equal, because I need to compute those methods differently when my internal representation uses an r and a theta instead of an x and a y. And the trigonometry is that the way you compute the value of the x-coordinate is you return the radius times the cosine of the angle. So, here is r times cosine theta, and the y is r times sine of theta. Don't worry, you won't be tested on the trigonometry. Just point, just pointing out that to the client, these look like getter methods, but they're actually performing in an interesting computation. Setter methods are actually a little more complicated. If you want to set the x value in terms of some new x value you still have the old y value. And you have to do a bunch of stuff with arctangent and square root and all sorts of stuff. I do believe this works. I'm just using this built-in y getter methods. I'm calling this method here. I'm storing at a local variable so I don't call y more than once, even though, I use b multiple times and trust me that the math is correct. Y equals is similar we do basically the same computation to set the new y field. Now, for the interesting stuff. Let's override distFromOrigin and distFromOrigin2. Well, it turns out the entire advantage of a polar representation of a point is the distFromOrigin is trivial. That's exactly what the r instance variable holds. So, we need to do this override. If you look up in the superclass, this computation, sorry, that's 3D point. This computation would be wrong. We would go to read the x field or the at y field, they are not even there, we would get nil back. If you try to multiply nil, you get an error message. So it's essential that we do this override. But now, the fascinating part is that distFromOrigin2 doesn't need overriding. We could override it if we want, but it works as is and this is the thing I want to emphasize to you. If you look at distFromOrigin2, what it does when called is call self dot x and self dot y, and then compute with the result. Even though, distFromOrigin2 is inherited from the Point class, it is now down here, part of the PolarPoint class. And so, when we execute those calls to self dot x and self dot y on an Instance of PolarPoint, we get this code and this code, these methods and therefore this will work. Let me prove that to you. This is the key example. I've already loaded the file. I could make a PolarPoint dot new 4, comma, how about math pi over 4 ? So like this and it turns out I could ask pp dot x and you get 2.82 and you could ask pp dot y and I get 2.82. So what's happening here in the code is, even though, there's an instance variable @r and an instance variable @theta, when I call the x method or the y method, I do these computations and it turns out I get 2.8 from both of them. If I ask pp dot distFromOrigin, I get 4 and that's because @r is 4. And this code right here just returns it, it really is just a getter for r. And now the interesting one, if I do distFromOrigin2 I do get 4, although, it did some floating point computations, so I actually get 4.0. And that is because I inherit distFromOrigin2, so I evaluate dist code. But in an environment where the object itself is an instance of PolarPoint, so when I say self dot c and self dot y, I execute this code down here, so I end up taking r cosine theta, r sin of theta, squaring them, taking the square root and I get the right answer, okay? So this is the key punchline, that when you have a method that makes another call on the same object, when it uses self, that self is the entire object. And if self is an instance of a subclass, then you use the subclass' methods like the overridden x and y that we saw in our definition of PolarPoint. That is the thing that distinguishes object-oriented programming and subclassing from other things we've seen in this course. So, I've now shown you an example. And in the next segment, we can take a step back and give a more precise definition of the semantics of method look up. So, that we can understand this behavior precisely.