In the last video we talked about how we can create child classes from a parent class.
This means we can create new classes that have all the functionality of the parent class.
One thing I mentioned, but glossed over, was the fact that the child class can define new
behavior for an existing method.
We created a clock class with subclasses.
Each child class had their own way of telling time, even though each class used the same
method getTime().
Well it's time we talked about how exactly we do that.
In this lesson I'll show you how to override behavior for existing methods, how to stop
another developer from overriding your method, and we'll look at some unintended consequences
when you override a method in Java.
When we want to replace or modify functionality in our child class methods, we override the
method.
In our clock example we are telling Java, "hey when we call getTime(), we want you to
use this new method instead of the one in the parent class".
When Java goes to run the getTime() method, it will first look in the current class.
If it's there, it will run it.
So how do we override a method in Java?
We do this by marking the method with the @Override annotation, and providing new functionality.
We won't go into annotations too much for now, that's a whole group of videos coming
soon enough, but for now just know an annotation in Java is a metadata hint for the compiler.
If you forget to add the annotation for an overridden method, Java will give you a warning.
It's not a big deal, but you want to get into the habit of adding it.
When we run the code in a main method like this, “child method” is printed to the
screen.
We can even change the child variable to be typed as ParentClass.
The instance is still typed ChildClass.
That means when we call myPrintMethod(), we get the instance version of myPrintMethod()
from the ChildClass.
If we wanted to run the parent method first as part of the child method, we use a special
keyword called super.
That looks like this.
In this code, we’ll run the ParentClass version of myPrintMethod() first, then we’ll
continue with the implementation of myPrintMethod() in the child class.
That prints “parent method” and then “child method”.
So in our digital clock let's override getTime().
We provide a new method in the DigitalClock class and mark it with the annotation.
The signature of this method must match the signature in the parent method.
Java will complain about that too if they are different.
We should also add a comment for this method.
Best practice says we should start the comment with the phrase "this implementation does
x" where x is what is different.
There are two reasons you want to do this.
One, as we've seen, the annotation is optional.
The original developer might forget it, so you want make sure it's clear this is an overridden
method.
And second it tells us exactly what is different from the parent method.
Our completed class will look like this.
What happens when we don't want a user to override a method in Java.
There might be a good reason why our implementation should be the final version.
That's the keyword we need here.
Final.
When we mark a method as final, that means we're done.
We cannot override a final method.
If we don't want child classes at all, we can also mark the class as final.
That means nothing can be overridden.
The final on the method isn’t needed.
Everything is final and locked down in this case.
This really should be our starting point for designing classes.
The reasons are similar to the arguments we saw in the last lesson, why start with everything
private.
Best practice is we don't expose inheritance unless we intend the class to have child classes.
And if that's what we want, selectively allow the methods we want overridden.
Mark the rest as final.
The last bit to cover here is something we need to be aware of when making child classes.
It's called shadowing.
Shadowing is when we use the same variable name that overlap in scope.
Say we have a protected variable in the parent class called numberOfSeconds.
Then in the child class we create another variable and give it the same name, numberOfSeconds.
This might seem like a contrived example, but imagine the child class is written by
a different developer.
We'll set the first variable to the value 24.
And the second one to 42.
Let's add a method to the child class called toString() which returns the value number
of seconds as a string.
Since toString is in Object, we'll override the behavior.
Now let's create the child class, and assign it to the parent class.
This is something we'll do quite often in Java.
Create a class, but use the child implementation.
Now if we set the value to something else, like 50.
What happens when we print the variable and then call toString()?
We get 42 and 50.
The parent variable is shadowed by the child variable.
We have to cast our class to the parent class in order to print 42.
That's one solution, but we know better.
From the last lesson, we know the attributes should be private.
That means we can access them only through getters and setters.
Using a getter, we'll always get the parent version 24.
The child class will not know it's shadowed the parent variable since it's private.
If we want to override that behavior, we can override the getter.
Either way, by being explicit with the getters and setters, we won't get any surprised from
accidental shadowing.
And that's the end of this lesson and how we override a method in Java.
We'll look at another important object oriented concept called polymorphism in the next video.
If you have any questions about this video, leave them in the comments below.
And I'll see you in the next video.
Hey!
Thanks for watching the video.
If you like what you're seeing or have any questions, let me know in the comments or
on DeegeU.com!
New videos come out each week, so make sure you subscribe.
You don't want to miss a video!
In fact, lets go watch another DeegeU video.
See you there!