/* OBJECTS, AT LAST! In the Java programming language, every function must be inside a class. It is thus impossible to talk about functions without mentioning classes. However, in the previous sections of this tutorial we have in fact focused on the writing of functions, and classes played only a minimal role. In addition to functions, classes can contain their own variables. A CLASS CONSISTS OF BOTH VARIABLES AND FUNCTIONS You've learned that variables inside functions, both formal parameters and those declared internally, are local to the function. These variables effectively cease to exist when the function exits. They've served their purpose. A variable declared inside a class, however, is shared by all the functions of that class. When a function's code modifies the variable, the change is persistant even after the function has exited. A variable inside one class is distinct from variables inside other classes, even if they have the same name. We'll again first give an abstract example followed by more concrete ones: class SampleClass { private int x; public void incx() { x = x + 1; } public void printx() { System.out.println("x is now " + x); } } // SampleClass Variables declared inside class are (usually) preceded by the word "private" as opposed to "public". I'll explain why later. The two functions incx and printx inside the class SampleClass both have access to the variable x (but not functions inside other classes). Furthermore, when incx increments the value of x, the effect persists even after incx has exited. All integer variables defined inside classes have initial value 0. So if one calls incx twice followed by printx, one would see "x is now 2". Furthermore, now it makes sense to have more than one instance of a class. Recall that classes are not objects - they are blueprints for creating objects. When a class contains only functions, it actually makes no sense to have more than one instance of the class. But now classes have variables, and each instance of a class gets a new set of these variables. In the following main functions, we create two instances of SampleClass, myobj and yourobj. Each of these objects gets their own copy of x. Thus calling incx on one object is not the same as calling incx on the other object: they'll be changing different variables: public class whatever { public static void main(String[] args) { SampleClass myobj; SampleClass yourobj; myobj = new SampleClass; yourobj = new SampleClass; myobj.incx(); myobj.incx(); myobj.printx(); // prints x is now 2. yourobj.incx(); yourobj.printx(); // prints 1 (not 3!) } } // whatever Notice that although "incx" is called three times, the last call to printx printed 1. That's because the x inside myobj was incremented twice but the x inside yourobject was only incremented once. Now for a more practical example, consider the problem of writing a program to manage bank accounts. Each bank account has a balance, along with a bunch of other information such as name, interest rate, etc... We want to perform a set of operations on the account, such as setting up the account with an initial balance, withdraw, deposit, balance inquiry, etc... For example, we would want to write a funciton public void withdraw(double amt) That subtracts amt from the balance, and similarly a function deposit that adds to the balance. Now, were do we put the double balance variable that represents the balance of the account? It cannot be a local variable inside the withdraw function, because then any changes this function makes to the balance will be lost when it exits!!! That would mean that you can withdraw money from your account without changing the balance - that might be good for you but not so good for the banking industry. Furthermore, we want the withdraw and deposit functions to modify the same variable. Thus the logical place to put the balance variable is inside a class to which both the withdraw and deposit functions belongs: class account { private double balance; // running balance of account private String owner; // name of owner of account public void deposit(double amt) // adds amt to balance { balance = balance + amt; } // deposit public void withdraw(double amt) { if (balance >= amt) { balance = balance - amt; } else { System.out.println("insufficient funds"); } } // withdraw public double inquiry() { return balance; } public void setupaccount(double initbalance, String name) { balance = initbalance; owner = name; } } // account Notice that both deposit and withdraw still takes a parameter. This should make sense. Withdraw always modifies the same balance, but each time the amount to withdraw is different, and can only be determined at the time that the withdraw method is called (and likewise for deposit). The setupaccount method is intended to be called when the account is first created. It takes as parameters the initial values of the internal variables (balance and owner) of the account, and sets them accordingly. The inquiry function just returns the balance. EVEN-FURTHER_THAN-BEFORE-MORE, we dont' just want to manage one account. We want our program to work with multiple accounts. Thus we want to create MULTIPLE instances of the account class, each representing a separate account OBJECT. That is, each account object will have its own balance variable separate from those of other objects: public class whatever { public static void main(String[] args) { account myaccount; account youraccount; myaccount = new account(); youraccount = new account(); youraccount.setupaccount(5000.0,"good student"); myaccount.setupaccount(4000.0, "evil professor"); youraccount.deposit(100.0); myaccount.withdraw(200.0); System.out.println("your balance is " + youraccount.inquiry()); System.out.println("my balance is " + myaccount.inquiry()); } } // whatever The program will print your balance is 5100 and my balance is 3800. Note how important it is to be able to have different instances of the account class, each with its own balance variable. Otherwise when I withdraw from my account it will subtract the amt from your balance. That would be good for me but not so good for you! CONSTRUCTOR FUNCTIONS Almost every class will have a function like "setupaccount" above. The purpose of this function is to intialize the internal variables of the object being created. It is so common that there is a special kind of function called a "constructor function" that is used for just this purpose. The constructor function is automatically invoked when the object is created using the "new" directive. It can take parameters as well, so it will know how to set up an object. It is distinguished from other functions in that THE NAME OF THE CONSTRUCTOR FUNCTION MUST BE INDENTICAL TO THE NAME OF THE CLASS. Furthermore, we don't write "void" as part of the function declaration even though it doesn't return anything. Here's how we write the "setupaccount" function above as a constructor function: // inside class account: public account(double initbalance, String name) { balance = initbalance; owner = name; } Again, note the special syntax: the name of the constructor function is the same as the name of the class, and there is no "void" even though the function doesn't return anything. Recall that previously we had created object using expressions such as youraccount = new account(); Now with a constructor function, we can say: youraccount = new account(5000.0,"really good student"); Since the constructor functions takes two parameters, we have to pass "new account" these parameters. The new directive automatically invokes the constructor function with the parameters you passed to it. And we don't need to call setupaccount explicitly anymore. Here's the new version of main: public static void main(String[] args) { account myaccount, youraccount; youraccount = new account(5000.0,"good student"); myaccount = new account(4000.0, "evil professor"); youraccount.deposit(100.0); myaccount.withdraw(200.0); System.out.println("your balance is " + youraccount.inquiry()); System.out.println("my balance is " + myaccount.inquiry()); } ----------- WHAT KIND OF VARIABLE SHOULD I USE? -------------- When writing a function, you must first be clear as to what kind of information it will require in order to accomplish its intended task. One place this information can come from are the function's parameters (only rarely will a function itself ask the user for IO). The fact that variables can be declared outside of a function adds another possibility. The information required by the function can either come in as parameters, or already be in the class. When you write a function, you must now ask 1. What kind of information does the function require 2. Where will this information come from. For the withdraw function, for example, in order to withdraw money from an account, the function needs to know the amt to withdraw, as well as the current balance (the specific account is already provided by "myaccount." or "youraccount."). The balance is a variable inside the class, but the amount to withdraw should be a parameter because it can't be determined until the function is actually called. You might also ask, why not have the withdraw function interact with the user using tools such as JOptionPane or Keyboard.readDouble in order to find out how much to withdraw. The point is, we should almost never mix computation with Input/Ouput. I/O is acutally a very small part of most programs. A program may perform a computation with different kinds of io - graphical, textual, or even through a network connection. Sometimes, a program may not want to do any IO (e.g., many internal operating system programs produce no io). By making our functions IO-INDEPENDENT, we can use them in all such settings. They become more general. For example, sometimes we deposit money at the bank counter, sometimes at an ATM, sometimes it's deposited automatically without any IO, such as automatic paycheck deposits. If our deposit function took one kind of IO, such as using Keyboard.readDouble, it will become useless in other settings. Furthermore, IO is generally much slower than internal computation, and mixing them will lead to a reduction in performance. Thus it is a general principle of programming to keep IO and computation separate. Unless specifically required, a function should be as IO-independent as possible. In my sample program the withdraw function is not output-independent because I used System.out. I did that to illustrate that the function should do something in case the amt is too much. I sometimes will use System.out.print statements inside functions just for the purpose of illustrating how they work. But in a real program withdraw should throw an "exception", which is something else entirely. You'll probably learn about them next semester. To conclude this tutorial, we write the final version of our "waiter" example. This time the class waiter will contain a variable to keep the running bill that the customer has rung up. Each time the customer orders something we will to add to the bill. And when the customer asks for the check, instead of just saying "here's your check", we can now give the exact amount that needs to be paid! Also pay attension to how the constructor function is used. The program is a simulation and we'll use System.out statements just to make the program interesting. */ class waiter { private double tab; // "instance variable" - different for each waiter public waiter(String name) // constructor function { System.out.println("My name is " + name + ", and I'll be your waiter!"); tab = 0.0; } public double priceof(String s) { double price; // value to be returned if (s.equals("chicken")) // remember: can't use == with strings { price = 5.95; } else { // burger or fries if (s.equals("burger")) { price = 4.95; } else { price = 1.95; } } // burger or fries return price; } // priceof public void order(String s) { if (s.equals("chicken")) { tab = tab + 5.95; } else { // burger or fries if (s.equals("burger")) { tab = tab + 4.95; } else { tab = tab + 1.95; } } } // priceof public void check() { System.out.println("Please pay $" + tab); } } // end of class waiter public class objects3 { public static void main(String[] args) { waiter ourwaiter; // declares a waiter variable ourwaiter = new waiter("manfred"); // makes a waiter object double x; // variable to hold price of burger double y; // variable to hold price of chicken x = ourwaiter.priceof("burger"); y = ourwaiter.priceof("chicken"); if (x