So it's now time to talk about inner classes because the thing is in Java,
it's actually possible to nest a class inside another class.
So what I'm gonna do is start by outlining the four types of nested classes, and
then we'll go through some examples.
So the four types of nested classes that you can use
in Java are static nested classes.
We could use a nonstatic nested class and we call that an inner class.
We use a local class, now that's an inner class that's defined inside of a scope
block which is usually a method and
also an anonymous class which is a nested class without a class name.
Now, the static nested class is mainly used to associate a class with its
outer class.
So in terms of behavior, you'd think of it as being identical to a top level class,
but the difference is that it's packaged in its outer class rather than in
the package.
So that really means that it cannot access the nonstatic methods or
members of its outer class without first creating an instance of that class.
So I know that sounds like a mouthful.
But I think what we'll do is, we'll start with a nonstatic nested class or
the inner class, because these are quite useful.
And the reason that they're useful is when it doesn't make sense to refer to a class
without its outer class.
So the example is if we're actually modeling an engine.
We might actually have a gearbox class and a gear class.
So let's go a head and
actually create that to see how this actually works in practice.
So I'm going to go and create a new class.
Gonna call the first one Gearbox.
And we're gonna add a few things to the Gearbox.
We're going to have an array list for
the gears, number of gears and also the gear number.
So we'll create those fields.
Private ArrayList .
We haven't created that class yet.
We're going to do that gears private int maxGears and
private int gearNumber, and we'll set that to 0.
We'll create constructor, and the only parameter is gonna be the maxGears,
like so.
And set that to maxGears to maxGears.
And then we'll also do a this.gears = new ArrayList.
So we'll just initialize it.
But we also wanna do gear neutral = new
Gear (0.0.0) that's so.
And I think what I did there is, I didn't specify the right level of Java for
some reason so I had to fix that.
If you ever do get that problem just right-click this and
go to open bunch setting.
We're gonna make sure that we've got project set to right version.
So we'll change that to version that eight.
You can see Diamonds we're introduced in version seven.
I have actually said that in an earlier video to make sure you're always using
version eight there.
After you select that, that should fix that error.
And what we need to do is we need to add the Gear class,
which we'll actually fix at shortly.
So we're creating a new Gear as you can see there, and
we're going to add that to the array this.gears.add(neutral).
So that's the base class, and what we'll actually do now is create an inner class.
This is the inner class which is sort of a class within a class.
So to do that, making sure that you're actually in the public class definition.
So we're in the final bracket in between any method, at the end of any method but
before the closing brace that where we can start typing.
So we can type public class Gear, just as you would normally do it and then
we can actually add some private fields just as you would normally create a class.
So int gear number, your number and
the same private double ratio.
We can create a constructor and you can see we'll be using a constructor in
the gearbox constructor to create a new gear.
So we'll do that, and the constructor will have two parameters,
the gear number and the ratio like so.
That should fix that other error on the screen there now.
And we'll just create a public method for
drive speed which will basically accept the number of revs as a parameter and
multiply that by the ratio to get the drive speed.
So to do that, so we normally do, we'll just actually create a method.
So public double driveSpeed int revs.
We could return revs multiplied by ratio.
So there's a step list like a definition for when you're in a class.
So it doesn't actually make sense to refer to a gear in this scenario,
except when we're talking about a gear box.
So that's why we've modeled it like this.
So the gear isn't something that sort of is useful in its own right.
It's coupled with a gearbox so it makes it useful.
So that's the idea of using an inner class.
As I said, when it doesn't make sense to refer to something except within
the context.
In this case, it doesn't make sense to talk about gears unless it's within
the context of a gearbox.
This one we borrow the gears using an inner class of the gear box class.
Now instances of the gear class have got access to
all the methods of fields of the outer gearbox class, even those mark as private.
So notice that the use of this in the inner class refers to the inner class and
not the outer one.
So in other words this.gearNumber,
the this is actually talking about this actual class here, the public class.
So the inner class in other words, it's not referring to gearNumber.
That's in this one here, cuz that's actually in a different class.
That's in the top level class, the gearbox class.
I keep that in mind that when you're using this particularly with inner classes,
that you're actually dealing with the class that you're currently in this case
we're talking about the gear class.
And that probably brings up an interesting point here.
But they're using the same variable name for two different things like this,
is confusing.
And of course, the reason why it's confusing is that inside the gear,
both this stock gear number and gear number,
refer to the gear number of the gear and not the gearbox itself.
So if you want refer to gearbox's gear number from an instance of the gear class,
you have to use gearbox.this.gearNumber.
So if a word or variable or
parameter name is the same one as the one that's in the outer class,
it's said to shadow the declaration of the outer class or in the outer class.
So here gearNumber in the gear class.
This one here. We can actually say that that is shadowing
gearNumber in the gearbox class which is that one there.
So that's not a good thing.
So we're gonna rename gearbox variable to current gear to avoid confusion, and
that's what I certainly recommend because you're gonna get into all sorts of
problems otherwise.
So what we're gonna do is we're just gonna right-click that Refactor > Rename,
we'll actually change that to current gear.
CurrentGear.
The center, that will actually make a lot more sense to do it that way to
avoid any confusion because obviously,
you wanna make things as easy as possible when you're developing.
And anything you can do to reduce this sort of confusion,
particularly when you start dealing with inner classes.
It is very important to do that.
So make sure you do keep that in mind.
Now the gear class itself is a member gearbox and
it must be created within instance of it.
And probably the best way to show you what I mean by that is actually to show you
the examples.
So we'll go back to our main class and create that so
you can understand how that works.
So go back to main. We're gonna write some code here, so
we're going to start while typing gearbox.
So Gearbox we'll say, mcLaren it's a nice car, = new Gearbox(6).
The six gears, in other words.
Now, to actually create a gear class,
we have to use Gearbox box.gear, like so.
First = mcLaren.new Gear 1,
the ratio 12.3.
Then we can do something like System.out.println(first.driveSpeed and
we can set that to say, 1000 revs like so.
So it's important to understand the declaration and
because gear is an inner class to gearbox, we need to specify like that,
we need to specify the outer class Gearbox., and then Gear.
And then actually also here, you notice that line there, we actually used
the gear box instance that we created and we did a .new there for the gear.
So slightly different syntax when you're creating an inner class like that when you
probably seen in code before.
So keep that in mind cuz it's very important.
You'll actually get an error if you try to create a gear object
without actually having an instance of gearbox to created from.
And just to show you what I mean by that let's create a few more examples and
you can sort of see the weird areas that you get.
So we've got the first one there, mcLaren = new Gear.
That one worked fine, but if we tar topping
Gearbox.Gear second = new Gearbox.Gear.
Notice the syntax is different 2, 15.4.
We actually get this weird little error here.
And the error is, conduct in the bracket .Gearbox is not an enclosing class.
So you can't actually do it that way.
You basically need to use the .new back referencing the outer class
versus what you saw in the example above.
We created the outer class which was gearbox, created new instances of that and
then we used the .new on the end to get that to work.
So that doesn't work like that, but you also can't do this.
You can't do Gearbox.Gear third
= new mcLaren.Gear(3, 17.8).
That also doesn't work, so
you get an error that's you can't resolve the simple gear and that's because again,
we need to use the .new to create a new instance of it like that.
So keep that in mind that you probably would think that
with particularly with the third gear, that it would actually work like that.
But as you can see, any attempt to create the third gear might
look like how you'd expect it to look like but it actually doesn't work as well.
And again, just confirmation that in the case of the second gear.
It actually files with that error which is always gearbox is not an enclosing class
which is slightly confusing, so if you look at that,
you're probably thinking why is it doing that?
And that's because gearbox does seem to be an enclosing class,
in as much as the outer class for gear for that call new to work.
Gear would have to be a static nested class.
So it's just really a matter of getting used to the syntax.
The bottom line here is that this is the correct way to
actually create the outer class which you would normally do anyway,
there's nothing new about that, but in terms of the inter class,
you reference the outer class first dot then whatever name the inner class is.
And then you when you that's for
declaration purposes and when you're actually initializing it the name of
the object you've created .new then whatever the name of the unit class is.
And then the normal parameters you would have in a constructor.
And so for this just run that to see that it's actually working.
You can see that I've actually got some errors there.
I'm not good at picking up typos when I'm actually recording videos.
Now if we actually run it, now that I've actually fixed that other little typo,
we should actually find that it works and it comes up as you would expect it to.
So bottom line is that the correct syntax might be a bit clunky, but
that's just how it's done.
That's how the designers of Java have implemented this, and
we obviously have to work with that.
Another thing to keep in mind is that often though the inner class is gonna be
private anyway.
So in other words instances will only be created by the outer class.
So in other words you wouldn't normally, if you got a gearbox here
it's the outer class, the inner class is probably in this case geared is probably
something you don't want people don't normally directly access anyway.
So in other words, the instance is gonna be created by the other class.
So making this class gear private makes sense here
because we're not actually interested in the individual gear objects we're driving.
We wanna change the gear, but
we do that by interacting with the gearbox not with the individual gears.
So in other words, your public interface that you're exposing to your
program here would be gearbox, you wouldn't be exposing gear directly,
you'd be exposing the fact that you can actually change gears,
you won't be interacting with the gears itself, if that makes sense.
So what we could actually do is actually make some changes to that, so
that's how it would actually work.
And you're sort of going back to the gearbox class anyway.
Size that down.
That we didn't actually create a new gear anyway.
And in the constructor we add a new gear for neutral anyway by default.
And that's how we could access that.
So if we actually change this now and change this so
this is actually a private class.
The inter class is now private, so you can see there.
We've still got a constructor and the ratio,
but we don't need to make any other changes to it.
But that will mean if you come back to here, we actually get errors now and
that's because we can't access that directly anymore, but
we'll need to make some changes to the gearbox to actually do that.
So what we're gonna do is actually add a few functions.
We're gonna add a function for operating the clutch.
We're gonna add the add a method rather to actually add gears, so
we can actually add gears to our gearbox object which will be adding gears to
the array list automatically.
And to show you how to change gear as well, all done from the outer class,
in this case gearbox.
So this is going to be above the declaration for
the private class gear now, so we'll start off by operating clutch public
void operateClutch(boolean_in), in as you can see there.
And I haven't created that variable yet, but we can do that shortly.
So ClutchIsIn = in, whatever the status of that is.
I will create a Boolean in here.
So it'd be private boolean clutchIsIn like so.
So that's the ability to operate the clutch as a method,
we'll also add the other method which is the ability to add gears to our gearbox,
add public, void, add gear and we'll pass a number,
that should be the gear number and the ratio, which you saw us using before.
Now we'll just do some basic checking here.
We'll put if((number > 0 && (number <=
maxGears) maxGears was something we defined up above.
MaxGears, if that's the case,
this.gears.add creating a new gear class which you remember is now private.
So only the outer class, the gearbox class can directly talk with it now, and
we need a gear number and also the ratio.
So that's the add gear method and then the other thing we wanna do is we want
the ability to change the gear as well, and to do that we use a change gear.
We'll create a change gear method,
so public void change gear int newGear.
We wanna make sure that it's valid.
So we're gonna put if newGear >= 0,
remember it can be 0 if it's neutral & newGear <this.gear.size.
So we are just validating that we actually have got a gear of that number.
That's the case.
And also check ClutchIsIn assuming it's a manual.
But if this .clutchIsIn, if all that is true we can go this.currentGear = newGear.
We can do something, we can print just a message out to say,
gear + newGear selected else, what we're gonna do is put out a message Grind.
[LAUGH] Which would basically mean that,
that is a problem with you trying to change the gears and
you're getting that weird sound you get from a menu when you're trying to do that.
And we'll set this .current here to 0 cuz we weren't successful in actually
sending the or changing the gears essentially.
And what we'll also do,
is we'll actually add a wheel speed method as well to determine the speed.
So I'm gonna create one, public double wheelSpeed.
And the parameter will be revs, if(clutchIsIn).
Scream [LAUGH] like so, and we'll return 0.0, so
we're not returning any calculation at this point in time.
Otherwise, if the clutch is set in, in other words the car can move,
you're gonna return revs * gears.get
(currentGear).getRatio.
And what we haven't done, is we haven't actually exposed the getRatio.
So we need to do that, that's a method of the inner class.
Or we can just end this and just getter, so we're gonna do a getter for
our ratio like so, and that should fix that other error up.
So you could see how now, we've written the gearBox class
to perform all the functionality it needs in the gear class itself.
We're creating new instances of the gear and saving it in our array list up here,
but we're also using various functions within the gear to get the ratio and for
other sorts of calculations.
In this case here, you can see in the wheelSpeed,
if the clutchIsIn obviously we're not actually moving.
There's gonna be a problem.
[LAUGH] So we're screaming, we're turning zero.
Otherwise though, it's the revs multiplied by the gear or the current gear.
The current gear that we've actually set in the changeGear method.
And then we're using the dot ratio which is actually called from the inner class
itself.
Now that's how you would actually go about doing it.
So what I'm gonna do, this video is getting a bit long.
I'm gonna stop here and in the next video, I will come back and will actually
put some of this code into the main method and start seeing how this actually works.
So I'll see you in the next video.