Transcripts
1. Introduction: Hello there. Welcome
to my course on Java. Now if you click on this course, you probably have at least
a vague idea of what Java is and that is somehow important to your career and
your education. Well, you're not wrong.
Let me show you how Java is one of the most popular programming languages out there, used by pretty much every
single tech company, pretty much any tech
company's careers page is going to list Java as
one of the skills. Knowing how to program at
all is the skill that puts you on the fast track for
a very generous salary. The reason is that Java is
performant, feature rich, and relatively easy to
pick up compared to some other languages
such as C. That also means that you can pick up programming without going
through a week's long course. The essentials of Java can be distilled into just a few hours, which is what I've
done in this course. You'll use the lessons
in this course to build your very own interactive
game that you can then share with your friends
or put on your resume. One thing that recruiters look for are personal
verifiable projects, and this one fits the
bill quite nicely. You can choose to do
the entire project on your own if you
want to challenge. Alternatively,
I've also included four walk through videos. Help you out just in
case you get lost. I've also set up
all code samples in an online sandbox environment.
Now, what does that mean? It means that there's no
need for complex computers. No arcane set up instructions. All you need to do
is visit the links I provide and click
the green Run button. You can spend all your
energy focusing on the actual coding instead of wasting time setting up
a coding environment. I hope you're excited
to learn about Java. I spend a lot of time putting this course together and
I hope that it shows. I'll see you in the next video.
2. About You and Me: Hello there. In this video, we're going to
talk briefly about the intended audience
for this course, as well as why you should
choose this course out of all the available courses that you probably see out there. So firstly, why
should you trust me? You don't even know me well. I work for one of the
largest tech companies in the world that is
currently based out of Cupertino, California. I'm not really allowed
to say whom I work for. But suffice it to say that advertent code
that has impacted the user experiences
of millions of people out there,
maybe even yours. I have a lot of experience
writing code that matters. Before working in
the tech industry, I actually taught
undergraduate computer science at the University of California, Berkeley for four years. The course I taught C 61 was named one of the
top five courses in the United States
by Bloomberg and regularly saw more than 2000
students each semester. I also recorded Youtube
video walk throughs for that course under the channel
CS 61, a departmental. As of August 2023, that channel has received
more than 963,000 views. Finally, in 2020,
I was named among the top 13 instructors in the university's
engineering department. Out of several
hundred, I have a lot of experience teaching and
managing large classes, especially those
with a significant online component
such as this one. Now why should he
choose this class? Well, I alluded to this
in a previous video, but one significant
advantage of this course is that all the code is
public and executable. Now, you may not understand what that means or
why it's significant, and that's totally reasonable. Basically, one problem
that often occurs in programming courses
is that even if you take the instructor's code exactly as it is and put
it in your computer, you might not get
the same output or maybe the code
doesn't even run. The reason is that computers
are very complex machines. Often your computer
might not be configured exactly the same way as the instructor's
computer is configured. In this course, all the code has been moved to an
online environment, including the project code. All the coding set up
has been abstracted away for you and
taken care of so you can focus all
of your energy on programming instead of
setting up your computer. If you want to follow along
interactively in this course, I recommend you set up
an account at Repl. I'll drop a link in
the course description as well as into resources. Next, I want to
talk briefly about the intended audience
for discourse. Now don't be alarmed
by that statement. Even if you have zero
experience in coding, you should still be able to
get a lot out of this class. That being said, if you really want to cover all your bases and make sure you understand all of Java syntax from the bottom up, I recommend you take a
look at my other course, Java for Beginners
Code in 1 hour. That class will
teach you some nitty gritty details
about programming. However, you don't need to know everything from that class to understand things
in this class. What I recommend you do is
watch a couple of videos from this class and see if you understand the significance
of what I'm saying. If it turns out that you
don't understand anything, I recommend you take a break. Check out a couple of videos from the course that I filmed earlier and come back to this
class when you're ready. And that's it for this video. The next time you see me, we're going to be jumping into
some actual content.
3. Project Introduction: Welcome friends. In this
video we're going to talk about the project
that you're going to be implementing
throughout this course. The project we're going
to be implementing is called Minesweeper. Feel free to look it up
on Google since it's a pretty popular game and
not exclusive to this class. Even if you're already
familiar with the game, I would still recommend you
watch this video so that you understand our
specific implementation. The code in the
project you'll be given has been
partially completed, but is missing some
important pieces. Your objective will be to fix these critical
components to make the game work as it
does in this video. We'll walk through
those components at later points in the course together so you don't get lost. At the end of the project, you should be able to play the game interactively and
invite your friends and family to play it as well. Now let's see how
this game works. Let's go ahead and click
Run our game of Mind. Sweeper begins with a ten by
ten board of untapped cells. All these cells, marked X are cells that have
not yet been revealed. These cells, lettered A through J on the
edges of the board, assist you in identifying
the coordinates of a cell. Our coordinates are going to
have the form row column. For example, this
cell right here is going to have coordinates
because it's in A. In column B, all cells
are either number cells, empty cells, or mine cells, but their state is
currently hidden by the X. If you tap on a cell that is a mine cell, you lose the game. The objective of the game is
to find all non mine cells. Let's see what happens
when we tap on a cell. We can do tap B, okay? B was revealed to
be an empty cell, which we can see because,
well, it's empty. An empty cell is one that has zero mines immediately
adjacent to it. That means that cell A, cell B, A cell AC, cell BC, and cell BB all were not mines. That means that all of
them were safe to tap. Notice that after
we taped cell B, a bunch of other cells were
also revealed as well. All of these right here,
all these empty cells as well as these number
cells right here. We do this because
like I just said, an empty cell means that all adjacent cells to that
empty cell are safe to tap. We just tap them for
you to save time. If we find an
additional empty cell next to the original
empty cell that we taped, we additionally
tap all safe cells around that cell as well. And we continue
this process until we run out of safe cells to tap. Let's take a look at
this cell for example. This cell was revealed
to be a number cell. The number two means
that there are exactly two mine cells
immediately adjacent to it. That means that these cells are all potentially mine cells. This one is also
potentially a mine cell. How do we figure out
which one is safe or not? This is the crux of
the mine sweeper game. We need to combine
information from other cells in order to deduce
information for this cell. Here's one way we can deduce
which cell is a mine cell. Let's take a look at
this cell right here. This cell has also been
revealed to be a one, but since these cells around it have already been revealed
to be empty cells, this has been revealed
to be a number cell, and this has been revealed
to be a number cell. There is only one cell left
that is adjacent to this one. Therefore, this unrevealed
cell must be the mine cell. Since we don't want
to tap on a mine, we're going to flag the cell as a dangerous cell so that
we know not to tap on it. We can see that this
X right here is in row and column H.
We can do flag F, H. This triangle symbol will indicate to
us that the cell is flagged and we know
not to tap on it. Now, since we know that
this cell is a mine cell, we know that this cell is
going to be safe. Why? Pause the video and I'll
reveal the answer in 321. The reason is that this number
cell right here is a one. We already deduced that this was a dangerous cell because of
this number one over here. Therefore, because we
have already found the one cell that is adjacent to this number
cell that is a mine, we know that every
other cell around this number one is going
to be safe to tap. Let's go ahead and
test our hypothesis. Let's go ahead and tap I. Indeed, we see that I was
revealed to be a number cell, which is three that
was safe to tap. Although there are many
mines still in this area, in the vast majority of cases, you'll be able to continue
this process of inference for the entire board and eventually identify where all
the mines are. If you think you made a mistake, you can also unflag
the cell like so. Now, H has been reset to an unrevealed cell
for demo purposes. Let's see what happens if
you tap on this mine cell. These asterisks
represent minds in our board and we can see that
we now have a game over. Let's restart the game by
clicking the Run button. Now I'm going to speed through this next board really quickly so you can see the end result. Feel free to watch this video at very slow speeds if you
want to follow along and see if you can
determine why I made decisions to flag or
tap certain cells. Finally, we can see that we have tapped all non mine cells. We have won the game in total. Each game should
have ten mine cells. Okay, And that's
it for this video. I hope this gets you excited
about doing the project. I spend a lot of time putting
this together for you and I hope you learned a
lot about Java by doing it. I'll see you in the next video.
4. Inheritance Illustration: Welcome friends. In this video, we're going to talk
about inheritance. To make this lesson
a little less dry, we're going to talk
about ice cream. This is ice cream. Now imagine that you
have a friend who has never heard of ice cream and ask you to describe it to him. Each unit of ice cream comes with a number
of common traits. For now, we'll simplify
and it should be pretty safe to say that every
ice cream unit has cream. A temperature of about 0 Celsius and a cone to hold the cream. Now imagine that you finished describing ice cream to
your imaginary friend. But now he's curious about
different types of ice cream like strawberry ice cream
and blueberry ice cream. You would probably
describe it in much in the same way as you describe the original ice
cream over here. These new flavors also have a cone and a temperature
of about 0 Celsius. The only difference is that the strawberry ice cream has
strawberry flavored cream, and the blueberry ice cream
has blueberry flavored cream. This is all fine and dandy
for this particular example. If we only have three attributes
to describe ice cream, it doesn't hurt to repeat
ourselves for each, but you can imagine
that we might want to add even more traits to
describe this ice cream. Maybe we want to also add
a cone flavor attribute, or a cone texture. Or maybe the weight
of the ice cream, or the volume of the ice cream. You can get infinitely more
descriptive of ice cream. And every time your friend asked you to describe a new
flavored ice cream, you would need to
redefine every single one of those traits
over and over again. It would be really nice if we had a way to reduce
the amount of work we have to do to describe
new ice cream flavors. This is where inheritance
comes in naturally. At some point,
you'd get tired of describing the same
characteristics for ice cream. Instead of reiterating that
ice cream has a cone and a temperature of 0 Celsius,
you tell your friend, well, blueberry ice cream is
just like regular ice cream, except that the cream
is a different flavor, everything else is the same. Inheritance is a paradigm in Java that allows us to do this. Inheritance means that
behaviors and attributes of a base model get copied to
a newer, updated model. We still have the
ability to overwrite select attributes such as the
cream flavor in this case. So this is pretty awesome now, instead of repeating
ourselves and redefining every single ice cream attribute when we define a new
ice cream flavor, all we have to do is
override one attribute, the cream flavor attribute, and inherit the rest of the shared attributes from
the base ice cream model, like the temperature
and the cone. This sounds really simple, but it's a very important
tenet of Java programming. Reducing repetitive code
is so valuable to recap. Inheritance is how
we maximize code, reuse and simplify your code. I'll show you some concrete
examples of what this looks like in code
in the next video.
5. Inheritance Examples: Hello friends. In this video, we're going to discuss some concrete
inheritance examples. First, imagine that we have a class called dog and say that it has a
static field called sound and a get method called
get Sound that retrieves the sound in case you forgot or aren't familiar
with the syntax. I recommend you pause
the video now and watch the video titled Objects
part Two in my other class, Java, for beginners in 1 hour. Now we further have another
class called Brown Dog, which extends the dog class. This extends syntax is new. This is how we specify
inheritance patterns in Java. In this case, we would say that the brown dog inherits from dog. Don't worry about the
implications of this for now. Just recognize Dysntex. Now let's say the brown dog has one method called get color, which simply returns
the word brown. Now let's put this all together. We have the dog class
from earlier and we have the brown dog class that
extends dog in our main class. Let's create an instance of the dog class and one instance
of the brown dog class. Like so, if we print out dog get sound and brown
dog getcolor, the output is pretty
predictable. Dog do get sound. We'll just print out bark
and brown dog getcolor, we'll print out brown word. Magic happens is when
we do brown doggetund. Based on what we know so
far of Java programming, this looks like it
would fail, right? Since the brown dog class does not have a
get sound method, that seems like a
reasonable guess. It turns out that
if you run this, this last statement will
actually print out bark. The reason is that we have inheritance when one
class extends another, Java will first look in
the extended class and see if it has a method or field
defined if it doesn't. Java then checks
its parent class. In this case, we first check brown dog for the
get sound method. It doesn't have that method, so we check the
parent class dog dog has the get sound method. Use that method
which returns bark. I want to emphasize
here that Java will use the most specific
method it finds first. Remember that Java
looks in brown dog first for get sound
before it looks in dog. This means that if brown dog had a get sound method that
returns something different from the dogs get sound method and we call brown
dog dog get sound. Java would have used
the get sound method defined in brown dog instead. That was a mouthful.
If you're watching on 1.5 x or two x speed
and didn't catch that, I recommend you rewind 30 seconds and listen
to that sentence again. On one X speed, that's the end of our discussion
on inheritance. To recap, we use the
extends keyword to tell Java we would like to create an inheritance
relationship. Once the inheritance relationship
has been established, Java uses dynamic
method resolution to figure out which
method to use. It first looks in
the actual class we're calling the method from. And only if the method is absent does Java look in
the parent class. Here's the link to
the repel code, using this lesson for
you to tinker with. I'll see you in the next video.
6. Super Examples: Welcome friends. In
this video we're going to talk about super recall from the previous
lesson that we had a dog class that had
a get sound method. We also had a brown dog
class that inherited from brown dog had
a get color method. When we called brown
dog get sound, Brown dog re used the get sound method from
dog via inheritance. Also, recall the order in which Java resolves method names. If we called brown do get sound. Java would first look in the brown dog class
for get sound. Java would only look
in the dog class if brown dog did not
have get Sound. If brown dog did have
a get sound method defined and we called
brown dog get sound, brown dogs get sound would override the get sound
method of the dog class. Meaning that we would
execute the logic contained within brown
dog instead of dog. We can either re, use or override methods
of the parent class. But what if we want to override and reuse the logic
of the parent class? Let's see how it works. First, let's say we have
our dog class again, get sound method
that returns bark. Now say we have our
brown dog class which inherits from dog and also
has a get sound method. This method has a new keyword that we haven't seen before. Super. Super is how we reference the parent
class of the current class. In this case, super is really
an alias for the dog class. This means that super
do get sound is analogous to saying
dog get sound. Which means that super get
sound will return bark. The result of this entire
brown dogtetsund method will be bark brown bark. That's pretty much
it. We've learned about our new keyword super, which is used to refer
to a parent class when we're writing code
inside the child class. This keyword also allows us to create hybrid override methods. That is to say we're able to re, use code from the parent class and also add our
own custom logic. Here's the link
to the repel code in this lesson for
you to tinker with. I'll see you in the next video.
7. Subtype Polymorphism: In this video, we're going to talk about subtype polymorphism. Before we jump in,
please don't be intimidated by these very
fancy sounding words. The concept is
actually not that much more complicated than
anything we've seen so far, but the designers of Java couldn't come up
with a simpler name. Once again, let's begin
our illustration with a dog class that has a get sound method
that returns bark. We'll also have a brown dog
class that inherits from dog, and this brown dog method
returns brown bark. Putting it all together,
we have our two classes. We have a third get sound method located
in our main class. This third method takes in a dog instance and simply calls that dogs
get sound method. If we instantiate an instance of the base dog class and then pass that base dog class into this method as
you would expect, we would get bark as output. But now let's say we instantiate a brown dog instance and pass that object into
this get sound method. This is strange. The get sound method
here in the bottom right only accepts
arguments of type dog, yet the type of object we're
passing in is brown dog. Based on what we know of Java, this seems like it
should error, right? It turns out this
will not error. This line will actually
print out brown bark. This is due to something in
Java called dynamic method Resolution is an example
of subtype polymorphism. Subtype polymorphism basically
means that a subtype of a parent type can be
treated as a parent type. Intuitively, this
should make sense. A brown dog should have all
the properties of a dog. A brown Chihuahua should have all the properties
of a brown dog. A more specific thing
should at least have all the behaviors and traits of a less
specific related thing. Now back to this example, the reason we can pass in a brown dog into
this method that accepts dog types is that Java assumes specific
types of dogs, will be able to
call all methods of the base dog class when the
program is actually run. Java will then resolve instance methods using the
dynamic type of an object. Aka the type that an
object actually is a brown dog and not
the static type that we declare it to be
in this method dog. I'll say that once again because it's a pretty important concept. The static type of
an object is what we explicitly declare
the object as. In this method, it's brown dog. In this method, the static
type changes to dog. The dynamic type of
an object is what the object actually is
and it never changes. In this case, the dynamic
type is brown dog. To resolve an instance method, we use the dynamic
type of the object. You may be wondering
why we do this at all. Why on earth would we
confuse ourselves? Why not just call it
a brown dog when we instantiate it and just keep
it as a brown dog forever? The reason that we
might want to re, use this method for
many types of dogs. Imagine that on top of
the brown dog class, we also had white dog, blue dog, and yellow dog classes, all of which inherit from dog and all of which have
get sound methods. It would be really
annoying if we had to define one method that takes in an object of type brown dog and another method that takes in
an object of type white dog, and yellow dog, and blue dog. When all these methods
do the exact same thing, instead of repeating ourselves, we just create one method that accepts the type of
the base model dog. By having a more
general input type, we can maximize our code
reuse and just have one method that takes care of all the more specific cases. That's it for this video. To recap, we talked about
subtype polymorphism. It's a Java paradigm
that allows us to treat specific instances of an object as a less specific base object. We also talked about static
versus dynamic types, which dictate the way we
resolve instance methods. When referring to
an instance method, we use the method defined in the dynamic type of an object
instead of the static type. Here's the link
to the repel code in this lesson for
you to tinker with. I'll see you in the next video.
8. Abstract Illustration: Welcome friends. In this video, we're going to walk through a visual overview of
abstract classes. In this illustration, we're actually going to return back to our ice cream examples from the inheritance
illustration lesson. Recall that we have an ice
cream class and we also have strawberry ice cream and blueberry ice cream classes
that inherit from ice cream. Now if you think to yourself, what do these three classes
look like in real life? You can probably come
up with some idea of what strawberry and blueberry
ice cream look like. More or less, they look
something like this. These classes
reflect reality and they are anchored to
concrete examples. But what about the
ice cream class? What does non flavored
ice cream look like? You might be thinking
of vanilla ice cream, but even vanilla is a flavor. Vanilla ice cream would also be a subclass that inherits
from the ice cream class. But ice cream itself, with no flavor,
there's no such thing, at least not in your
local grocery store. In Java, we would actually model this ice cream
class as an abstract. An abstract class is something that cannot
be instantiated directly because an instance of that object doesn't
make any sense. Just like ice cream with
no flavor makes no sense. Instead, we can only instantiate subclasses
of an abstract class, such as strawberry or
blueberry ice cream. Once again, we're adding complexity to your
job and knowledge. It seems like all we're doing is limiting your functionality. And you may be asking
yourself, why. Why would we create
an abstract class that can't even be instantiated? Aren't we deliberately
limiting and slowing ourselves if we force ourselves to create a subclass before
using it for anything? The answer is that even
though an object like flavorless ice cream does not
have any meaning in itself, it still holds
value in two ways. The first is that
inheritance patterns still hold true here. Just like with
regular inheritance, we can define a field value on the base object and have subclasses inherit
for that field. Even though flavorless
ice cream does not exist, we can still declare
its temperature to be 0 Celsius and have subclasses
all inherit that temperature. This should make sense
since we don't need to have a concrete ice cream class to
determine its temperature. We can just say, in general, ice cream should be 0 Celsius and define that in the
base abstract class. The second way abstract classes are still valuable is that they can specify a contract
that subclasses must fulfill. For example, we can declare that all ice cream should have
a method defined that returns the color of the ice cream in the
base ice cream model. This method won't be
defined because there's no such thing as a color for
all ice cream instances. However, the
strawberry ice cream can have a get color
method that returns pink. And the blueberry
ice cream can have a get color method
that returns blue. If we had an additional
ice cream class that did not provide a get
color implementation, Java would fail to compile. That's a good thing
because we don't want our code to
compile unless we're absolutely sure that all
concrete ice cream instances have color defined for them. That may sound a little
confusing, but don't worry. I'll show you examples of how that works in
the next video. That's it for this lesson. We talked about abstract
classes and how they're almost objects in that they cannot be
directly instantiated, but can provide a contract
or template for subclasses. We'll see concrete
examples of this soon. I'll see you in the next video.
9. Abstract Examples: Hello friends. In this
video we're going to walk through some examples
of abstract class usage. And we're actually going
to shake things up today. Instead of walking through
slides and diagrams of code, we're going to be
hands on coding. Of course, I highly
recommend you follow along in your Repel
It account if you can. If not, be sure to
visit the Repel link. After this lesson is over, fork it and tinker
with the code. The first thing we're
going to do is visit Repl. It hit Create repel button, choose the Java template and name your Repel
abstract classes. Now that it's created, the
first thing we're going to do is create an
abstract dog class. We're going to do so by clicking the new file button over here and call it Java. The first thing
we're going to do is create an abstract dog class. The class declaration looks almost like a regular
class definition, except we're going to add
the abstract modifier. The abstract modifier just
means that we're going to have at least one abstract
method within this class. Let's first define an attribute called numb legs
and set it to four. Note that this is not
an abstract field. There's no such
thing. Only methods can be abstract in Java. Intuitively, it
should make sense why we defined as attribute
in an abstract class. Remember, we don't
need a specific type of dog to tell us how
many legs a dog has. Dogs in general have four legs. It's safe to define it here. Now let's define a regular non abstract
method called Get Sound, which is going to return bark. This is going to
behave exactly like any other non abstract method in a regular class.
No surprises here. Now finally, let's define
an abstract method. We're going to call it get
color and not define it. Again, this should make sense. It makes sense to define, get sound for dogs in general, because dogs generally
make the same sound. But what is the color of a dog? You can't answer
that without having a concrete instance of
a dog in front of you. Therefore, it's an
abstract method. Now let's create a new class, regular class, not
an abstract class. And have it extend our
abstract dog class. We're going to call
this guy a brown dog. And it's going to
extend our dog class. Public class Brown. Do note that the syntax for extending an abstract
class is the same as it is for extending a
non abstract class. Here we're going to provide a concrete implementation of the abstract get color method. The color of a brown
dog should be brown. We'll just have it
returned brown. Let's additionally add an override annotation
to this method. The at override annotation
is 100% optional, but it's good practice
to include it. The reason is that Java will
then check at compile time whether the method
you've annotated is actually
overwriting a method. Examples in which this would be useful would be when
you may be misspelled, the method or the method doesn't actually exist
in the parent class. It's especially important when overwriting abstract
methods because abstract methods don't have any logic defined in
the abstract class. We want to be absolutely
certain we've provided a concrete
implementation for the method. Now let's put it all
together in our main class. So the first thing we're going
to test is instantiating a dog like so. Let's
see what happens. Look, we got an error. It says that dog is abstract
and cannot be instantiated. This makes sense, because
as we've said before, abstract classes cannot
be instantiated directly. We can only instantiate
their subclasses. Now let's try instantiating
a brown dog instead, which inherits from this time the code compiled
without any issue. Again, this shouldn't
make sense. Brown dog is a subclass
of an abstract class. Now let's see what happens
when we call these methods. First, let's rename
this guy Brown Dog because it is a brown dog. Let's first take a look at
brown dog dot numb legs. If you recall, brown
dog dot numb legs should be this guy right
here inside the dog class. Since fields cannot be abstract, numb legs is going to act
exactly the same way as attributes defined in regular
classes are going to work. When we call brown
dog dot numb legs, Java will first look in
the brown dog class to see whether a numb legs attribute
exists. It does not. It then looks in
the parent class, then it looks in dog and it sees that numb legs does exist. This first line should print
out four. Let's test it. Indeed, we get four back. Next, let's try out
some method calls. Let's see what
happens when we call brown dog dot get color
and brown dog dot get. Sound As expected, brown dog
dog get color returns brown. That's the method that was empty in the original dog class but was overridden in brown
dog, brown dog, dog get. Sound Was the method we defined
in the parent dog class and that brown dog did not define explicitly. Quick recap. Brown dog has get
color, dog has get. Sound. We can see that regular methods act exactly
the same way as they used to. If it doesn't exist
in brown dog. We will then call the method
defined in the parent class. Do however, get color also acts similarly
to regular classes. We call the method that is
defined in the subclass. In this case, if we had
forgotten to override the get color method of the abstract class that brown
dog inherits from Java, would have thrown an error. Let's actually see what
happens if we did that. Let's say we misspelled get
color like so and we hit run. You see now we have an error. Brown dog is not abstract and does not override
abstract method, get color in dog. This is one of the benefits of using the abstract classes. We see our errors upfront
before we even run the code. That's it for this video. Today we took a look at abstract classes and
how to implement them, as well as some of the
errors that might arise when you misuse
abstract classes. Here is the link
to the repel code. I'd like for you to fork it and tinker around
with the code.
10. Minesweeper Walkthrough 1: Welcome friends. In this video we're
going to walk through the first part of
this project using what we've learned in
the last few lessons. If you want to follow
along interactively, you're going to need to make a account and
verify your E mail. The link for that should be in the class description and was in the intro video
for this course. If you don't want to
make another account, that's totally
fine too though Of course I strongly recommend
you do make an account. The first thing you'll
want to do after making an account is visit bit dot lee, minesweeper skeleton like so. The link is also in the
project description. Once you're at this page, click the fork button and it should take it to a copy of
the code in your account. Let's take a look at the
structure of this project. Have the files, main board cell, empty cell, mind cell,
and number cell. Much of the code in
Maine and Board has been implemented for you
and we'll tackle some missing pieces later
there in later walk throughs. For this video, we're going
to focus on the cell classes. As you might have
guessed already, a cell class represents one of the squares in the
Minesweeper game. Let's take a closer look. The cell class is
an abstract class. It declares a contract for
other classes to follow, and also provides
some default methods. Every concrete cell class must implement a two string method, which is its string
representation in the board, and a tap method which defines what happens
when the cell is tapped. We also have some
flags that have default values of false
and we have some methods that should be shared among all implementations
has been tapped. Flag, un flag numb
neighbor minds and increment numb neighbors. I want you to think
about the following. Why are these methods abstract and why are
these concrete methods? Pause the video and
I'll answer in 321. Well, if I ask you
what a cell in general should look like and minesweeper, could
you answer that? You can't. A cell is blank. If it's an empty cell,
it has a number in it. If it's a number cell and it has an asterisk, if
it's a mind cell, you need a concrete
implementation of a cell to determine what its string representation should look like. Similarly, the behavior for tap also depends on what kind of
cell you're talking about. For example, if you tap on a
mind cell, the game is over. But if you tap on an empty cell, we just help you tap all
the neighboring safe cells. On the other hand,
if I ask you whether a cell regardless of its type, has been tapped, can you answer that question?
Yes, you can. Regardless of the
type of cell it is, you can answer whether
a cell has been tapped. Similarly, the
behaviors of flag and unflag on a cell are the same
across all cell classes, regardless of their type. Okay, let's begin addressing these two comments
into cell class. Firstly, we see that there's a bullion method
has been tapped. When you have a method that returns the state of an object, that's a hint that you
should be looking at the fields of the object. In this case, we have
tapped and is flagged. What do you think
this method should return, given that information? Well, we should return tapped since its name suggests
that it's going to store information about
the cells tap state. Next, let's look at flag. It returns void. We don't have to worry about
a return value. Instead, this void
method to suggests that its sole purpose is to modify the state of an object. Given that we want to modify
the state of the object, what variable do you think
we should be modifying? Well, we should be modifying
the flagged field, setting its value to true. Similarly, the unflagged method is going to set the is
flagged field to false. That's all we have to
do for the cell class. Let's move on to the empty cell. First we see that we have to override all abstract methods. Let's copy over the
abstract methods and begin implementing them. We see here that there's
a comment to use this special character
for flagged cells. Let's start by
implementing that logic. How do you tell whether
a cell has been tapped? Well, let's take a look. We have this is flagged
variable that stores information about the cell's
flag state. Let's use that. If this cell has been flagged, we return this special
character right here. Otherwise, if this
cell has been tapped, then we return an empty space. Because it's an empty cell, if you tap on an empty cell, it should reveal an empty space. Finally, if this cell has neither been flagged nor tapped, what should
it look like? Well, recall from
our first video talking about this project, we said that all
cells begin as an X, which simply which
simply represents that, that cell state has
not yet been revealed. Next, let's take a look at, as the name suggests, what do you think this
method is going to do? Positive video, and
I'll answer in 321. The top method is going to
set the tapped field to true so that we can remember
the state of this cell. After we set the flag to true, we notice that this method
needs to return a bullion. Well, it's actually pretty
hard to tell here whether we need to return true or
false. I'll give you a hint. If you want to challenge, try visiting board Java and
look at its tap method. See if you can deduce what
the intended return value of the cell classes tap method should be on a successful tap. And pause the video if you
want to think about it. If not, I'll just
tell you the answer. The answer is in 321, we're going to return true, since a true return value indicates that the cell
was safe to tap on. Which we can deduce from
looking at Board dot Java. We see in board Java that
if tap result is true, then we continue looking for neighboring empty cells to tap. In other words, a tap result of true means that the cell
was safe to tap on. Otherwise, we wouldn't look for neighboring
empty cells to tap. Now let's move on to the number cell and
repeat the exercise. This time, we'll
want to override the abstract methods and
also numb neighbor minds. The tap method is going to be pretty much the same
as the empty cell. A number cell is
also safe to tap on. We turn true and flip
the tap bullion to true. The two string method will
also look very similar to the previous classes
two string method except that if it's been tapped, we should return the number of mines that are
neighbors to this cell. Conveniently, we
see that there is a field called numb
neighbor minds here. Don't worry too much
about how we use this method increment
Numb neighbors just know that we've already
implemented that for you. Basically, we just
call this method at the appropriate time when
planting mines on the board. Numb neighbor Minds gives
us the correct value. Since we want this method
to return a string, Numb Neighbor Minds
as an integer, we need to convert this
integer to a string. But how do we do
that? Well, let's use a fantastic
resource called Google. Let's search Java how to
convert an integer to a string. Let's go ahead and click
on the first result. Let's scroll down a little bit, and we immediately see
that there's an example right here, I equals ten. And we convert that ten to a
string using string value. Let's go ahead and
use that stream value of this number neighbor minds. This way this return value matches the return value
of the two string method. Finally, let's also override
numb neighbor minds, which we didn't have to do
for the previous class. Numb neighbor minds,
as the name suggests, returns the number of mines
that are neighboring cell. We have a field that
returns exactly that. Let's go ahead and return
num neighbor minds. Now we've completed
the number cell. Let's take a look
at the final class we're implementing
in this video. Mind cell is actually
going to have a lot of similarities with the previous cells that we just implemented. Let's go ahead and copy
these abstract methods. The mind cells tap
method is going to be different from
the previous classes. We're still going to be setting
the tapped field to true, but instead of returning true, we're going to return false. Since true means that
it was a safe tap, a mine is not a safe
cell to tap on. The two string
method is going to look similar to other classes, except that if it's been tapped, we should return an asterisk because that's its
representation in the board. Actually, that's all
we need to do now. We're done with all
our cell classes. We actually won't be able to
verify whether we've done it correctly until the very end of all these walk throughs. As a challenge, if you
want to see whether you truly understood what was
going on in this video, I recommend you re fork the
original code and see if you can fill in all the to do comments in the skeleton
without my help. Otherwise, I'll see
you in the next video.
11. Interfaces: Welcome friends. In this video we're going to talk
about interfaces. But wait, we haven't
even talked about interfaces before.
What are they? Interfaces are
classes that specify a contract and cannot be
instantiated directly. Now I know what you're thinking. Doesn't that sound exactly like the definition we gave
for abstract classes? You would actually be correct. Interfaces are very similar to abstract classes and that their fundamental
functionality is the same. Now you're probably asking
yourself why though. Why do we have such entities in Java that pretty much
do the same thing? It's almost as if the Java language designers are
trying to trick us. Well, it turns out that
there are some differences between abstract classes and interfaces. Let's go over them. The biggest difference is that abstract classes only
support single inheritance, whereas interfaces support
multiple implementations. In Java, a class can only
inherit from at most, one class that includes
abstract classes. Brown dog can inherit from dog, but we wouldn't be able to have brown dog inherit
from something else, like say brown
animal for example. Interfaces get
around this problem. Interfaces aren't inherited
like classes are. Instead they're implemented and a class can implement
multiple interfaces. In our brown dog example, we could have brown
dog implement a dog interface and implement
a brown animal interface. And then brown dog would
have two contracts to fulfill the dog interface and
the brown animal interface. Based on this information, abstract classes are useful when we have straightforward
object hierarchies. Interfaces are useful
when we have an object whose contracts should mix and match methods from
multiple hierarchies. Say we have a brown dog that
inherits from a dog class, and the dog class inherits
from a mammal class. This is a linear hierarchy, and it makes sense to implement our method contracts
as abstract classes. On the other hand, say
we have a brown dog, and we'd like it to fulfill the object template
of a colored animal. And we'd also like it to fulfill the template of an
animal with legs. Colored animals and animals with legs are two separate
object hierarchies. Of course, there are
many animals that should fulfill both contracts, but there are also animals that only fulfill one or the other. As you can see,
choosing interfaces or abstract classes
boils down to how you decide to design
your class hierarchy. Now let's see how we actually
use interfaces in practice. First, we define an interface
called animal with legs and declare a
method that must be implemented called get non legs. We'll also define an interface
called colored animal that declares a
method that must be implemented called get color. Now let's create a
brown dog class. The first thing I want you to
notice is that we're using the implements keyword
here instead of extends. The next thing I want you
to see is that here we've specified multiple
interfaces that must be implemented
by brown dog. If you try to do this
with the extends keyword, Java will throw an error. This is a feature
unique to interfaces, being able to implement
multiple things. Apart from the class
definition though, the rest of the class looks pretty similar to
what we've seen. Since we must
implement all methods declared in the interfaces
we are implementing, we define both get color
and get numb legs. We also add the override
annotation to be explicit that these implementations
are intended to override the declarations
in the interfaces. That's pretty much
it. In this video, we talked about interfaces, which are a new type
of class that declares a contract that must be fulfilled by classes
that implement them. Interfaces allow you to create multiple inheritance
patterns that you can't do with regular
abstract classes. Here's the link to the repo
code used in this video. Please visit the link fork the repo and
tinker with the code. I'll see you in the next video.
12. Enums: Hello friends. In this video we're going to talk about enums. Let's start off today's
illustration by defining a class called dog one, which has a get type method
that just returns brown dog. Let's say we also have
a class called dog two, which also has a
get type method, but this one returns white dog. Finally, we have dog three. Dog three get type
returns yellow dog. Yes, that's a yellow
with an H instead of a W. Now let's put
it all together. We have our three dog
definitions on the left and then on the right we
instantiate three dogs, one instance for each class. Let's say we want to
run some simple checks. We just want to call, get
type on each of these dogs. Then check the output. If the output matches
some expected value. We'll print something
for our first dog. We want to check whether dog
one's type is brown dog. For the second dog we'll want to check whether
it's a white dog. Finally, for the third dog, we want to check whether
it's a yellow dog. Well, since we
accidentally misspelled yellow dog in our dog
three get type definition, this condition will
be false and we do not print out that
dog three is yellow. That's not a huge problem in
a simple program like this, but I can give you a
higher stake situation. Imagine that your
website search traffic from multiple countries and you want to change
the language of the website based on the
country the user is from. Let's say that for
someone in Japan, you'd like to set
the language of the website to Japanese. Similar to this logic, you'd have a bunch
of if statements. Choosing the language
of the website based on user dot get country. Now let's say that we misspell Japan somewhere in our code. Now this Japanese user
is not going to get the right language and you just missed out on potential sales. It would be great if we could bulletproof our code
against such typos, and we have a tool that
does exactly that. Let's create an enum
class called dog type. Usually when we create a class, we write public class name. But here, note that we're
saying public enum, dog type inside this enum. We then define three values, yellow, brown, and white. Note that these three values are not in quotes, they
are not strings. It might still not be
clear how this helps. Let's take a look at
how we use these. Let's define a new
method for each of our dog classes
called get enum type. We can then see for dog one, this method returns
type brown and we return type white
and doc type yellow. For the other two
dogs, this might look basically like
what we had before, but there's a very
important distinction here. Enum acts similarly to a
variable or a field in Java, in that Java will perform a look up when
you reference it, when I say doc type brown, Java will actually look up the dog type enum
class and see if there is a value inside
of it called brown. If there is no such value, Java will fail to compile. That's one of the main
benefits of enums. Enums allow Java to
perform a compile time check that all the reference
values actually exist. This reduces the chance that you'll misspell your
reference values. Enums also mean that there's
one source of truth for constant values
instead of before when we manually wrote out each
string to compare values. We can now do
something like this, where we have a
stronger guarantee that the constant value we're
referring to actually exists. If we accidentally misspelled
doctype yellow here, where we put Y E L L
O H. Java would then fail to compile because it would check inside
the doc type enum. Realize that there
is no such thing as doc type Y L L O H.
That's it for this video. We talked about enums, which allow us to define constant values for
our applications. These constant values
are helpful because if we ever have a misspelled
reference to an enum, Java will throw a
compilation error and we'll know exactly
where to fix the issue. Enums also provide one source
of truth for our constants. You define your
constants in one place, and you always refer to
those constants elsewhere in your program instead of rewriting the value
over and over again. Here's the link to the
Repl code in this video. I'll see you in the next one.
13. Switch: Hello friends. In this video we're going to
talk about switch. Let's begin our
illustration with similar definitions
from the last video. We'll have a dog type with three color types and
we'll have a brown dog whose get type method
returns dog type brown. Now in our main method, let's instantiate a brown dog. We'll say if brown dog's type is white print that
the dog is white. If the brown dog's
type is yellow, print that the dogs is yellow. If the dog type is brown, print that the dog is brown. Otherwise, print that the
dog's color is unknown. Now, there's nothing actually
wrong with this code. It works perfectly fine. But note that here we have the same value in
every if clause. And not only that, we have the same logical structure
in each if clause, comparing the same value
to different enum type. Again, nothing's wrong with this from a functional
standpoint, but it just looks clunky. It turns out Java has
a solution for that. Here's an alternate way of
writing the same thing. Before I start talking
about this code, I want you to take
a look yourself. Can you guess what each
new keyword is doing? Pause the video and
when you're ready. Let's see how close you were. Firstly, this switch
clause at the top is basically just saying
that we want to compare this value
over and over again. We're going to compare
the value inside the switch parentheses to the values that follow
these case keywords. This first case clause is
pretty much equivalent to if brown dog dot get type is
equal to dog type white. The second is equivalent to if brown doggettype is equal
to dog type yellow. You can probably guess what
the last case clause means. Notice that in these case
clauses we don't say dog type white and
dog type yellow. We just say white and yellow. The reason is that
in switch blocks, Java will deduce the enum type, the value in the
switch parentheses, and expects you to just write the enum values without the
enum class name in front. That's a fairly minor point, but just keep it in mind if
you run into any errors. Finally, this default case is roughly equivalent to
else, but not exactly. It actually means always execute this case if the program
reaches this line. However, we only
reach this case in the Ls case because of
these break statements. But what are they? It turns out that these break
statements stop further evaluation
within the switch block if the code path
ever reaches them. For example, let's
say that we did not have the break in the brown
clause in the Brown case. If that were the case, then all this code would be executed. We would print out
that dog is brown. And then since there is
no brake line anymore, Java would continue executing
the code in the next case. The next case here
is the default case. We'll also print out
Unknown dog color. As you can see, if our switch block had a
lot more cases in it, it would be a big waste of
time if we continue to run the code even after we already found the type
of the brown dog. In general, when you're only expecting one case
to be matched, it's best practice to set
a brake line in each case. That's it for this video. In this lesson, we
talked about switch, which basically is just
a more concise way of writing repetitive
if else logic. Here's the link to the
Repl code in this video. I'll see you in the next one.
14. Minesweeper Walkthrough 2: Welcome friends. In this video, we're going to walk
through the second part of this project using what we've learned in the last few lessons, specifically enums and switch. This time we're
actually going to take a look at the main class. There's a lot going
on in this file. Before we begin, please take
a moment to read through it. It's okay if there are
parts you don't understand. Many of the variable names
are pretty descriptive and should at least give
you a decent hint as to what it's doing. Now let's go through
this together. First we create a board, we print the board, and then we begin an
infinite loop here. This scanner input line
basically is just telling the console to see what the user entered on
the current turn. Recall in our demo
we did things like flag row column and
tap row column. Whatever you enter into the
console will be saved into this string user input here, user input split just
means we're going to separate the one string
based on the white space. For example, when we have
tap space a space B, then we should end up with an array that has
three elements in it. The first element will be tap, the second element will be A, and the third
element should be B. That's why if the length
of the array is not three, we reject the command
and start the loop over. You may not be familiar
with this continue line. All it does is tell Java to skip the rest of the loop
and start at the very top. Again, we're going to go back
to the top of this y loop, Check for the next user input. Here we have row
equals negative one, followed by an empty loop. Column equals negative one, also followed by an empty loop. We'll come back to
this in a later video. It's not super
important for now. Just know that row and column are going to
be integers that represent the indices
of the board that the user would like to
perform an action on. Finally, we reached a part of the code that we
are interested in. Here we create a bullion
called safe cell. We have an empty switch
block that follows it. Below we say that if is safe cell is
false, then game over. This implies that
this switch block should be changing the
value of safe cell. Otherwise, is safe cell
will just always be true. And it would be redundant to
have this F case right here. Think about the last walk
through that we did. Can you think of the method that we implemented that returns a bullion value indicating whether a tap was
a safe tap or not? The answer is you
implemented the tap method. Recall that we had
this Tap method that records that
the cell has been tapped for number cells
and empty cells will return true for mind
cells, we return false. And if you explored
a code some more, you would have seen that in Board Java we also have a tap
method. And what do we do? First, we tap the
cell located at index row column and save the result of that tap into
this bullion tap result. If it was a safe tap and there are no
mines surrounding it, then we tap all
neighboring cells around it and return true. Otherwise, if we taped on
a mine, we return false. That implies that within
this switch block, we should be calling
tap row column, save the result of that
call into is safe cell. But recall, a switch block is used when we have
multiple cases to check what cases should we be checking within
this switch case. Once again, pause the
video and think about what we discussed was happening
earlier in the sloop. I'll reveal the answer in 321. Well, the only time
we get a value for is safe cell is when
we tap on a cell. But those are not the only
actions a user can take. Recall from the project intro
video that a user can also flag and unflag a cell if
you forgot what those do. Basically, flag
will mark a cell as a potential mind but
it won't tap the cell. Will remove a flag from the cell also without
tapping the cell. Because the user command
is going to take the form action row column, we're going to put command zero inside these
parentheses right here. Our first case is
going to be a tap. If the user input a tap as the command,
then we're going to say, then we're going to reassign the value of a safe cell to be the result of tapping on the board at
position row column. Don't forget, after we perform whatever action we want to perform
within this case, we need to leave a break
statement before the next case. If the user command was a flag, which method should recall? Well, here's a good chance to use our code reading skills. Let's see what other methods
are in the board class. We have an is solved
method right here, but that doesn't
really seem likely. Hey, here we have a flag
and an unfed method. Those seem to fit our use case, let's use them back
in our main class. In the flag case, we're
going to call flag column. Since this method returns, we don't really intend to use the return value of this method to do anything else,
we just break. Finally, we'll have
our last case, which is the flag case here. We'll do flag column, then we'll break as well. We do have one small
problem here though. It's totally possible
that a user could input some random string
as the input command, or the user might
have accidentally misspelled tap flag or unflag. But we want to make
this code more robust and this is where
enums come into play. Let's predefine some
possible values that we want to
accept from the user. Up here in the action, um, let's go ahead and
define unflagg, tap. It doesn't really matter what
order you write them in. Now here, let's convert these case values from
strings, enum values. This will guard against
potential typos. Let's go ahead and
change it into a tap. Let's make this into a flag, let's make this into an un flag. But now we have a small problem. Remember that Java will infer
the type of the value in these parentheses and check that that type matches the type
of these case values. But that isn't the case here. The type of command
zero is a string. The type of these case
values is an action enum. How do we convert a string
into an action enum? Well, this is a
fantastic opportunity to flex our Googling skills. Let's go ahead and Google
Java how to convert a string, a string, into an enum. Let's go ahead and click
on the first result right here. We scroll down. We don't really need
to read most of this, but we can see right
here in the code block, they're telling
you that they have an enum called Pisa status. Um, and it has a
value inside of it. Piece status enum. Ready? Let's scroll
it down some more. We see that we have this
example, Pisa status enum. Except this time we derive
the value of the enum, not by saying patatenum, ready, but piece of status, enum
value of string, ready. That's a pretty good
hint as telling us that the value method of an enum class will convert
a string into an enum. Let's go ahead and use that
instead of command zero, let's do action value
of command zero. Now that's pretty much all we had to do for this walk through. Unfortunately, once again, we won't be able
to see the result of what we did until we
finished the entire project. But I hope you'll stick
around to see the end result. That's all for this video. Thanks for watching and I'll
see you in the next one.
15. Exceptions: Hello friends. In this video we're going to talk
about exceptions. Let's revisit the example we
had in the previous video. Recall that our dog type
enum had three types, white, brown, and yellow. If the dog type matched
any of these three cases, we would print a statement and then break out of
the switch block. The only time we'll hit
this default case is if brown dogs type is none
of these three colors, but then we only have
these three types defined. When would we ever
encounter this case? Well, it could be if the type is null or maybe in the future we end up
adding an extra dog type. But forget to update
this switch block. But generally speaking,
we don't actually expect to hit this default
case in most use cases. If we do hit this default
case with this code, we're actually going
to get a silent fail. We'll have an extra line being printed out, Unknown dog color. But the program runs just fine. It doesn't fail to compile and there's no error
when we run it. We don't know whether
unknown dog color being printed out
was expected or not. Instead, what we should
do is throw an exception. Here's an example of one of the most common
types of exceptions, illegal state exception. Note this in text here. We say throw, which tells
Java to throw an error. And then we provide a new
instance of the exception with new and passing an error
message into the exception. Now if Java ever reaches
this line of code, an error will be
thrown when you run the program and the
program will stop. This is actually a good thing. Let's say you added an
extra dog type called Blue, But you forgot to
add a case for it. You want the code to stop
and tell you that there's an unknown type instead of continuing to run as
if nothing happened. It's hard to see with
this small program. But imagine you have a much
more important application. For example, let's say
there's an application that tells you how much
money you have in the bank. It's a lot better for
the bank to say there was an error loading
your account rather than silently failing and telling you that you have
negative $50,000 in your account for
both the developer and the user of the application. The explicit error is a lot more informative than
arbitrary weird output. The added benefit for
the developer is that the custom error
will actually tell you where something
unexpected happened. It also makes debugging
a lot easier. That's it for this video. We talked about exceptions which deliberately stop programs
with the custom error message. Although at first glance, these may seem like a terrible thing to add to your program. But in fact, they help ensure program stability by providing sanity checks and
providing hints to the developer in
the event of a bug. Here's the link to the repel
code used in this video. I'll see you in the next one.
16. HashMap: Welcome friends. In this video we're going to talk
about the hash map. The example today is pretty short and can fit in one file. The first thing I
want you to notice is this import at the
top of the file. The hash map is a
data structure that is included in the
Java Util package, which is a built in package. Next, let's take a look at
how we declare a Hashmap. As usual, we first declare the type of the variable
which is a Hashmap. Then we use these angled
brackets to specify the types of the keys and
values within this map. The integer in de
angled brackets specifies the type of the keys. The string in the brackets specifies the type
of the values. A key is basically
something we're going to use to look up values. For example, think of the
context list in your phone. When you want to look up
your friend's phone number, you first look for
his or her name. When you click on the name, your phone will then display
your friend's phone number. Your friend's name is a key that maps to a phone number
which is a value. In this case, we're going to be mapping integers to strings. Also notice that we used capital I integer
here instead of lower case t when we
declare integer types that are contained within other
objects as a key within a map, we use capital I integer, but otherwise
capital Integer and lower case T are pretty
much interchangeable. Next let's look at how we
instantiate a hash map. Notice that we still
include the angle brackets, but we don't put
anything in them. That's because this
syntax tells Java to simply infer the types
of the keys and values. From the left side
of this assignment, Java will know to
create a hash map with integer keys
and string values. In the next line, map put one yellow says that we
are now mapping one to yellow within the map map dot put to brown means that
we map two to brown. When we print map dot get one, we print out yellow. When we print map dot get two, we print out brown. It seems pretty simple
and it usually is. One thing that I want to
call out here is that these gets and puts happen
in constant time. We haven't really talked
about what this O of one notation means because it's not really a
focus in this class. You don't really need to know
in detail what's going on, but I'll give you
a brief overview. The Hash and hash
map is actually a mechanism in
programming that allows the computer to store
objects in a way such that it's very easy and fast
to retrieve them later. It still takes time to retrieve
and store those objects, but the time is pretty much
negligible in most use cases. Maps are pretty great
for storing data. All right, so that's
it for this video. We talked about the hash map, which is a data structure that stores and maps keys to values. It does all this
in constant time, meaning that the latency required to write
to and read from the map doesn't increase by much as we increase the
number of items in the map. Here's the link to the repel
code used in this video. I'll see you in the next one.
17. Map Addendum: Welcome friends. In this
video we're going to go over an additional feature of
Java related to hash maps. Here's the example we had in the previous video with a
little bit more code in it. First, let's import
Java util dot map. Next, let's take a
look at this line. Down below, we declare
a variable called x and give it a type of
map, integer, integer. We instantiate it
using map 1234. A couple of things to note here. First, a map is not the
same as a hash map. A map is actually an interface, and a hash map is a concrete implementation of
that interface. Second, a map created with
map of will be immutable, meaning that you cannot modify its keys or values
once it's created. This map of syntax looks funny, but it's basically equivalent
to mapping key one to value two and key
three to value four. If we do do get
one, we'll get two. Since one maps to two, if we do x dot get two, we'll get a null value because
two is a value, not a key. Since the key two does not
exist, we get a null result. Finally, docket three
will give us four, since keys and values
alternate in the map of creation call and we specify it three after the value two. That's it for this video. I just wanted to give
you an introduction to a fast way to instantiate
a map if you don't want to first create the map and
then fill it up with keys and values one by one
to quickly recap. Maps created this
way are immutable. When we do so, we alternate keys and values within
the map of call. Here's the link to the
repel code in this video. I'll see you in the next one.
18. HashSet: Welcome friends. In this video, we're going to talk
about the hash set. The example today is pretty similar to the one
in the last video. It fits in one file, and even the syntax is pretty
similar. Let's get into it. The first thing
you should notice is that just like the hash map, hash sets must be
explicitly imported. Hash sets also live in
the Java Util package. Next, let's look at the
variable declaration. We declare a hash set, and then within these
angled brackets we declare the type of the elements that will be
contained within it. We use capital I integer since we're wrapping the
integers in a container, just like lists or hash maps, when instantiating
the hash sets, we don't need to
specify the type of the elements on the right
side of the assignment. We can just put empty
angled brackets, and Java will infer the
type of the elements from the variable declaration on the left side of the assignment. The ad method allows us to
store items in the set. Note that Java will
not allow you to put an object of anything other
than an integer in the set. You won't be able to put
strings or bullion values. You would get a compile time
error if you tried that. Here, if we try adding 35 after we already
did so earlier, nothing will actually change. This is one of the key
features of hash sets. Hash sets will only
store unique items. If you try to add
the same item twice, Java will not error, but it also won't do
anything different. We also have a size method, and you can probably
guess what that does. It just returns the number
of items in the set. In this case, that's two. Since we only have
35.91 in the set, the contained method
returns true or false depending on whether
the requested item is in the set already. It's true for 35 and
it's false for 36. You can also remove
items from the set. If you call items
that remove 35, then 35 will no
longer be in the set. We can confirm by calling
items that contains 35, which will return false. Now one final key
takeaway for you. Just like hash Maps, hash sets also use the hashing mechanism. You don't need to know the
details but just know that these calls contains
add and remove, all happen in constant time. Meaning that the time required
to run these methods does not increase significantly as
the size of the set grows. This can be really helpful
when you want to use a data structure for really
fast look ups and storage. Say for example, you
want to check whether a certain advertisement has already been played for a user. That way, we can always
play unique advertisements. Well, one way you might
represent that is each user object has a hash set associated with him or her. That set contains
the advertisement objects that that user
has already seen. That's just one basic example. And we'll have another use case for hash sets in the project, which you'll see very soon. All right, and that's
it for this video. In this lesson, we
talked about hash sets which allow us to store
value for later look ups. They also have constant
time reading rights, which make them very helpful
in diverse use cases, like the unique advertisement
example we talked about. Here's the repo link for
the code in this video. I'll see you in the next one.
19. Minesweeper Walkthrough 3: Welcome friends. In
this video we're going to walk through one of the most impactful parts of the project. Using what we've learned
in the last few videos in board Java. Scroll down to this method called gather
neighboring empty cells. Let's take a look
at what it does. This Q class linked blocking Q might not look familiar to
you, don't worry about that. It's not that relevant
to the course. All you really need to know
about this right here, is that it's assisting us in performing something called BFS, or Breadth First search. That's breath, as in B, R, E, A, D, T, H. We didn't talk
about that in discourse. And it's not really
necessary that you know how it
works specifically. But feel free to
Google it if you're curious because it's
a very cool concept. In this method, our BFS
search is collecting all empty cells that are adjacent to the provided
row and column. Once it collects all
adjacent empty cells, it then looks for
empty cells that are adjacent to the empty cells
we've already collected. And then we continue to
look for empty cells adjacent to those empty
cells, and so on. As you may have guessed,
this is part of the code that handles a
tap on an empty cell. Recall that when you
tap on an empty cell, we typically get a huge chunk
of the board tapped for us. That's done via this method, which tells the program
where all the empty cells adjacent to the cell you
tapped on are located. Before we go on, I want
to acknowledge that some of you may be feeling a bit frustrated with
these walk throughs. Perhaps you feel like, hey, why didn't we talk about BFS
in the walk through videos? Why are we being introduced to new data structures and
algorithms in this project? Well, there are a couple
reasons for that. The main one is that
a huge part of being a programmer is being able
to read unfamiliar code, understand it at least
partially, and then modify it. It's not true that there's a finite amount of
Java you can learn. And only after
you've learned it, you'll be ready to tackle
all the code in the world. The truth is that there are a ton of smart people out there. And people use all sorts of hard to understand
algorithms and code. There's no magical amount of Java that you need to know before you're ready
to face the world. Learning to work
with code you're not 100% familiar with is
an everyday reality. If you've made it this
far in the course, I'm glad you're still here and
learning to learn with me. All right, now
let's finally take a closer look at some key
components of this method. Look at this variable called, especially the line right
here that says pole split. What that's doing
is it's removing a string from the
data structure, separating the string into components using the delimeter, and then saving the result of that split into
disvariable coordinates. For example, if
there was an item in the Q that was a string a B, then the result of
splitting by the would be a two element array whose first element would be a and whose second
element would be B. Here you can see
that in our case, A is actually a row index and B is actually
a column index. We use these indices to
select a cell in our board, and then we have this to do. It's actually really hard to understand what we
need to do here. If you've never seen BFS, don't feel discouraged
if you feel stumped. I'll tell you what's
going on here though. In this loop that follows
the to do we have this line A plus plus j. As you can probably guess, Ad will add the string,
we give it to Q. One thing you might not
notice immediately though, is that we might
infinitely add items to Q. And then we'll never exit this loop that checks
whether Q is empty. Basically, the idea is that we want to only perform
this nested loop here, at most once, for every
single cell in the board. But right now, if you've
already seen row A in column B, you might still
accidentally add a B to Q and run through this
nested loop once again. So we need to keep track of all cells that we've
already visited. We want to store
cells that we've already seen in the
data structure and then only execute this
double nested loop at the current cell has
not been visited already. Can you think of
a data structure that we've learned about that allows for fast insertion
and fast look ups? Think about it and I'll pause the video and I'll
answer in 321, the answer is a hash set. We can store cells that we've already visited in a hash set. That means that if you encounter a cell that is not
in the hash set, that means you've never
encountered a cell before. This way, we can
ensure we only run this double nested loop on
each cell at most once. Here's what we need
to do First up here we're going to instantiate our hash
set called scene. Then down here we're going to first check have we
already seen this cell? We can use the sets
contains method for T. Then we wrap this entire double nested
four loop inside of here and flip this
to a negative. If we have not yet
seen the given cell, then we add this cell to the scene hash set because now
we have visited this cell. Now that we've
visited this cell, we perform our loop. Now we ensure that
this loop will never be run again. Why is that? Well, let's see, 1 second. I think I got this indentation
messed up. There we go. Okay. Why does this work? Let's say we encounter a cell that has never
before been seen. It's not going to
be inside of scene. We add it to scene, and then we perform
this double loop. Let's say that we
accidentally add the cell that we just saw
inside, back inside Q. Then when we go through
the loop, again, we're going to that
we're going to pop the cell that we've
already seen out of the que. And then we're going to check if scene does not contain popped. Well, because we
already added the cell that we saw into the
scene has set here, then by the time we come back to this if statement
on the next round, this will return false. We won't be able to
execute this double nested for loop on that
cell another time. That's it for this video. Now we've accomplished our task. We're just one piece away from being able
to play our game. I'll see you in the next video.
20. Minesweeper Walkthrough 4: Welcome friends. In this video we're going to finally
complete our game. When you're done
with this video, you should be able to play
the game interactively and share the link with your friends so they can play too. All right, for the first
part of this walk through, we're going to
revisit Board Java. At the top of this file, you should see that we have this letter to num map defined. Recall that when
you play the game, our commands take the form. Action, row, column like a, B. Our row and columns
are input as letters, but we need to convert
these letters into indices. And our minesweeper board, our board indices,
are going to start from top to bottom,
left to right. In other words, A
maps to index zero, maps to index one, and maps to index
two, and so on. Here, we just need to define those mappings.
Let's do that. We have a mapping to zero, mapping to one Quick
thing to note, the fact that I left a new line between each pair of values, Key values is actually
not necessary. You can also just put
these all in one line. I just put it into different
lines to make it look nicer. Also, make sure that you have double quotes
around your keys. If you put single quotes, that's actually a different
Java type called a character. Next we're going to
go to Main Java, scroll down to where we
have integer row equals. This is where we do the
conversion from letter to number. Recall from the
previous video where we talked about this commands
array right here, when we split by white space, we break up the user command
into three elements. The command is the
first element, the row letter is
the second element, and the column letter
is the third element. Remember, our indices
start at zero. That means that the row letter is going to be at index one. In commands, let's retrieve the number mapping
of the row by doing board letter to nu map get commands one here. This letter to numb map is what we just
defined over here. It's going to take the row
letter that the user input and then fetch the
numerical index that that letter corresponds to that we just defined in
the other class. Similarly, let's do
the same thing for column board letter to
numb mapget commands to. Now we actually need to add a few enhancements and
correct a typo I made in previous videos in Board Java where we implemented gather
neighboring empty cells, change scene to scene cells. The last line of
this method here we actually expect a variable
to exist called scene cells. That was my bad, I apologize. Let's change the
scene to scene cells. Finally, in the cell
classes that we implemented in one of the
first walk throughs for this, make sure that you
have the tapped, the tapped case before
the is flagged case. This is because once
you tap a cell, that cell state is final. You can't flag or untap it. However, if is flag comes first
in the two string method, then that means if you flag a cell first and
then later tap it, the board is still
going to render the triangle symbol for
the board instead of the tapped state since it checks the flag bull before
the tapped bull. Don't worry if that's a bit
confusing, it's a tricky bug. The main point of this project
was to practice coding, not to find obscure bugs.
Don't worry about it. Now let's try clicking Run and Play our game. Nice. It looks like the
game is working. We're getting number
cells revealed, empty cells revealed at
all the proper places. We can also try flagging, and we get a flag in
the correct place. All right, so that's
it for this video. Thanks for making it this far. Now you can play the game
and share with your friends. I'll see you in the next video.
21. Module 2 Conclusion: Hello again. Welcome to
the end of this course. Congratulations. You've now
built a working mines fee per game that can generate more than 1 trillion configurations. And you can use
these to challenge your friends and show
off on your resume. And by the way, I'm
not exaggerating, I really do mean more
than 1 trillion. You can check my
math. You've also learned several ways
of designing code. Using inheritance, abstract
classes and interfaces. You've also learned about
useful data structures such as hash maps and sets. It may be hard to
believe me right now, but as you progress
through your career, you're actually going
to find that a lot of advanced technologies are
actually just built on top of basic things such as inheritance and
basic data structures, like the ones you
learned in this course. You'll just have to take
my word for it for now. But eventually, I hope that one day you'll see what I mean. Most importantly, programming is a skill in which everyone
is continuously learning. I don't promise to
have taught you everything there is to know
about Java programming, but I do hope I've given
you a clearer picture of the fundamentals so
that you're well equipped to learn even
further on your own, whether it's through
my future courses or through someone else's. Once again, congratulations
on finishing the course, and I wish you all the
best until next time.