Parameters (Section 7.4)
Parameters and arguments
Parameter passing
Passing objects (and arrays)
Namespaces
Conditional operator (Section 7.3)
The switch statement (Section 8.1)
Cases
The necessity of break
The default case
When switch doesn't apply
Textbook: Section 7.4
We've pretty much finished up everything, but there are some issues that we haven't really covered completely. Today I want to pick up three different issues, which I feel we should cover to finish with Java completely, but which I have put off because there was never time to do it. The first of these has to do with parameters.
A parameter is a named value defined at the time a method is called. An argument is the particular value given into the method. This distinction is somewhat weird, but you'll find that people use it quite often.
For example, consider the following very simple program.
In this program, the parameter of square() is x, and the argument passed into square within main() is 10 (which x represents for the duration of the function.import csbsju.cs160.*; public class Example { public static double square(double x) { return x * x; } public static void main(String[] args) { IO.println(square(10)); } }
Java uses a technique for passing parameters called call by value. That is, an argument's value is copied into the parameter variable, so that changes to the parameter variable do not affect anything except for the method itself. Consider the following program.
In this program, the variable y within main() holds 10. We pass that in as the value for the parameter x in square(), and that method immediately changes x to be its square, 100. But that change of x does not affect the value of y within main()! This is because the value 10 is copied into the local parameter variable x, and subsequent changes to x only affects that single local variable.import csbsju.cs160.*; public class Example { public static double square(double x) { x = x * x; return x; } public static void main(String[] args) { int y = 10; IO.println(square(y)); IO.println(square(y)); } }
(Some languages pass parameters differently, and some give you a choice. Java isn't among them.)
Objects and arrays work similarly - but it's important to remember that the value of an object variable is actually a pointer to the actual object. So this memory address is what actually gets copied, not the entire object.
Consider the following program using the Account class we defined earlier.
Here, the mine variable's value is the argument to doubleAccount(), and so acct holds the same memory address as mine does. So, actually, acct and mine point to the same object. Thus, the deposit() method is actually also depositing into the object pointed to by mine.import csbsju.cs160.*; public class Example { public static void doubleAccount(Account acct) { acct.deposit(acct.getBalance()); } public static void main(String[] args) { Account mine = new Account(); mine.deposit(100.0); doubleAccount(mine); IO.println(mine.getBalance()); } }
Naturally, this doesn't extend to changes to the actual parameter value. Say we instead wrote doubleAccount() as follows.
This wouldn't work the same, because the final line here is changing acct to point to a different object. It doesn't actually affect the object pointed to by mine. The net effect of this method is simply to waste time - it creates a new account, but other methods have no way of accessing it as written.public static void doubleAccount(Account acct) { Account new_acct = new Account(); new_acct.deposit(acct.getBalance()); new_acct.deposit(acct.getBalance()); acct = new_acct; }
Arrays work the same - which isn't too surprising, since arrays in Java are objects too. For example, you might write the following method to zero out an array.
public static void zeroArray(int[] array) { for(int i = 0; i < array.length; i++) array[i] = 0; }
You've probably already figured this out, but it's important to know that each method's variables lives in its own space of names. You can't refer to a method's variables within another method.
In my earlier examples, I was careful to give different variables different names, even if they're in different methods. But that was just for clarity's sake. In the last example program, we could have used acct in place of mine, and it would work the same, since the acct within main() has no relationship to the acct within doubleAccount() - they're completely different variables.
Textbook: Section 7.3
I wanted to quickly tell you about this new operator, since the textbook has covered it by this point. It's called the conditional operator. (Sometimes it's called the ternary operator, because it is peculiar in that it actually works with three arguments. The other operators are all either binary (like *) or unary (like ++).)
The conditional operator gives you a way of putting an if-then-else into an expression.
Both the question mark and the colon are parts of the conditional operator. The part before the question mark is the condition. The computer will evaluate this to see whether the condition is true or false. If it is true, the computer evaluates the expression between the question mark and the colon, and the result is the value of the overall conditional expression. If the condition is false, then the computer evaluates the expression after the colon, and the result is the value of the overall conditional expression.IO.println(m > n ? m : n);
So in the above line, the conditional expression is m if m exceeds n and n otherwise. In other words, this line will display the larger of m and n.
In the order of operations, the conditional operator ranks just above the assignment operator. Usually this is what you want, but it's good practice to use parentheses here even when you don't need them, to make things more readable.
int max = m > n ? m : n; // UGLY: don't do this. int max = (m > n ? m : n); // much better
The conditional operator is never necessary when you program, but it's occassionally convenient. But you should be aware that it's extremely controversial. Many people say it should be banned. But many others use it. So it's worth knowing about.
Textbook: Section 8.1
We've seen else if popping up in programs with some frequency.
The switch statement is an alternative to this.if(letter == 'A') { mingrade = 90; } else if(letter == 'B') { mingrade = 80; } else if(letter == 'C') { mingrade = 70; } else if(letter == 'D') { mingrade = 60; } else if(letter == 'F') { mingrade = 0; }
A switch statement looks like the following.
You put an expression in the parentheses after switch (in this case, we want to switch based on the value of letter). Then, between the braces, we list what we want to do for different possible values of that expression. For each possible value, we write case, followed by the value, followed by a colon. Then we list what to do, followed by a break statement, telling the compiler that we've reached the end of our case. The break statement sends the computer to the next statement following the closing brace.switch(letter) { case 'A': mingrade = 90; break; case 'B': mingrade = 80; break; case 'C': mingrade = 70; break; case 'D': mingrade = 60; break; case 'F': mingrade = 0; break; }
It's important to remember break statement! If you leave it out, the computer will proceed into the next case. (This is called `falling through' - which is occasionally useful. Most often, however, it's an error.) Suppose, for example, we wrote the earlier switch statement as follows.
Then what would happen is that mingrade would become 0, regardless of what letter holds. If, for example, letter held 'A', the computer would set mingrade to be 90, then 80, then 70, then 60, and then finally 0.switch(letter) { case 'A': mingrade = 90; case 'B': mingrade = 80; case 'C': mingrade = 70; case 'D': mingrade = 60; case 'F': mingrade = 0; }
This aspect of switch statements is usually irritating. But there is one particularly important case where this is useful: When you want multiple cases to do the same thing. For example, the following code converts a character into the value it represents as a hexadecimal value.
switch(letter) { case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': value = letter - '0'; break; case 'A': case 'B': case 'C': case 'D': case 'E': case 'F': value = letter - 'A'; break; }
You can also use the default label as a sort of ``else'' condition.
Here, if letter is anything other than the accepted characters, an exception is thrown.switch(letter) { case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': value = letter - '0'; break; case 'A': case 'B': case 'C': case 'D': case 'E': case 'F': value = letter - 'A'; break; default: throw new Exception("not a hexadecimal digit"); }
Though switch is good to know about, its usefulness is severely hampered by the following two facts.