[MUSIC] In this segment, I want to continue our study of subclassing with two classes that override methods of the superclass in interesting ways. In particular, 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 object's own getter methods by calling self.x and self.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 of 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 use super here so that the initialize method in the point class can be used to initialize x and y and then I said add z equal to the z argument. Okay. So, now I also need to override distFromOrigin and distFromOrigin. These need to do different things for a three dimensional point. It turns out the distance from the origin in three dimensional space is square root of x squared plus y squared plus z squared. I could implement those directly and maybe I could do that will be even shorter here. But I decided to show super in a similar way. So for distFromOrigin, 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 super class. Let that be in this local variable d and then take the square root of d * d plus @z times @z. And that would get me a proper distance. For distFromOrigin2, I continue this approach of using has 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, multiplying them together and then I take the square root. So why is this controversial? People have been arguing about this for decades. You could certainly make an argument that it's a hack. It's an abuse of 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, that makes perfect sense. First of all, you have lots of code reuse and we like code reuse and more over, you kind of think of it as two dimensional point. Maybe it's the projection of the point down to 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 t argue here that even if this is poor style, it does help us learn the semantics of overriding. That when I create an instance of ThreeDPoint, I inherit the methods x, x equal y and y equal. I override the methods initialize, distFromOrigin, distFromOrigin2 and I add the methods z and z equal. And that is the definition of the methods defined for a ThreeDPoint. 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 an instance of ThreeDPoint is really not that different than a function closure. A function closure just has one method, call me. Whereas ThreeDPoint has 8 or 9 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 its 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 a code reuse convenience. But there's a huge difference between subclassing and closures like in functional languages, which 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 non-object-oriented 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, bare with me. A PolarPoint is not represented by an x coordinate and y coordinate. It's represented a radius, a distance from the origin R and a theta, the angle of the point from flat. So you represent it with an r and a theta. So here I am having PolarPoint, subclass Point, but I'm going to override a bunch. I'm going to override just about everything, actually. 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 @r and @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 would have them but not use them. Here, because of how Ruby does instance variables, it doesn't even have it. I'm just using different instance variables in my subclass. So, it turns out that I need to override x, y, 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 sine of theta. Don't worry, you won't be tested on the trigonometry, just pointing out that to the client these look like getter methods but they're actually performing 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 arc, tangent, 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 it a local variables, so that, 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 that distFromOrigin is trivial. That's exactly what he r instance variable holds. So we need to do this override. If you look up in the superclass, this computation, sorry, that's threeDPoint. This computation would be wrong. We would go to read the x field or the out y field. They're not even there. We would get nil back. If you try to multiply by nil you get an error message. So it's essential that we do this override. But now the fascinating part is the distFromOrigin2 doesn't need overriding. We could override if we want but it works as it. And this is the thing I want to emphasize for you. You look at distFromOrigin2, what it does when called is call self.x and self.y, and then compute with the results. Even though distFromOrigin2 is inherited from the point class, it is now down here part of the polar point class. And so when we execute those calls to self.x and self.y on an instance of polar point, 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.new(4, Math::PI/4), so like this. And it turns out I could ask PP.x and you get 2.82 and you could ask PP.y and I get 2.82. So what's happening here in the code is even though there's an instance variable at r and an instance variable at theta. When I call the x method or the y method, I do these computations and it turns out I get 2.8 for both of them. If I ask pp.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 I did some floating point computations, so I actually get 4.0. And that is because I inherit distFromOrigin2, so I evaluate this code, but in an environment where the object itself is an instance of polar point. So when I say self.x and self.y, I execute this code down here. So I end up taking r cosine theta, r sine of theta squaring them, taking the square root and I get the right answer. 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 the subclass then you use the subclasses methods like the overwritten X and Y that we saw on our definition of PolarPoint. That is the thing that distinguishes object-oriented programming and subclassing from other things we have seen in this course. So I have 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 lookup. So that we can understand this behavior precisely.