In the previous few videos, we produced
the main part of our app that responded
to the different button clicks. Now the
operations buttons need a method called
performOperation to do the arithmetic,
and we created a dummy method to allow
us to test the program, up to that point.
So it's now time to look at what the
performOperation
will do.
Now the program stores the two numbers
that are involved in the calculation in
variables - operand1 and operand2. now
when the program starts, operand 1 will
be null and operand2 will be zero. Now
the performOperation method is called
with two parameters. The first is a
String holding the value from the Edit
Text -
and we've already checked that that
isn't an empty string. Now the second
parameter is the operation, again as a
String, holding the symbol that
represents the operation. So the first
thing that this method will do is check
operand1. If it's null then there
isn't any calculation to perform, but we
do have to store the value in the
operand. And as well as that, we need to
update the result on the screen. The
input EditText widget is also
cleared, then we're done, and the program
goes back to waiting for a button to be
clicked.
Now if operand1 isn't null, then the
value passed in must be operand2. After
the initial call of this method, operand1
is only ever updated with the result
of a calculation - as long as you count
equals as a calculation, but you'll see
that in a minute. Now the next step is to
decide which operation is pending. There
will always be an operation pending,
because we've initialized pending
Operation to be equal, to be a String - in
other words, the equal sign.
Now if the pending operation is equals,
we set it to the operation that caused
this method to be called - the one passed
in as the second parameter. We then check
to see if that was also equals. If it was
then there's no arithmetic operation to
be performed - instead we just transfer
the value to operand1 and display that
as the result. So the result of an equals
operation, when there's no other
operation pending, is just to update the
result with the new value, and put the
new value into operand1, ready for the
next calculation. So there's no need to
clear the value in operand2, because
that will get a new value the next time
this method's called. Now if the pending
operation wasn't equals, the operation is
performed on operand1 and operand2.
Then the result is set to operand1,
which now holds the answer to the
calculation, and the method ends, clearing
the input field on its way out.
Now it may actually help to see both
flowcharts together, so here you can see
the design of the complete program. Al
right, so it's time now to write the code
for the performOperation function.
Alright, so here we are again in Android
Studio. So what I'm going to do is create
this function - this perform
Operation. So we're going to change this
and we're gonna start at the top. And our
first bit of code; if parenthesis, it's gonna be
operated1 - if that's equal to null. Then we
open a code block, and it's going to be
operand1 is equal to value.toDouble,
and otherwise, you put an else outside of
the code block and open another code
block. Otherwise, operand2 is equal to
value.toDouble. Then we're going to type,
after that, if parentheses pending
Operation is equal to, the equal sign. Then
pendingOperation equals operation - and
actually, what I should have done there is
open a code block, then have the code block
closed there. Next, we're going to type
when parentheses pendingOperation and
open a code block. Then we're going to
type double quote equal, then a dash
greater than sign, operand1 equals
operand2. Then we're going to - that's the
equal sign - then the test for the divide
button. So the / - greater than sign, if,
then parentheses, operand2, if that's
equal equals to 0.0. Then we're going
to type operand1 equals Double.NaN,
then
and here we're going to handle attempt
to divide by zero - to divide by zero. So
we're specifically testing for zero
there. Otherwise, else then operand1,
or open a code block again, equals
operand1 and two exclamation marks
here. Then divided by operand2,
closing code block, as you can see there.
Next we want to handle the, on the next line, the -
and I should really indent this so
it's a little bit more readable, because
it's not really readable the way I've
done it. So I'm gonna do it like that,
which is how we should really have had
it anyway. So we've got our if operand equals 0.0 -
well this is the code that's executed on
that line. Then we've got the else there,
this is all to deal with the, deal with
the division. Then we're going to
come back here, and we're going to handle
the next one which is multiplication -
the asterisk. So we do a dash greater than
sign operand1 equals operand1,
operand1, two exclamation marks, times
operand2.
Okay, next line we want to do handle
the minus, so double quotes minus -
greater than sign. It's going to be
operand1 again, is equal to operand1, two
exclamation marks, take operand2. And
the last one will be + - it's obviously on
the next line - in double quotes,
- greater than sign operand1 equals
operand1, two exclamation marks + operand
2. Alright, we close off that code block
so we delete the rest here - for the when,
that is. And the next one, which closes
off the if for the original test on line
83, and the else for checking whether
operand1 is 0. Then on the last line
down here, we've got,
we're going to delete the display
Operation.text equals, and instead, what
we're going to do is put down there;
result.setText, the operand1.to
String. And then we want to type new
Number.setText double quotes and
two empty, or two double quotes within
the parentheses, I should say. Alright,
so what is this function doing now?
Well, the first thing it does is checks
if operand1 is null, and assigns it to
the numeric value of the value String
that was passed as the first parameter.
Now if that was the case, the rest of the
code is skipped until the result is
displayed, way down here on line 104 -
basically, at the end of the function.
Now the input widget's cleared on the
next line, ready for a new number to be
entered. Now if operand1 wasn't null, the
value gets assigned to operand2, then
the rest of the code - the main part of
the function's code - is executed. Now
if the pending operations is equals - so
we're down to this line here now, line
93 - then the current operation should be
performed. And so therefore, pending
Operation is assigned the new operation.
So in other words, operand1 equals
operand2. Now if you're used to Java, you
may actually well be horrified by the
code, here on a line 88, because in Java, you
should never compare strings using
equals equals. It almost always fails, so
the comparison will be false.
Now that's such a common source of bugs,
that Kotlin has changed the way that
equals equals works, when dealing with
objects. So it now does the comparison
that you'd expect. If you want the Java
behavior in Kotlin, you need to use 3
equal signs, and as it's rare that you do
want that behavior, the extra typing isn't
a problem. Alright, so the code then
uses a when statement, to decide when to
add and subtract, etc. And I
think I pointed down here before, when
were talking about pendingOperation, but
of course, I was referring
to the code that was being executed up
here. But we're now at the stage where we
actually are now talking about the
pendingOperation. Alright, so the code
at that point, is using a when operation
to decide whether to add, subtract etc.
Now if the operation is still equals
after the previous test, operand1 gets
the new value, which has already been
converted to a number and is stored in
operand2. Otherwise, the appropriate
calculation is performed, and the results
stored in operand1. Now there's a check
for division by 0 - you can see that here
on line 94 - and the calculator will
display NAN for Not A Number,
rather than crash. NAN, by the way, is a
special value to cater for cases like
this, where a calculation results in
something that can't be represented as a
number, which you'd get when you try to
divide by 0. Alright, and then finally the
method displays the results in the result
EditText, and clears the input field
before returning. Now one other thing to
look at is, why have we got these two
exclamation marks after the operand1,
whenever we use it in a calculation?
We've actually used that, as you can see, a
total of four times in this method - in
this function - for each of the operations;
the divide, multiply, subtract and add.
Well the exclamation mark is sometimes
called bang, which would make exclamation
mark, exclamation mark, the bang bang
operator. And that's not a bad name for
it, because if you've used it incorrectly,
your code's dead. Now the Kotlin
documentation describes it as being for
Null Pointer Exception lovers, and if you
use it on a null object, you will get a
NullPointerException. So let's actually
have a look at what it does, and why I
considered it safe to use it here. So
we're going to look at the divide
operation, and specifically, this
code here on line 97. So if I remove the
bang bang operator, so delete the two
exclamation marks, you can see that we
actually get an error. And if I just
hover over, Smart cast to Double is
impossible, because operand1 is a
mutable property that could have been
changed by this time. Now the error
previous to that, before the Kotlin was
updated, is that None of the following
functions can be called with the
arguments supplied. So the point here is
that the error message is implying that
one of the operands is of the wrong type,
which can be confusing at first, because
after all, they're both doubles aren't
they?
Well actually, no they're not. operand2
is a double, but operand1 is a Double? -
the nullable version of
Double. And if we come up and just check
the declaration of these two properties,
way up to code there, we've got our Double
on line 16, Double? for
operand1 and just Double, which is set
to a value of 0.0 on line 17.
So Double question mark, which is the
nullable Double class, isn't the same as
the non-nullable Double. Kotlin treats
them as different types, which is how it
prevents us from accidentally assigning
null to an object. Now going back to our
divide operation again, here on line 97,
and I'm going to put back the bang bang
operator - the two exclamation marks - after
operand1, to remove that error. So
what the bang bang operator does, is
return a non-nullable value of operand1.
So that'll only work if operand1 isn't
null. If it is null, we'll get a
NullPointerException.
Okay, so that's what it does, and why we
need to use it. Now the reason, by the way,
that it's safe to use it, is because all
this code is inside a null check. So
right at the start of this function,
right at the very top, we've got a
test here on line 83; if operand1 is
equal to null. So right at the start of
this function, we test to see if operand1
is null, up here on line 83. And we'll
only actually be executing this code if
it isn't, so therefore, it's a safe place
to use bang bang.
Now Kotlin provides the bang bang
operater for when you need it, but
remember that it's your responsibility
to ensure you don't use it on an object
that could possibly be null. Alright, so
the method exactly follows the flowchart
now and should work, so let's actually
run it and see if it does work. We're
going to run the code and switch over to
the emulator. So let's now try some
operations to see whether they work. So
let's try the first one. We'll do
seventy, then we'll do a plus five, and
then we type the, or click on equals.
And you can see we've got 75 at the top,
so that's working. Now we can try
multiplying by ten. So if we type now, at
this point, the multiply, or click on
the multiply button 10, then equals, we
should get the answer - hopefully, 750. You
can see we've got 750 at the top there.
So let's divide now, by 375. So I'm going
to type 375, 3 7
5, then divide. Click on divide, then we get
the answer of 2, which is correct. Now
if I do a divided by 2, here again, so
if I actually click on divide, 2
equals, we get the answer of 1. Now if I
try to divide by zero, by entering the
divide, or clicking on the divide button, zero,
then press equals, or click on equals, we get the
NaN - Not a Number.
Now that would normally crash, but we're
getting the result NaN instead, and
the reason for that - we're handling this
here, is the code on line 94 and 95. So we're
basically sending operands to NaN,
rather than the app crashing, because, of
course, dividing by zero isn't allowed.
Alright, so I don't think we've done a
minus yet, so we'll try typing 65 -, 65.
Well actually, we'll type 65 equals to get rid of the
NaN, so I've done that now. Now we'll try
a subtraction, so we'll put minus 80, then
minus 50 equals minus 65, which is
correct. And then we'll type 150 plus, 150
plus, and we get the answer of 85, which
is correct. So I think things are working
fine. I'll try typing a few decimals now,
so if I try typing 10.25 plus, we get the
answer of 95.25.
And let's just try another one; plus
64,64.987
equals 160.237.
So that seems to be working well. So
we've got our basic calculator working
well, at this point in time. So it does
work fairly well and one nice touch, is
that there's no need for a Clear button
to start in your calculation. So when you
want to start a new calculation, press
equals to finish the current one, then
type the new number and press equals
again. The new number is then transferred
up into the result, ready to add
to it or multiply it, or whatever you
want to do next. However, that's not
necessarily very intuitive, so we're
going to look at that more in the next
video. So I'll see you in the next video.