Abstract Classes Part 1

A free video tutorial from Tim Buchalka
Java Python Android and C# Expert Developer - 1.69M students
13 courses
1,693,105 students
Lecture description
Get to know more about Abstraction in this lecture.
Learn more from the full course
Java Masterclass 2025: 130+ Hours of Expert Lessons
Gain real-world experience with OOP, coding exercises, and problem-solving for job-ready Java development skills
134:50:19 of on-demand video • Updated April 2025
Learn the core Java skills needed to apply for Java developer positions in just 14 hours.
Be able to sit for and pass the Oracle Java Certificate exam if you choose.
Be able to demonstrate your understanding of Java to future employers.
Learn industry "best practices" in Java software development from a professional Java developer who has worked in the language for close to 25 years.
Acquire essential java basics for transitioning to the Spring Framework, Java EE, Android development and more.
Obtain proficiency in Java 17, as well as older versions incluing Java 11 and Java 8.
Java 21 features coming soon!
English
So in this set of videos we're gonna
start talking about Abstractions. So abstraction is when you define
the required functionality for something without actually
implementing the details. In other words we're focusing
on what needs to be done, not on the how it's to be done. So, interface is, as you saw,
are purely abstract, and they don't specify any actual
aspect of the implementation. The actual implementation, of course, was left to the classes that
implement the interface, and that's why we use the word implements
when we're using an interface. And as you can see, I've got the challenge
from the interfaces set of videos. And I'm just gonna go through and
just confirm that again. So you can see that we've
got our interface here. Now this was the interface that
originally had the ArrayList. And if you remember I changed it
back to a list, and I changed it back to ArrayList here just to actually
talk about this a little bit more. But again, getting back to the interface, the interface defines
what needs to be done. It doesn't define how it's done. The how,
we actually have to go into the class. In this case say Monster, and
that's actually implements that's implementing the Isavable interface meaning
that it's actually gonna do the work. And we're getting that
error right now by the way, because as you remember we
actually changed this to a list. But I've purposely change the interface
back to an ArrayList because I wanna talk about that. So the ISaveable interface,
as you can see onscreen, provided, or specified that there must be
a write method that returned an ArrayList of values to be saved,
and also the read method. As you can see on the screen, there that was provided with
an ArrayList of values to be restored. So there weren't any other details
of how the class should populate, or write the ArrayList. No details on what the class should
do with the read values either, that's left entirely up
to each individual class. You could really if you wanted to, set it
up so that each class implemented this interface might do completely different
things with the write and read methods. There's no rules of what's to be done
with those methods and interface. And that those methods have to be
implemented in a particular class. But strictly speaking, if you look at this
interface, it's not complete abstraction. And the reason is in this particular case, there's a specification that
ArrayLists must be used. But in a typed language like Java it's
hard to avoid doing that sometimes. But in this particular case, and I explained this a little bit in
the challenge, in the previous challenge, the interface challenge,
we can abstract this and use just a List. So what we could've done
is we could've changed, and what we did do is we changed it,
so we actually removed the need for an ArrayList, and actually just changed
it so in actual fact it was just a list. And by doing that, we abstracted again, because we then didn't force the Monster
class or the Player class or any other class that implemented this
interface to have to use an ArrayList. They could use any type of list for
that matter. And that can be really useful and I
have made a recommendation that you should do that whenever you are using a list
of some type in your programs. Generally speaking in
terms of the declaration, it's good to write it like that and
that leaves your implementation open to actually use any type of that
is anything that implements that particular interface in which case this
is the list interface which part of Java. And just as a recap, we can see that is if we
actually click on this, I'm holding down command, I believe it's Alt on your
control, sorry, on a Windows machine. Click on it, we can go into the actual
list interface, which is part of Java, is quite interesting, actually
reading some of the code sometimes. And you can see that this is actually
the public interface for the list, which the other classes such as
ArrayLists within Java etc., actually have to implement
to actually use. Now obviously by definition,
being an interface is abstract, so you can't use a new ISavable or
a new list. What you need to do is instantiate
a class that implements the interface. And you obviously saw that
in the interface challenge. Now Java also allows abstract classes. Now, before we get too much into that,
I just wanna go one step further and just mention that with this interface,
we could've actually taken it one step further, because we
specified the type to be string. But again, if we wanted to be really
flexible, we could even remove that and made that a really generic list. And left that up to the user,
the class that implemented this interface to actually decide what
type to actually use. So I could have quite happily done that,
and we could have then gone back to our monster class, and actually look
to changing it, we would have to change a few little things around a little bit
with terms of the definitions. But ultimately you could have
given that extra functionality, or the extra flexibility I should say for the actual classes that we're
implementing in this interface. So keep that in mind that you can take it
one step further if you wanna go to that level and
leave yourself maximum flexibility. But sometimes there's a bit of a fine
line as to how generic you make it and how specific. Sometimes, somewhere just in
the middle might be the way to go. So as I mentioned, interfaces are by
definition, in Java, Abstract. So you can't use the new ISavable or
new list. You have to, instantiate a class that
interface, that implements I should say, the interface. And what I'm gonna do is I'm
gonna close this one down now, I'm gonna swap over because I've created
a new Project in IntelliJ for Abstraction. We are gonna actually
start typing some code. Because as I mentioned,
Java also allows abstract classes. And these are classes that define methods,
but do not provide an implementation
of those methods. The implementation itself is left
to the classes that inherit from the abstract class. So this is different from
inheriting from an interface. You can also inherit from
an actual class as well. So in the videos in inheritance we
created a very basic animal class, and then we extended it to create a dog. let's see if we can do something
similar now, using an abstract class. So I'm going to actually open up
this now and create a new class. New > Java class, and
I'm going to call it Animal. And what I'm gonna do is I'm gonna
make this an abstract class. So to do that we type in the word
abstract after our modifiers. So, public abstract class Animal,
and we can define the variable. So we just type private String name, we can have a constructor, which
we'll pass us the name of the animal. And let's also add a couple of
extra abstract methods, and this is the part I wanna show you for
eating and breathing. So we can put public abstract, again the abstract keyword after
the modifier, void, eat, and notice we've just put a semicolon there,
so we haven't implemented this at all. And public abstract void breathe. And we can also add a constructor. Sorry, we can also add a getter
I should say, to name. So there's an abstract class, and
you can see here what we are doing is, as we're about to see, we're gonna
inherit from this abstract class. And it's gonna let us define
behaviors that are necessary, without specifying how
they are to be performed. And this really ensures that
the subclasses, such as the dog, must actually implement them, because of
course, normally, in the normal class, if you're extending from a class,
you can create a base method in the class. But there's no requirement for
the other class, the class that is subclassing to
actually implement those methods. But by creating abstract methods, we actually are forcing the class that
is ultimately gonna implement from this abstract class to create those methods for
us. And that's why we've actually
marked them as abstract. The other methods and the other field
in this case are quite okay, and they work as normal Java code. But we're actually specifying and saying there's some abstract methods
here that have to be implemented. So if you did actually refer
back to the inheritance videos, you'll see that this is
very similar to that. Really the only difference is we've
actually marked it as abstract, the actual class definition
at the top there. And also we included those
abstract methods as well. So to keep things simple in this example,
I've actually removed the member variables like brain, weight, etc.,
just to keep things really simple. Now if we actually tried extend from this
I wanna show you what actually happens. So I'm gonna create a new class,
I'm gonna call it Dog. I'm gonna extends Animal. And I think I've put
Animal in the wrong place, Animal should have been in that package,
so I'm just gonna move that. Drag it and
it's asking where I want to move it to? I want to move it to that package,
so I'm gonna click on Refactor. And that should fix it up now, so
we're actually in the right place. But now you can see we've got
a different type of error. And if we have a look at that, it's actually telling us that class dog
must either be declared abstract or it needs to implement the abstract
method eat in animal. And in actual fact there was two methods
there that needed there to implement, but this little error message is
only popping up the first one. So you can see that now, in order to be able to create a valid
class, we have to implement those methods. So we can do something like create
a constructor first, the Dog like so, and we'll just use the default
super to save the actual name, which is in the animal class. And we can do some overrides now. So we can do Alt+N and we can actually
go to implement or override, they'll both actually
pop up the same things. And you can see now we've
got two options here. We've got the two options there and
notice the little descriptions here or the little icons,
they were a little bit different. So getName you can see that's like
a method, but this one is also a method. But notice how it's got
a chunk taken out of it. That's IntelliJ way of telling
you that this is an abstract method. So I can actually select both of those and
override them, which then makes the class valid and
I can just put something in the code. GetName + is eating. And for breath, Breath in, breath out, repeat, pretty basic. So that's now made it a valid
class as you can see. And also keep in mind that, because we did implement the getter for
the name, getName in the Animal class it's available in this class when I actually
used it here in the eat method. And likewise from the constructor I
mentioned that we actually called, the constructor using the super keyword, which calls the constructor that's
actually in the animal class as well. So the important thing here is that
not all methods have to be abstract. This is one of the differences between
an abstract class and an interface, you can actually mix these up. An interface, if you recall,
if you go back and have a look. Actually it looks like
I closed the project, I probably should have just switched over. But with the previous interface which
you saw earlier in the video, the entire interface was completely abstract, and
there's no implementation whatsoever. So there's no opportunity for
us to enter fields. There wasn't any opportunity for
us to enter any other code. We purely were able just to specify
the methods that were gonna be used for that interface, that a class that was
implementing that particular interface had to actually fulfil in
order to make it valid. But as you can see in the case of Animal,
we can actually mix it up. We can actually add fields. We can add regular methods, or
a constructor in this case. But also a regular method as you
can see there with the constructor. But then we can also specify that certain
methods are abstract as you can see there. So in terms of actually using this,
it works now as you would expect. So if you go to write some code,
we could do something like Dog _dog = new Dog, Yorkie, dog.Breathe. Again, it behave,
you would think it would work, dog.eat. And if I actually run that. Breathe in, breathe out, repeat. Yorkie is eating, so
you can see that it's all working nicely, as you would think that it would be. Okay, so that's working as you think it
would but let's take this now to the next step and actually create another class,
which in this case is going to be bird. New > Java class, and
we're gonna call that Bird. Bird and we need to extend, cuz like
obviously, using the extends keyword, because it's a class, and Animal. And create a constructor,
which works as you think it would, and let's also override those methods. Eat and Breathe, again. And we'll put for eating,
getName + is pecking. And for breathing, well,
it's going to be the same, so it's breathe in, breathe out, repeat. Like so, and of course, we can then go
back to our main class, main method. Bird, bird = new Bird. Bird.breathe. Bird.eat. You can run that. We get the results, as you can see. Birds breathing, and
parrot is pecking, Yorkie's eating. So it's doing the right thing there. Now birds can also fly, so it could be a good idea to add
a fly method to our bird class. But, not all birds can fly, so implementing a fly method in
the bird class is not a good idea. Better idea would be to create an abstract
Bird class that extends Animal, and also has an abstract fly method
that individual Bird objects can implement as they're able to. So let's go ahead and
see how that would work. So we'll go through now and
modify our Bird class. Close this down to give us a bit of space. Now I'm gonna change this up here,
now where we had public class Bird, I'm gonna make that abstract. Leave the constructor as it was and also
leave the two methods as they were, but also down the bottom,
let's add another abstract method. So public abstract void fly. So that's another abstract
class at this point. So this is an abstract class that itself
is extending from another abstract class. It's implementing the required methods
that the Animal class requires it to, namely eat and breathe. But it's also defining
an abstract method fly. That ultimately has to be overridden
by another class as well. And of course we made this class abstract, which it needs to be in
order to do this anyway. So now that we've done that,
of course we go back to the main class we'll get an error because, hold on so
we can see what the error is. So Bird is abstract. It cannot be instantiated. Remember, we said that
earlier in the video, that you can't directly instantiate
a class that is abstract in anyway. So what we need to do is
actually create another class. So to do that, we'll change this, and
we'll actually create a Parrot class. Back here, and create a new class, Parrot. We can extend, extends Bird,
without a constructor. And all we need to do here,
is because the other methods are already overridden in the Bird class,
we only really need to do one. So if we go back there and have a look, and go to override, we can still override
Eat and Breath if we wanted to, if for some reason this type of bird had
a different way of eating or breathing. But really, all we wanna do
is override fly in this case, to make this class valid. So I'm gonna do that, and I'm gonna put something like
Flitting from branch to branch. So it's actually flying
effectively from branch to branch. And again parrots automatically inheriting eat and
breath from the, and breath, I should say from bird, so we don't have
to re implement those methods again. It now has its own implementation for
fly. So if you go back to the main method now. We'll now change this so this is
actually parrot, instead of a bird. Remembering that Parrot is
now extending from Bird. Parrot set to new Parrot, and
now instead of typing Parrot as a name, we'll say it's an Australian ringneck,
and it's gonna be Parrot.breathe. That will still work. Parrot.eat. But we're also gonna do parrot.fly now. So if you run that, you can see
that there's no errors anymore. You can see now Australian ringneck is pecking,
flitting from branch to branch. And just as another example to show
that we can do something different, let's create one more class. New > Java Class and
we'll call this one Penguin. And we got to extend from Bird again,
extends from bird. Create a constructor. And let's just override fly again. And we can put I'm not very good at that. Can I go for a swim instead. And that's our implementation of penguin. So going back to the main method again, we can just come down
here I can put Penguin, penguin = new Penguin,
Emperor and penguin.fly. And now, if we actually run that. You can see, we actually get the message
quite correctly, still, that in the case of the bird Australian ringneck is
pecking, flitting from branch to branch, and we only printed out
the fly method for penguin. I'm not very good at that,
can I go for a swim instead? So, you can see that's
working quite nicely. So I'm gonna end this video here. In the next video what we're
gonna do is start talking about how this is different
to an interface and how to decide whether to use
an interface or an abstract class. So, I'll see you in the next video.