"Java Objects (Retired)" was retired on January 31, 2017. You are now viewing the recommended replacement.
Heads up! To view this whole video, sign in with your Courses account or enroll in your free 7-day trial. Sign In Enroll
Well done!
You have completed Java Objects!
You have completed Java Objects!
Preview
Let's take care of a bug we found surrounding uppercase and lowercase guesses.
Learn more
Related Discussions
Have questions about this video? Start a discussion with the community and Treehouse staff.
Sign upRelated Discussions
Have questions about this video? Start a discussion with the community and Treehouse staff.
Sign up
So we ran into that slight
little issue that popped up
0:00
when we added an uppercase value as input.
0:03
The uppercase value T is different
than the lowercase value of T.
0:05
So we need to ensure a specific
case in the letters of our data.
0:09
So when dealing with input, what our end
user is providing us through our prompts,
0:13
like those letters, there are a few
things that we can and should always do.
0:16
Firstly, we should check
the correctness of the input.
0:21
This is called validation.
0:23
Input that has been checked
is said to be validated.
0:25
Another option is to modify or
transform the value so
0:28
that it becomes a valid value.
0:32
This process is called normalization,
and the data is said to be normalized.
0:34
So in our, hey, you already guessed
that letter exception example,
0:38
we validated the input and let the caller
know that the validation had failed.
0:42
Now, we're looking at a place where
we need to normalize the input.
0:47
We need to normalize
alphabetic case of our values.
0:50
So, what we'll do is when we start
the game with new Game("Treehouse"),
0:54
we'll ensure that the value is lowercase.
0:58
And then we'll also make sure that any
time someone calls applyGuess on the game
1:00
object like so, we'll take whatever
they gave us and make it lowercase.
1:05
A great practice to make sure things are
working as you anticipate is to write what
1:10
are known as tests.
1:14
Tests exercise your code and
1:15
make sure that your code works
exactly as you expected it to.
1:16
We, unfortunately, are not going to
get into writing tests in this course.
1:20
But we do offer content that covers
testing, check the Teacher's Notes.
1:24
So for now, while we're coding up this
validation and normalization a bit,
1:28
we'll do some quick manual
spot check testing.
1:32
I'd like you to get a feel for what kind
of tests we might actually write, and
1:35
show you why they're such a critical
part of writing applications.
1:39
Let's get to it.
1:42
Okay, so let's get to work on this bug.
1:44
I'm gonna pull this over
into In Progress column.
1:46
And first off,
1:50
let's make sure that the answer
that's passed in is always lowercase.
1:51
And we can do that by this,
toLowerCase, right?
1:55
Remember that on strings toLowercase.
1:59
Now to tackle the guest side of things,
2:01
one thing that we haven't even checked is
if our user is even giving us a letter.
2:04
So let's go explore the character class,
the documentation for the character class.
2:09
So if we go here and we go Java 8,
and we search for Character.
2:13
So this first result here.
2:19
So the character class is a wrapper
class for our primitive data type char.
2:21
Now much like how we saw integer
wrapper for a class for ints, right,
2:26
where we used integer.parseInt.
2:30
Well since a primitive char
doesn't have any methods,
2:32
this class is used to provide
helper methods for chars.
2:35
And it does it through, guess what.,
let's go look at the methods here,
2:38
through static methods.
2:43
So, here's some common getters and things.
2:45
But what we're looking for
is we're looking for something that says,
2:47
is this a letter?
2:51
So, if we look through here and
we say, is letter.
2:52
Hey, look at that, it takes a char and
it returns a boolean of true or false.
2:55
So, let's try it out.
3:00
Let's pop over to jshell.
3:01
So, we come in here and we'll say,
Character.isLetter, and
3:02
I sure hope that a is.
3:07
And boom, it returns true,
cool, just like we thought.
3:09
So what happens about the character 2 or
a weird @ or something?
3:12
Okay, I'll submit it works for
uppercase too right?
3:17
Great, so I wonder if there's
a toLowerCase for chars,
3:21
just like we looked at for
the strings, I wonder if that exists.
3:24
So let's come here and
I'm just going to do a search.
3:28
And a look for to, look at that,
there it is, toLowerCase.
3:30
And it's a static method
off of the char and
3:33
it converts the character
argument to lowercase, cool.
3:35
So we could also for over here we
could say a character too low and
3:38
just tap complete and
boom there it is to lower case and
3:42
if we give it an a That should
return as a lowercase A, beautiful.
3:46
Okay, so with that information, we should
be able to get a valid lowercase letter
3:51
and throw an exception
if in fact we don't.
3:56
You know what?
Now is a great time to show off a concept
3:59
that we haven't touched on yet,
and that is private methods.
4:01
So our game object here currently is doing
some validation inside of this applyGuess.
4:05
Let's go ahead and
let's make a new method and
4:11
we'll kinda group our
logic together there.
4:13
So we'll make it private.
4:16
And by making this private, it will not be
accessible to anyone but the class itself.
4:18
So we'll say private is gonna return
a chart that has been normalized.
4:23
All right, so let's say normalizeGuess.
4:28
And it's gonna take a char and
it's gonna return a char.
4:33
So again, because it's private, the
prompter, like for instance, the prompter
4:37
can't say game.normalizeguess,
only our code here can.
4:41
So this method is a good way to
group common functionality and
4:45
not clutter up your other methods.
4:49
Here let's build it.
4:51
So first we wanna know if
it is not a letter, right?
4:53
So if it is not a letter,
gonna pass on letter.
4:58
Ideally this would kinda be
a custom exception, but for
5:05
now we'll just do a throw new
IllegalArgumentException, right?
5:09
They passed in a illegal
argument to make a guess.
5:13
So we'll say a letter is required.
5:16
So now that we know it's a letter,
we can transform it to lowercase.
5:22
So we can say letter =, again using
that same character wrapper class,
5:25
toLowerCase(letter).
5:30
Awesome, great, now we already have
some validation and applyGuess,
5:35
why don't we put that in
here in the normalizeGuess.
5:39
So I'm going to cut that out,
and I'm gonna paste it here.
5:42
And then finally our normalizeGuess
needs to return the letter, great.
5:46
So now in applyGuess we can just
call our private method there.
5:52
We'll say letter = normalizeGuess(letter).
5:56
See how it keeps things readable?
6:02
We could very easily just jam all of these
lines in this applyGuess method here.
6:04
But see how nice and concise this is?
6:08
Methods provide a great
way to name grouped logic.
6:10
So how about we do this, let's add
a little smarts to our prompter object.
6:14
If we come over to prompter object and
we look here at promptForGuess.
6:18
What if we made this thing just keep
trying until it got a valid guess?
6:21
Sounds good, right?
6:26
So there's a couple ways
to do this approach.
6:27
But first let's move this isHit up the top
here, let's get this stuff down here.
6:29
So we move isHit to the top.
6:36
And I'm gonna bring this scanner
definition to the top too.
6:38
Let's go ahead and do that.
6:42
It's kinda common to the whole method,
so we'll bring him to the top.
6:43
We'll set up a new variable there.
6:47
And let's do a trick here.
6:49
Let's store a new value that we're gonna
use to keep track of state in this method.
6:51
Did we get an acceptable value?
6:58
So we're gonna set that
to default to start with.
6:59
Okay, so like we said,
we wanna do a loop here.
7:02
We wanna loop through things, and
we wanna make sure that it happens once.
7:05
So while we don't have
an acceptable guess, but
7:09
we wanna make sure it happens once,
so that's a do while loop.
7:12
So again, that starts like this,
you say do.
7:15
And then we kinda wanna run all this
stuff, don't we, to right about here.
7:18
And then we're gonna close that do loop,
see how it's highlighted?
7:22
And we'll say while,
we'll go not isAcceptable.
7:25
Speaking of not acceptable,
look at this lineup of this code.
7:31
Let's go ahead, and
I'm gonna highlight this.
7:35
And I'm gonna do a command bracket or
a control bracket, right bracket.
7:38
And it will move,
you can move left or right with that.
7:45
There we go.
7:49
So what we wanna do is we wanna
make sure that we flip or
7:51
change the isAcceptable
value to true here.
7:56
And what's happening is
if it comes through here,
8:02
we know that it's valid,
otherwise it's coming here.
8:04
And now, since we're looping,
we can try to give a cleaner message.
8:07
So let's switch this to a printf.
8:10
And we'll put a format string here of,
we'll say, here's your error message.
8:12
Please try again.
8:16
The famous Please try again.
8:19
All right, so we're gonna pump in there,
just like we were before,
8:21
the exception message that we set, okay.
8:26
And we're still returning the isHit.
8:31
So this is gonna loop and
will return isHit, okay.
8:33
Let's mentally walk this really quick.
8:36
So this method, promptForGuess is called.
8:39
And we set up a variable
to know if it's a hit and
8:43
we set a variable to
know if it's acceptable.
8:45
Our first iteration through the loop,
8:49
which will always happen because
we're using a do while loop.
8:51
It's going to get an input.
8:55
It's gonna pull off that input.
8:58
And because our input returns a string,
we have to use this charAt.
9:01
Sure would be nice if applyGuess took
a string for us, but it doesn't.
9:07
So anyway, applyGuess takes the guess,
and in here, it does some normalization.
9:10
So we pass that single charge through and
it has a way of normalizing our char.
9:17
Let's take a look at that one more time.
9:21
As we come through applyGuess,
it comes in here,
9:23
it calls this private
method normalizeGuess.
9:25
And we pass that original guess through,
9:28
it comes through here it does
all of our checks that we want.
9:30
If it makes it all the way through here,
we have a valid letter.
9:33
If not,
it throws an IllegalArgumentException.
9:36
Now because one of those exceptions will
break out of this try block, it will run
9:39
this message, and this isAcceptable
will never be flipped to true.
9:43
So therefore, when it comes back down
here to check, it will go again, and
9:47
again, and again.
9:52
So let's walk that really quick with one.
9:54
So if the person entered in a 1 here,
and they came and guessed, and
9:56
the guesser says, trying to be tricky for
some reason, put in a 1.
9:59
They hit applyGuess,
they come on over to game.
10:03
It would go to normalizeGuess,
isLetter, false, a letter is required.
10:05
Here it comes, a letter is required,
please try again.
10:08
And notice that it jumped
over the isAcceptable.
10:12
So isAcceptable is still false.
10:15
So take a second and think about an app
that you've used that takes your input.
10:18
Now this loop is pretty common right?
10:22
Whether it be on the web or
on an app on your phone or tablet.
10:24
Do you follow it?
10:27
It is totally understandable for
you not to grasp this immediately.
10:29
This very well may be the first
time that you're thinking about
10:32
this side of the application.
10:35
But I know that you've
filled out web forms before.
10:37
So you've had this experience
that we're creating.
10:39
So if you need to, go ahead and pause me,
and try to walk through each line there
10:41
on your own in your head, or
out loud if you aren't in public.
10:45
Rewind me a bit if you wanna
hear me explain it all again.
10:48
Okay so, let's give this a test run.
10:53
I am going to do a clear && javac
Hangman.java && jave Hangman.
10:56
So let's make sure that we
can't guess a number, right?
11:10
That was our first exception.
11:12
A letter is required, beautiful.
11:14
Nice, and there it is,
it'll keep going and keep on asking me,
11:16
it's pretty insistent there.
11:19
So that works.
11:20
So what happens if I give it a capital H?
11:21
Awesome, it lowercases it.
11:24
You know what, actually we moved the code
that tests the checks of duplicate
11:26
guesses into that normalizeGuess method.
11:30
So we'd better test that too,
to make sure it's working.
11:32
So I'm gonna guess an h again.
11:35
Cool, it says h has already been guessed.
11:36
And then I'm gonna guess t,
and then Guess it again.
11:39
Cool, and we're still at seven tries.
11:41
Nice, we did it.
11:44
Okay, great, and I just wanna point out
if we had actually written a test for
11:45
all that,
we'd know that we didn't break it.
11:50
Because we could just
run a series of tests.
11:52
But as it stands, we have to test that
every single time we change our code.
11:54
We would write tests for one, and
we'd write tests for uppercase, and
11:57
we'd make sure that that worked.
12:00
In fact, we'd also test what happens if
somebody just came here and pressed Enter.
12:02
No, look, it's trying to get the first
12:07
character out of a string that
doesn't have any characters.
12:10
Well that code was the code we
were wanting to clean up anyway.
12:14
So let's go ahead and
let's close that issue.
12:16
And unfortunately,
lets make a new one that says,
12:20
BUG: Sending no values on
a guess causes a crash.
12:26
There we go.
12:33
And let's label that,
let's give that a red label.
12:36
All right, here we go.
12:39
Nice job on the looping and
exception handling.
12:42
Doing that manual testing or QA,
which stands for quality assurance,
12:44
helped us to uncover another issue that
we should probably fix here shortly.
12:48
If you don't enter a letter, the
application encounters a critical error.
12:52
Are you beginning to see how handy
it would be to be able to run
12:57
a bunch of tests and make sure
everything is working at all times,
13:00
especially if you change a bit of code?
13:03
You would just run the test to
make sure everything still works.
13:05
As it stands, we have to do that ourselves
every single time the code changes.
13:08
All right, let's refactor in clean up
that code right after this exercise.
13:13
You need to sign up for Treehouse in order to download course files.
Sign upYou need to sign up for Treehouse in order to set up Workspace
Sign up