Continuing on from our generic class from the last video, so
we actually started this code.
We didn't actually run it.
So I'll just run it now.
Just to confirm that we've actually got the right number of players,
we should get the answer three cuz we've actually added three there.
And you can see we've got the answer three.
But it does actually bring up interesting issue.
There's an obvious problem which I started talking about.
And that's that the first time in this case, our baseball player, and that's Pat,
the first time he actually runs onto the field and tries to hit the football with
his bat, he's probably gonna be red carded and sent off the field.
So in other words,
our team class is actually allowing this to add any kind of player to the team and
there's no actual check to see whether they're the correct type of player.
Cuz obviously in the case of the Adelaide Crows, we've decided that that's
a football team so there should be some way of ensuring that only people,
only players who are football players can be added to that team.
And likewise for a baseball player, only baseball players should be able to
be added to a team for that baseball, and likewise for soccer.
So we need some sort of mechanism to actually make sure that happens so
that only the right types of players can be added to teams.
Now there's a few solutions to this.
One solution would be to create three different team classes, so in other words,
a football team, a baseball team and a soccer team are three
different classes and ensure that they only accept the correct player type.
But in that case, the code's gonna be largely identical.
So we're gonna be duplicating code and it's usually never an efficient way to
code to actually duplicate codes so I don't recommend that.
And of course that's not to say we couldn't extend the team class to create
three different team types within there, so
sort of have sort of private classes for each of those team types.
But then if we did that, we need to implement functionality that's unique
to each team type in that code as well.
So a common code to do things like add a player,
play a game, record a score et cetera would be in the base team class.
But fortunately, Java's got generics,
which we're talking about obviously in this section.
And it enables to really specify type when we're creating our class.
And this is exactly what we want here.
So what we wanna do is modify team to be a generic class.
So let's go ahead and actually do that, and we'll see that working.
So we're gonna go to our team class.
We're gonna come up here.
We're gonna start making some changes to it.
So at the moment, obviously, it's accepting a generic player there.
This is in the array list.
Also in the addPlayer method.
So let's actually make some changes.
First so we're going to modify the class declaration to include the top parameter.
So to do that, we use a diamond as we did before but we put a T in the middle and
that's to indicate that there's gonna be a type there.
We also come down here to ArrayList and we do a similar thing.
So instead of player we delete that and make that T as well.
T was all can see it comes up, and IntelliJ comes up and
says this is a type parameter of com.timbuchalka.t, which is correct.
And the third area you wanna change is our addPlayer, so instead of allowing a player
class, we're basically adding the type parameter again for addPlayer.
So again we're changing the type so we can make this more generic and
it's going to work for any type of player.
And how this basically works is when we actually instantiate a class the T will be
replaced automatically by Java with the actual class we're using,
with the real type in other words when we're actually instantiating the class.
And that's why we've actually changed the addPlayer and
the ArrayList as well, so all three changed so that we can actually,
we're able to actually do that sort of type checking.
So notice that we're actually getting an error in our player.
And the reason is that we specify that its type is T,
which is a parameterized type as we've said before.
And because this type's not known until we actually instantiate a class in other
words at run time, there's actually no way for Java to know that an object of type
T in this case really does have a get nine method.
So the only sensible thing that a compiler can do in this situation is to
flag an error which is actually showing on the screen.
Now we can't get around that for the moment.
So what we're going to do is we're gonna cast player the player parameterize type
to make that a player type before attempting to evoke its getName method.
So we're not gonna change the parameter here.
We're going to actually change it when we're actually referencing it.
So we're just gonna do this.
We're going to cast that to player cuz we know ultimately it is going to
be a player object, like so.
We need to actually put two brackets there for that to work.
Player and then put another bracket there and that should fix that up.
And likewise with this one, you need to the cast as well,
player, player and bracket to fix it.
And that fixes it as well.
Now it is a solution but it's a pretty ugly cast,
in the sense that we're invoking a method on a cast object and
we need parenthesis around the cast before we can use getName.
But do not fear, it's only temporary, very soon we can see.
I'll actually show you how you can remove the need to do this.
But this is okay, that's out of trouble for now.
Or yes working through our code.
So getting back to our main method now, to our main class and
then to our main method, you can see that there's actually no errors at the moment.
But if we actually have a look at some of these warnings, we're getting an uncheck
call to add player T as a member of raw type com.timbuchalka.team.
So I talked about raw types in the previous video and what they were and
how to get around them and you also saw why we shouldn't be using them.
So with that said the program is still going to run, but poor old pat will still
be given a red card, so in other words we're not validating the type of player.
It gives the particular type of team.
But we can actually fix that quite easily by specifying what type of team our
football team variable should be.
So team now has the ability to accept the type of argument.
That's what we've just changed in the team class.
So what we have to do is provide it with one.
So we're gonna go back to our declaration for the adelaideCrows.
We're gonna change that.
So we're gonna add the type argument to that.
That's going to be FootballPlayer.
So once we've done that, we still actually got an error here, unchecked assignment,
because what we haven't done is we haven't added the diamond.
So we need to add the diamond after the team.
Now they're actually using it to the left in the declaration section, so
I'm going to put the diamond in there now, and that now removes that error.
So we've got a proper assignment about generic class now.
So we've actually indicated that we're gonna be using the FootballPlayer, and
there's a sequence that during that you can see now Joe was added.
And the reason that Joe was valid and it's not telling us that there's a problem is
that he was assigned as a football player.
But in here, the other two, Pat and Beckham, cuz our baseball players and
soccer players, that ain't meet the criteria for this top now,
and it's actually being flagged as an error.
So it's actually an error not just a warning.
It's telling us that it physically can't add those to this class anymore based on
the definition.
So in other words this particular instance of team is now only going to
accept football players and
attempts to add Pat and Beckham, and there was showing [INAUDIBLE].
So let's give good old Pat a chance to shine.
So what we're going to do is we're gonna accommodate these first, tab,
get rid of those.
And let's actually add another class.
We're gonna call it team BaseballPlayer.
We'll call it baseballTeam.
I'll get rid of player team, we'll call it baseballTeam = new Team.
Yeah, we need to add the diamonds if we're actually using that on the left-hand side
which we are for your type assignment and that's the type of team like Chicago Cubs
which is a baseball team in America, for those who don't know.
And we can now successfully do baseballTeam.addPlayer(pat).
Now that will work quite nicely, because again Pat is,
he's been defined up here as a baseball player.
He's a type baseball player, and we've defined that as the team of being of
type baseball player, so consequently he's able to be added without any problems.
But with that said, there's still some problems here.
And at the moment, the top parameter T, in our team class can be applied,
can be supplied rather, any type, except primitive types such as int, so
in other words, any object.
So we're not doing any validation of that.
So we can actually do something like, for example,
Team brokenTeam = new Team.
And this might work which it won't work and
if we do something like brokenTeam.addPlayer,
no-one, the string's in valid type.
And as you can see, there's no compilation errors.
We're just getting a warning up here,
because Beckham's actually not in use at the moment.
But the problem with this is, is gonna be the cast when we actually go to run this.
So if we actually run this code we actually get an exception.
An exception has got to do with the fact that java.lang.String cannot be cast to
com.timbuchalka.Player.
And of course if you remember going back to our team class,
we actually defined that in here.
We actually added this ugly cast to get the .getName to work.
So what we really need is some sort of way to validate the type of class actually
when we're using it for this team.
So in other words, we need the ability to restrict the type that can be provided for
this T argument from the T from the team blast.
Now the good thing about that is that Java also provides a mechanism for
restricting the types that can be used as type arguments.
They actually call these bounded type parameters.
By using those you can provide them with an upper bound.
So I'm gonna explain these terms and more data once we've seen them in action, but
let's actually change the declaration of our team class first, and
then we'll go through it.
So at the moment we've got team, and then we've got the type in diamond,
as you can see there.
But we can actually change this to put extends Player,
noting that I'm actually typing it within the diamond.
So T extends player now.
If you go back now to our main class, cuz we've done that we're now actually getting
an error here and notice it's not a warning.
It's actually an error to say java.lang.String is not within its bound.
It should extend com.timbuchalka.Player, so that's our way of actually
restricting the type of class that we can actually use for team.
Now just wrapping that up again, so basically what we've said is that we've
told Java that the type parameter we'll accept for this class when using generics,
is gonna be any type that extends from player or subclass of player.
So in other words by typing it like this with this extends Player,
Player is set to be the upper bound of T.
And of course T as I've mentioned earlier is a bounded type parameter.
So if we attempt to use any class in other words that is not derived from Player,
we're gonna get that error which was seen here in main now.
And as a consequence of that cuz we've actually done that,
the good news is we can go back to our code again, go back to team,
and we can actually get rid of this cast.
So we can actually remove that and
go back to what it was before which is just player.getName.
So I'm gonna do that, like so.
And for the next one.
And again the reason why I want to do that is because of the bounded top
parameter we've put up here,
we've said that all valid upper bounds of T have to extend from players.
Or as a consequence Java then knows that, okay,
so in other words Java now knows that the top T in that player extends from player.
So it's gonna have all its methods including getName of player.
So basically there's no longer an error and there's no need for
you to actually add that cast anymore.
Now going back to our main method again, so
let's actually change now this team here.
We're at the moment the brokenTeam to be a soccer team, so
we can type SoccerPlayer, brokenTeam = new Team, this won't work.
And notice how it's still going to come up okay.
We've defined a new team even though we haven't changed the name.
But the important part here is this next declaration is brokenTeam.addPlayer,
we're passing the string.
Java's also showing an error for
that when we try to pass a string to the addPlayer method.
And again this is exactly what we want because we've defined the addPlayer method
and the class to require us to use the player class or a subclass of player and
obviously string isn't there and that's why we're actually seeing an error.
So if we actually replace that string with our soccer player Beckham we
actually see that the error goes away,
and we've got a tick in the top right-hand corner and no warnings either.
So before going on to the challenge in the next video
just wanna discuss a few other points.
Firstly an argument passed for
a type parameter can either be a class or an interface.
So you can actually use interfaces as well.
So although we use the player of class in these examples,
this is going back to team.
We use the player class there.
You could've declared team to accept an interface instead.
And that's certainly valid to do that in Java.
And the other thing to note is that interfaces themselves can also
specify type parameters.
So in other words you can create instances of a class that implement an interface,
and then you can ensure that the interface methods you implement
act on a specific type of object or objects.
And we've seen that in previous examples when we used the list string for
example, this was an interface that takes a generic type parameter.
And the usage is the same as it is for class.
Now we also used a single bound when we specify the team top parameter, so
restricting teams to being created for
objects that we'll inherit from the player class or some class of player only.
Now Java does allow multiple bounds,
although say you can have class T extends Player in coach and manager, for example,
to allow multiple instances of player coach or manager to be added to the team.
But with that in mind, the normal inheritance implements rules still apply.
So in other words, you can only extend from the single class but
multiple interfaces.
So basically if you wanna do this with more than one class you'll need to use
interfaces for those other types as well.
And the other thing to keep in mind is if you're specifying multiple bounds,
then the class must be listed first cuz otherwise Java's gonna raise an error
when you try to actually compile it.
In other words class first and then interfaces.
And the way you do that just briefly we're not gonna actually add the code.
They'll enable you put something like & Coach, & Manager for argument's sake.
That would be the syntax you would actually use.
But obviously you can only have one class, so
these two here with this scenario will actually have to be interfaces.
So I just wanna confirm that that is actually the syntax if you're wanting to
use multiple bounds.
So you put the player, in this case the class first,
then you actually put the interfaces after that if you wanted to do that.
Obviously, we're not doing that in this example so
I'm going to actually remove those.
And obviously in the case of moddable types, you have to then include code to
check what type of objects you're dealing with before in breaking these methods,
because it wouldn't be just in this case just the one class which was player and
we need a little more code to actually to do that.
And finally SoccerPlayer and
also FootballPlayer are both some classes of player as we know.
So obviously there is a relationship between them, so
either can be used when calling a method that expects a player for example.
But the important thing here to remember is that there's no relationship between
something like this.
So if you'd look at the list manager, List,
and List,
now I'll just add those even though we're not gonna do anything more with them.
So there's basically no relationship in this case between those two.
And the reason is because neither of them are a subtype of this player.
I do have a common parent, but the common parent in this case is List.
So I hope that makes sense.
The bottom line is for a code, probably a lot of the time with generics,
you'll probably be doing something like this where you'll only be extending or
using one class and maybe an interface.
But if you do need to use multiple bounds, you'll need to specify the ampersand and
then obviously also the interfaces as well.
And just one more thing we need to look at before we actually get to the challenge,
so the team class has got fields, check how many games were won, tied or lost, but
we haven't actually made use of them yet.
So we do include a match result method.
So let's make some calls on it now in main,
just to record the game results before we discuss the method ball.
So I'm just going to actually copy and paste some code here.
And we'll go back to main, paste this team in, paste this code and it should say,
like so.
And you're gonna change that to adelaideCrows.
Then I'm also gonna go adelaideCrows.matchResult.
And I'm gonna type fremantle, 2, 1,
and adelaideCrows.matchResult,
.matchResult(baseballTeam, 1, 1).
So, if we actually run that, basically it does seem to be doing something but
we're not actually getting a lot of feedback from it.
Now, the matchResult method, if we go back to that and have a look,
you can see that it does actually take a team object called opponent and
two ints which is our score and their score.
If our score is greater than their score and then we've won, so we've won,
as incremented as you can see in this code here.
Similarly for tied and lost, we actually go through that process.
The scores are equal, tied, otherwise we indicate that it's lost.
And that takes care of the team that matchResult's called from, but
the opponent also needs its fields updating.
And that's why we've actually got this additional call here.
So we do that by invoking the opponent's matchResult method as well.
And this time obviously we're passing null for the opponent argument.
And only when invoking the opponent's matchResult method, if opponent is not
null, otherwise it would end up calling the matchResult of the first team again,
on and on and on again.
We'll get into an infinite loop.
So let's actually just print and aug message here so
we'll put something like this, just to so we've got a bit more feedback.
We'll add a variable called the message string.
Message, and let's set that up
to be message = beat, if a tied,
we're gonna add message = whoops,
drew with, and last message = lost to, like so.
And if opponent is not equal to null we're actually gonna put the result there so
we're gonna put System.out, this.getName, + message, + opponent.getName.
Just so we've got that bit of output on the screen.
And notice here that we're also only printing the message for
the initial team, so we're doing that test for its null equal to null.
This makes sense, but because we're printing the name of the opponent,
we get an error if we take this part of the message outside of the check for
opponent be null.
So let's actually run this.
And you see we're getting some output now.
And drew, we've got a problem there with the way that I've typed in drew.
I need to put a space there.
So I'll go back to there and run it again.
And you can see it seems we're getting better feedback now.
Hawthorn beat Fremantle.
Hawthorn lost to Adelaide Crows.
Adelaide Crows beat Fremantle.
Adelaide Crows drew with Chicago Cubs.
Now, can you spot a problem with matchResult yet?
So the Adelaide Crows drew against the Chicago Cubs.
What's going on there?
But the Adelaide Crows played football, and of course Cubs played baseball.
So that's rather an odd match result.
So what we really wanna do here is make sure that only the correct team type
can be passed to the matchResult method as well, yeah, and
sort of any object of type team.
So you can probably figure out how to do that now.
So if we go back to our code again and
I'll just bring up the project window again and close down a run window.
Go back to team again.
So we need to do something in here.
We can probably figure out what we need to do to make sure that happens.
We need to make use of its generic type essentially which we haven't been doing.
So we're gonna change that to use the generic type which we've defined for
this class.
So now if we do that and go back to our main method, it should say we're now
correctly getting this error, and saying that Adelaide Crows,
well it's best to only be using football players cuz that's how we've defined
the Adelaide Crows up here as to be of team football player.
So Java's quite correctly indicating that it's now a compile time error.
So this video's gone on for long enough.
So what I'm going to do is stop the video here.
In the next video we'll continue on, and
we'll just finish off a few other things to do with the way we've set this up.
And then we'll move on to the challenge.
So, I'll see you in that next video.