- [Tim] So continuing on now,
before we actually sell some stock
let's check what happens if we add an existing item again.
If you remember, we've actually built in the
mechanism for that, for adding stock
to see if there's already stock in them,
it should add to the quantity.
So let's go back to our Main.java
and we'll go through and we'll
try and add some additional cups.
So I'm gonna go back here and add this code.
And let's actually add some more.
So you can see on line 19 we've
added 200, a quantity of 200.
Let's do temp equals new StockItem.
We'll do a cup again, 0.45 this time and seven,
so that should give us a total
of 207 with the 200 on line 19.
And obviously, I need to add that as well,
so I do the stockList.add again, paste that code in there,
and let's just try running that.
So that seems to be working nicely.
So let's try selling some.
So we're going to create a static method in here,
in this main class, to do that.
So let's go through and do that.
So I'm gonna do a public static int sellItem.
It's going to accept a basket, the string being the item
we're going to be selling and the quantity.
Quantity, like so.
And retrieve the item from the stock list first,
from stock list, StockItem equals stockList.get item,
and then we're gonna check whether it's a null,
and if it is, it means obviously it's not in our stock list.
So if it's equal to null.
Then we're gonna print an error message,
We don't sell plus item, because
obviously, it's not in our stock list.
And we'll return zero.
So we're returning zero as the quantity for this method.
And that will tell us, it will tell
the calling process that the item
was invalid, and it didn't actually
exist, because the quantity was zero.
So next, we wanna do a test to
see that we can sell the stock.
So the way we do that, we're gonna
put if stockList.sellStock(itemQuantity),
if that is not equal to zero, then
we're going to do basket.addToBasket(stockItemQuantity).
So again, just looking at cell stock,
we can go back in there and just
see what that was doing again.
You can see that it's actually literally
selling the stock, so it's actually
deducting it from the stock at that point.
So, we've done a test then.
To make sure it's not equal to zero,
which means we have got a valid quantity.
So at that point, it's been taken out
of stock, so the next line of code
on line 55 has to add it to the basket.
Then one other thing we need to do
is we need to return the quantity,
so we know how many were effectively sold.
Return quantity like so.
And we'll just put a final return 0 here.
Which means there was a problem.
If it gets down to that line of code, it
means it didn't have sufficient stock to be able to sell.
So that should pretty well make sense,
you should be self-documenting.
Sort of understand what's happening now with the sellItem.
So what we should be able to do now
is create a basket and sell some items.
So let's go and look at doing that.
So here, I'm gonna put Basket
timsBasket equals new Basket().
That's my name.
And let's try selling some items.
So sellItem, and the name of the basket.
timsBasket.
So the car that we know that we've got,
one of those in our list of items
that we added above, and one, quantity
of one, and we're going to print
out timsBasket, remembering it's
got a toString(), so that should enable us to see it.
And just to confirm, car did have stock.
They had two in stock.
All right.
And let's try another one.
We're gonna try it on a separate line.
So sellItem(timsBasket) again.
Car 1, that's basically selling another one.
And we'll print out timsBasket again.
Just check the quantity is two,
and now we should get an error.
So if we do a sellItem, try and do it
for a third time, we know that
the car's quantity is only two.
So we're going to do timsBasket car again,
the third time, and one, when that should
fail, so we'll see what happens when we do that.
And also let's try doing the same thing,
but with a product we know doesn't exist.
Say a spinner, which we know that we haven't got five.
And then print out timsBasket, just
to confirm that those two extra items
on line 52 and 53 weren't added.
And obviously, spinner isn't in the list of
our stock items, so that should fail for that reason.
And let's just do a couple more.
So we'll do a sellItem, let's try timsBasket
again, juice, which we know we've got.
Oops.
Juice.
And four.
sellItem(timsBasket) again, cup.
12 cups.
sellItem(timsBasket, "Bread", 1).
And we'll do a print out again of timsBasket.
And then let's finish it off with
a print out of the stock list.
Stock list like so.
So that should be that, so let's
actually run this and see whether it does work.
Okay, so looking at the output, the
output up here is starting from
what we've executed on line 46.
Shopping basket Tim contains one item.
We should probably fix that so it
says item if it's less than two.
You can say zero items or one item or two items,
so basically if it's one, we should change
that perhaps so it shows item, but we can do that later.
So car purchased, total cost.
Shopping basket contains one item.
Noting that it still contains one item
the next time around because it's still only
one item which is a car, but now we've
purchased two of them, and the total cost
there is now 25, two times $12.50.
And we've got an error here saying
we don't -- oh, I misspelt that.
We don't actually sell spinner, which is correct.
And notice that it also the third time,
it didn't add the car to the shopping basket,
which is what we want, basically, because
obviously at that point, it didn't
have any in stock and we can confirm that
there were zero in stock here for
the stock list and the bottom nerve,
value zero, because it's because we've sold the two of them.
And the shopping basket, we know that
we've added those other things.
We've added the juice, four of them.
We've added the cup, twelve of them.
And we've added the bread, one of them.
So we've got four items in total.
Two cars, four juice, 12 cups, and four bread.
Total cost is $41.26, and a new stock list.
Bread was 100 initially, that's now 99.
Cars, it was 0 in stock as I mentioned.
Juices, 32, so I think we had 44 first.
Sorry, we sold four juice, so that's 32.
There was 36 for starters on line 27.
There's now 32 in stock as you can see, so that's
correct, and the other one was cup.
We had 207 of those.
So there should now be 195 left.
Because remembering that we had
200, then we added another seven.
So the code seems to be working nicely.
Let's just fix that typo.
And we'll fix that, we don't.
That's my typo there, so we'll just fix that up.
"We don't sell " + item.
And looking at our stock list,
and here's the line in the basket.
We can change this, where it said it contains one item.
So if we've got zero items or greater
than one item, then we should show the
word "items", but if it's actually one,
list size is exactly one, then what we
should really do is just show the word items.
So we could do something like plus,
and we can go list.size() is equal to one,
is equal to one, we're gonna print item.
Item otherwise, we're going to print items.
Then we're gonna add the last backslash,
so if we do that, so if we do something like that now.
So basically, if the list size
is equal to one, we're gonna print out
the word "item", otherwise we're
gonna print out the word "items".
Then in both cases, we're still
gonna end the line with a new line.
So we'll just go back and just check that
that's actually fixed that other little bit of output.
It now contains one item, it contains four items.
So that's fixed -- made that work a little
bit nicer for us, so that's good.
And just confirmation that we don't
sell spinner, we get that message now,
that's correctly -- fixed the typo as well.
There's no spinners in stock, so we do get that message.
And obviously, we attempt to sell the
third car also failed because we've only got two in stock.
Probably ideally there, we probably
wanna put to a message out to say what's actually happened.
So we could come down to the sellItem, close this down.
And we could've done something along the lines here.
That third one, we could put if sellItem --
actually, what I'll do is I'll just read on
the code from the previous line.
So if store.sellItem, and if that
is not equal to one, I can do,
"There are no more cars in stock."
Just to verify that it's actually working.
So if we run that again, we should
now get an error message, or a message
on the screen to confirm that it didn't work.
"There are no more cars in stock."
As you can see.
So I've done that for that one line, but probably
that would be a good idea to do for
every call to sellItem, checking that
the return value was either equal
to the number requested or not
equal to zero, and it doesn't
matter which condition we use there.
And then putting a message there
if there's not enough in stock, just
be a little bit nicer than just not
printing anything in the case of it not working.
Now in the contents of the shopping basket
are printed, they actually appear in a random order.
And you can see here that that's just
sort of all over the place.
It's not sorted it all.
Now we could have them printed in the order
they're added in the basket by
changing the Basket class HashMap to a
linked HashMap, but this is a good
opportunity to see a TreeMap in use.
Now because we've already implemented the
compare interface in Basket, the only
change we really need to make is to the
constructor itself to get this to work.
So let me go back to Basket.
I'll just close this down.
Now at the moment, you can see the
constructor, we're using a HashMap.
All we really need to do to get that to work
is change that to a TreeMap.
So if we do that now, and just accept
the import, like so, and we can just
comment out -- I'll just delete the
HashMap now, TreeMap's in there.
If we run that again, we should find the
Basket is now in alphabetical order.
And you notice down here, now the shopping
basket is in alphabetical order,
and notice this output here.
The compareTo is now being called,
and that's because we're using a TreeMap
and it's sorting it for us automatically.
Which is seriously pretty cool.
So the point with this now is as you can see
is that adding items to a TreeMap
results in far more work for your code
than using a HashMap, and that's because
each time we're entering a stock item,
it's using the .compareTo method to check.
Basically, with the order, comparing that in other words.
Now the reason for that is because the TreeMap
has to actually check through its list to
find the correct place to insert the new item,
and of course it does that by comparing the
items it finds with the item being added.
Now although the TreeMap implementation is
quite efficient, this is the one created by Java, by Oracle.
Keep in mind that there is a performance
cost to having the items ordered and that's
something to consider when trying to decide
between a TreeMap and one of the unsorted
Map classes like HashMap and LinkedHashMap,
which is always gonna be faster
because it doesn't need to execute that extra code.
Now before we move onto the challenge in the
next video, let's talk about a couple of
other things that we should
really look at in a bit more detail.
Firstly, let's attempt to use the StockList class
items() method to bypass the correct way to add stock.
So what I mean by that is let's just close this down,
and right down here, let's try and
do -- get access to a product that we know doesn't exist.
So temp equals -- well, actually what
we're gonna do is try and save it.
So new StockItem(), remembering that
it's a unmodifiable Map, so we should
actually get a message if we try and actually
put some data in there, but let's confirm that works.
So new StockItem(), let's make a new StockItem().
We'll call it pen, $12.
And let's try and add it.
So StockList.items, remembering that
returns this unmodifiable Map.
Dot put, we're gonna try and save it,
so we're gonna put temp.getName, name obviously being
pen, and then the actual StockItem itself.
So let's actually run that and see what happens.
And you can see we do actually get an error.
We get an UnsupportedOperationException.
And that's good, because that's confirming
that it's working fine, because we
know that we setup the items,
and just to go back and confirm.
Going to StockList.
Let's close this down and look at our items.
Return collections.unmodifyableMap,
remembering that our list of items
and obviously that should've returned an
error, and we do actually get an error.
That's because it's obviously an
unmodifiable map, so you can see
that's a good security mechanism in place
to ensure that there's no other way
that items can be added to our basket other
than using the methods that we've actually defined.
With that said, there's one thing to be aware
of, though, when allowing calling
code to access your collections.
And this is even if you've
provided with an unmodifiable object.
Although the collection itself can't be changed,
in other words, items can't be added or removed,
it's only the collection itself that is unmodifiable.
Now the individual objects in the collection
can be accessed and modified, and I'll
show you what I mean by that.
So it's a little bit of a gotcha there.
So what we can do is we'll just comment
out these now 'cause we're not gonna
run those and we don't want to get the exception anymore.
So if we do something like StockList.items again,
.getCar(), so we're getting an existing
car, we know that object exists.
.adjustStock(2000), try it, give or take,
and then do a printout of StockList.
You can see that that has actually worked,
so even though the Map returned, the list is unmodifiable.
With the unmodifiable Map, we're able
to change an individual item there, so is that a problem?
Well, the answer is that it depends.
So in this case, the calling programme can't do anything.
With the StockItems via the items() method that
we hadn't allowed it do anyway.
We can see this by using the adjustStock()
method, so we'll just do that and show you what I mean.
Probably do something like -- let's try this.
So StockList.items.get("Car").adjustStock(-1000).
If we do that and then run it.
You can see that we've now got only 1000
cars in stock, and the adjustment of
2000 is something that the calling
programme could have done anyway, so
there's no problem in this case because
we had the functionality available to do that anyway.
So in other words, the point I'm making here
is that there's no problem in this
particular case with having access
to the objects via the read-only collection.
But if you design a system so that the StockList
didn't have a get() method, though, then you
wouldn't want the code to use the get() method
or the unmodifiable collection either.
So remember that it's the collection itself
that is unmodifiable, and not the
objects within it, so it's really
an important distinction there.
By providing the items() method, the StockList
class allows callers to retrieve individual
items, and if you don't want this to happen,
then consider another way to provide a list
of items, if indeed you provide that list at all.
That mechanism to provide that list at all.
Now let's see one possible option.
We're gonna add a price list method to the StockList.
So we'll go back to our StockList.
I come down here, we'll
actually do it before the toString().
We'll try public, so just to be consistent.
It's just to be brought up here.
Public Map priceList, we're gonna
do Map prices = new,
let's use a linked HashMap.
Then we're going to write a loop
to actually put all the items in there.
So for map.entry, and it's gonna be String stockItem
this time, item, should have been a comma there.
And list.entrySet, and we're gonna do
a prices.put, item.getKey() and item.getValue().getPrice().
And then from there, we'll do a return
collections.unmodifyableMap for prices.
All right, so going back to main, let's print that out.
Go down the bottom here, so we'll put for
map.entry, String double price
StockList.priceList().entrySet(),
and we'll print out price.getKey() + costsPrice.getValue().
And I'm missing a single -- a bracket there.
That's better.
So here, we're providing an unmodifiable map
of names and prices for the caller to use.
Now the entries in the map consist
of pairs of strings and double.
Now both of these are immutable.
So not only can't the map be modified,
but the individual entries in it can't be either.
So if it's important that access
to your objects in your collection is
restricted, then avoid returning any sort
of collection that contains the actual objects.
And then let me just run this to make sure it does work.
And you can see down here that it's worked just fine.
So I realised once this video was recorded
that I actually missed out a step.
Looking at the code on line 70 to 71,
you might be wondering earlier in the video
when I was talking about that, why did
I type both lines of code the same?
So I was trying to actually show
you that there's two different ways of accessing this.
And what I did was I actually made
a mistake and typed it the same way.
So the second example, the second line 71, works like so.
I just wanted to show you both ways.
The first way is returning it, the items,
and using the get() from there.
But the second way is just using the get() directly.
So I just wanted to point out that there's
two differences there, and again,
if you design a system so the StockList
didn't have a get() method, then you
wouldn't want code using the get() method
of the unmodifiable collection either.
That's the point I wanted to make.
But I just wanted to show you that
there was two sort of ways of accessing this.
So that's it.
That's the end of this video.
In the next video, we're gonna do a challenge
to help you understand what we've just learned.
So, see you in that next video.