Professional Documents
Culture Documents
Basic purpose of methods. Term: call = Execution proceeds to the code in a method. Term: return = Execution of a method finishes and then continues after point of call, possibly using a value form the method. Term: static method = Method requiring no extra object. Term: instance method = Method that operates on the object before the dot.
was called from, and execution continues in the calling method from that point. A method may return a value (eg, parseDouble) or not (showMessageDialog). The call-return terminology is almost universal.
Identifying methods
Parentheses follow name. You can identify a method name because it is always followed by left and right parentheses, which may enclose arguments (parameters). If you see a left parenthesis with a name preceding it, it will be a method call or definition, or a constructor (constructors are very similar to methods). In the following example each method name is highlighted. When calling methods outside of the current class, eg, in the Java library, static methods are preceded by the class name (followed by a dot), and instance methods are preceded by an object. When a static method is defined, the keyword "static" will preceded it. The example below has only static methods.
methods/KmToMiles.java Convert kilometers to miles. Use JOptionPane for input / Fred Swartz 22 Apr 2006
import javax.swing.*; public class KmToMiles { //============================================================ constants private static final double MILES_PER_KILOMETER = 0.621; //=================================================================
6 main 7 8 9 10 11 12 1
public static void main(String[] args) { //... Local variables String kmStr; // String km before conversion to double. double km; // Number of kilometers. double mi; // Number of miles.
//Note
//... Input kmStr = JOptionPane.showInputDialog(null, "Enter kilometers."); km = Double.parseDouble(kmStr); //... Computation mi = km * MILES_PER_KILOMETER; //... Output JOptionPane.showMessageDialog(null, km + " kilometers is " + mi + " miles."); }
13 }
Notes 1. This defines a method called "main". Everything between the "{" on the end of this line to the matching "}" second from the end is the "body" of the method.
main method, which someone (eg, the operating system) will call . .).
To do its work, main calls on other methods: showInputDialog, which is defined in the JOptionPane class,parseDouble, which is defined in the Double class, and showMessageDialog, which is also in the
JOptionPaneclass.
Whenever you call a static method in a different class, precede it with the name of the class containing its definition, followed by a dot. If you don't specify the class name, it assumes the method is defined in the current class.
// File : dialog/capitalize/Capitalize2.java // Purpose: Capitalize first letter of each name. Declare with first use. 1 // Author : Fred Swartz - placed in public domain. // Date : 30 Mar 2006 2 import javax.swing.*; 3 public class Capitalize2 { public static void main(String[] args) { //.. Input a word String inputWord = JOptionPane.showInputDialog(null, "Enter a 5 word"); 4 6 7 word. //.. Process - Separate word into parts, change case, put together. String firstLetter = inputWord.substring(0,1); // Get first letter String remainder = inputWord.substring(1); // Get remainder of
String capitalized = firstLetter.toUpperCase() + 8 remainder.toLowerCase(); 9 } 10 } //.. Output the result. JOptionPane.showMessageDialog(null, capitalized);
What's before the dot tells whether it's a class or instance method
What's before the dot? If it's a class name, then it's a static (class) method; if it's an object, it's an instance method. Nothing at front when calling methods in same class. When calling your own methods, you don't have to write anything before the method name. The compiler assumes the same class or object.
Left-to-right argument evaluation Term: actual argument = value which is passed in a method call. Term: void method = method that doesn't return a value. Term: value-returning method = method that does return a value.
1 2 3 4 5 6 7 8 9 10 11 12
methods/KmToMilesArgs.java Convert kilometers to miles. Use JOptionPane for input / Fred Swartz 22 Apr 2006
import javax.swing.*; public class KmToMilesArgs { //============================================================ constants private static final double MILES_PER_KILOMETER = 0.621; //================================================================= main public static void main(String[] args) { //... Local variables String kmStr; // String km before conversion to double. double km; // Number of kilometers. double mi; // Number of miles. //... Input kmStr = JOptionPane.showInputDialog(null, "Enter kilometers."); km = Double.parseDouble(kmStr); //... Computation mi = km * MILES_PER_KILOMETER; //... Output JOptionPane.showMessageDialog(null, km + " kilometers is " + mi + " miles."); }
13 }
Argument evaluation
Before a method is called, the arguments are evaluated left-to-right. In the example above most arguments are simple values, except the second argument in the call to showMessageDialog. Before the call can be made, this argument expression must be evaluated by performing the conversions to string and the concatenations.
showInputDialog returns a String parseDoublereturns a double value. These method calls can be used anywhere in an expression where a String or
double value is required. Here they simply provide the value for the right side of an assignment.
void. If a method has a "side effect", but doesn't produce a value, it is called a void method. TheshowMessageDialog method shows something to the user, but doesn't return a value, and is a void method.
When a method is defined, you need to specify the keyword where the main method definition starts.
Method categories
There have been proposals to establish a convention for identifying different categories of methods. This could be used in documentation for example. There are two categories that are well established.
Getter - methods return (ie, get) a value from an object. The value often exists as a simple field in the class, but may also be synthesized when the getter is called. Synonym = Accessor. Those coming from a C++ background will be more familiar with equivalent termaccessor. Naming conventions. The Java naming conventions for getter methods depends on whether the returned value is boolean or not.
o o o
Boolean getter names typically begin with the word "is" or "has", as in
All other type getters have names beginning with the word "get", as in
z.setText("Hello Earthling");
Other categories have been proposed, for example: factory methods, conversion methods, enumerators, listeners, testing, etc.
Show how to define a method. Explain parts of method header. Term: formal parameter = variable in method which gets the argument value.
1 2 3 4 5 6
// // // // //
File : methods/KmToMilesMethod.java Purpose: Convert kilometers to miles using a method. JOptionPane IO. Highlight call and method definition header. Author : Fred Swartz Date : 22 Apr 2006
import javax.swing.*; public class KmToMilesMethod { //============================================================ constants private static final double MILES_PER_KILOMETER = 0.621; //================================================================= main public static void main(String[] args) { //... Local variables String kmStr; // String km before conversion to double.
7 8 9 10 //Note 1 11 12 } 13
//... Input kmStr = JOptionPane.showInputDialog(null, "Enter kilometers."); km = Double.parseDouble(kmStr); //... Computation mi = convertKmToMi(km); //... Output JOptionPane.showMessageDialog(null, km + " kilometers is " + mi + " miles.");
//========================================================= convertKmToMi 14 private static double convertKmToMi(double kilometers) { 2 double miles = kilometers * MILES_PER_KILOMETER; 15 return miles; } 16 }
//Note
Notes 1. 2. Call our own method below to do the conversion. We could have qualified the name with our class name, KmToMilesMethod.convertKmToMi(km), but this is unnecessary when calling a static method in the same class. Altho this method is trivial, just a multiplication, it is good practice to separate the "model", or "logic", from the user interface. As programs become larger, this separation becomes essential.
Anatomy of the
Visibility -
convertKmToMi
method header
We'll take a look at each of the parts of the method header in order.
public, private, or package private static double convertKmToMi(double kilometers) { double miles = kilometers * MILES_PER_KILOMETER; return miles; }
For greatest reliability and flexibility in your programs, you should always give methods the lowest visibility to others that you can.
When you define a method, you should think about who can use it. Generally you want to choose the lowest level of visibility that makes your program usable, either private or the default (package). Here are the four options, from least visible to most visible.
private - If you don't want any other class to use it, declare it private. This is a good choice.
None (package) - If you don't specify anything, the default visibility allows only classes in the same package (directory) to see it. This is a common choice. It's common to use public visibility whenpackage visibility is more appropriate -- I do it myself. The lack of a keyword for package visibility makes it a little harder to read.
protected - Don't use this protected, except in certain cases to let a child class see it. Even
then, its use is controversial.
public - Let's anyone see it. Choose this if you've defined a method that will be used by others outside of your project. Note that main must be declared public so the run-time system can call it.
private static double convertKmToMi(double kilometers) { double miles = kilometers * MILES_PER_KILOMETER; return miles; }
A method should be declared
static if it doesn't user instance variables or methods. A static method must use
only only parameters, local variables, and static constants, and other static methods in the same class. If the static keyword is omitted, the method will be an instance method. This example uses static, but soon you will learn about instance methods too. Return type
private static double convertKmToMi(double kilometers) { double miles = kilometers * MILES_PER_KILOMETER; return miles; }
Method name
private static double convertKmToMi(double kilometers) { double miles = kilometers * MILES_PER_KILOMETER; return miles; }
Method names should begin with a lowercase letter. Method names are typically verbs, whereas variable names are usually nouns. Parameter(s)
private static double convertKmToMi(double kilometers) { double miles = kilometers * MILES_PER_KILOMETER; return miles; }
Parameters are enclosed in parentheses following the method name. They are also called formal parameters). There is only one parameter in this example - kilometers, but if there are more, they must be separated by commas. The type of each parameter is specified before the name (eg, double). Parameters are local variables that only exist inside the method. They are assigned initial values from the arguments when the method is called. Method body
private static double convertKmToMi(double kilometers) { double miles = kilometers * MILES_PER_KILOMETER; return miles; }
The body of a method is the statements which are executed when the method is called are enclosed in braces following the the method header. Additional local variables may be defined (eg, miles). Return statement
private static double convertKmToMi(double kilometers) { double miles = kilometers * MILES_PER_KILOMETER; return miles; }
A method returns to the caller after it has done what it wants. If the method returns a value (not a void method), it must contain a return statement that specifies a value to return. When execution reaches thereturn statement, control transfers back to the calling method, passing a return value to it. Returning an expression The above example returns the value in the local variable miles. The return statement can be followed by any expression of the appropriate type, not just a single value. For example, this method body could have been written as a single return statement.
because there is no object passed whose fields can be referenced without qualification. My feeling is that throwing a lot of OOP stuff at them at the same time that they're trying to understand method parameters and linkage is too much. And it isn't like they don't use static methods (Integer.parseInt(), JOptionPane.show..., Math.max(), System.exit(), ...). Since they need to understand what it means to qualify the method with a class name instead of an object, it seems reasonable to do a short intro to static methods.
Local variable lifetime is from method entry to method return. Local variable visibility is only within the method. Local variables have no initial value. Parameter variables are local variables initialized from the argument values.
Principles / Style
Local variables
Now that we've written two methods, them. Variables that are declared in a method are called local variables. They are called local because they can only be referenced and used locally in the method in which they are declared. In the method below miles is a local variable.
main and convertKmToMi, you should know a little more about the variables in
private static double convertKmToMi(double kilometers) { double miles = kilometers * MILES_PER_KILOMETER; return miles; }
// BAD BAD BAD BAD BAD BAD BAD BAD BAD BAD BAD BAD BAD BAD BAD private static double convertKmToMi(double kilometers) { double miles;
return miles; }
// Both kilometers and miles are implemented as local variables. private static double convertKmToMi(double kilometers) { double miles = kilometers * MILES_PER_KILOMETER; return miles; }
Unexpected meaning change.. Programmers assume parameter variables represent actual argument values. Assigning to parameters breaks that assumption. Doesn't change actual argument. Because formal parameter variables are really local variables, assigning new values to them doesn't have any effect on the actual parameters. However, in some programming languages assignment to a parameter can assign to the corresponding actual parameter (eg, C++ reference parameters). Therefore if you write an assignment to a formal parameter variable, it may mislead the careless programmer with a C++ background. Or the reader may pause and try to decide if you thought you were assigning to the actual argument. In either case it reduces the readability.
Example. The example below shows how a parameter could be reused. The overhead of declaring an extra variable is just about zero, so this really isn't more efficient, and even this small example is astoundingly misleading.
// BAD BAD BAD BAD BAD BAD BAD BAD BAD BAD BAD BAD BAD BAD BAD private static double convertKmToMi(double kilometers) { kilometers = MILES_PER_KILOMETER * kilometers; altho it works. return kilometers; } // BAD - Don't do this,
Style:
final
parameter. Few programmers do this because it adds extra clutter, which in a different way reduces the readability. The use of self-restraint in assigning to parameters is usually suffcient, but specifying final isn't a bad idea.
private static double convertKmToMi(final double kilometers) { double miles = kilometers * MILES_PER_KILOMETER;
return miles; }
Alternatives
Ruby is a relatively new programming language that has a lot of interesting features. If you think C++ or Java are the end points of function evolution, you might be interested in Ruby features, especially what they call "blocks", which are like method objects (sometimes called closures or continuations). It's not that the effect is impossible to achieve in Java, but that Ruby's simple syntax for it means that it makes the programs more, not less, readable to use them. Another feature is Ruby's multiple return values (tuples), which I've also used in an earlier (now extinct) language, Prograph. Once you use multiple return values, is makes shoehorning everything into a single return value very annoying. I've been occasionally reading Thomas's Programming Ruby, and find it very interesting, but I haven't really done anything with Ruby except for a few very simple test programs. What stopped me was that there is no good documentation on writing GUI programs. There are a couple of GUI libraries, but neither had sufficient documentation the last time I looked.
Examine the method call/return process in more detail. Term: call stack = Memory that is used to save return address and local variables. Term: stack frame = The storage on the call stack that is used by one method.
None.
The table below shows how the call stack changes as calls and returns in the KmToMilesMethods program are made. This shows the first 8 changes to the call stack after main is entered.
3.
4.
5.
Execute the method. After the stack frame for this method has been initialized, execution starts with the first statement and continues as normal. Execution may call on other methods, which will push and pop their own stack frames on the call stack. Return from the method. When a return statement is encountered, or the end of a void method is reached, the method returns. For non-void methods, the return value is passed back to the calling method. The stack frame storage for the called method is popped off the call stack. Popping something off the stack is really efficient - a pointer is simply moved to previous stack frame. This means that the current stack frame can be reused by other methods. Execution is continued in the called method immediately after where the call took place.
Programming ideas
All code is in methods. Style: The main method often consists largely of method calls. Style: Methods should generally be no larger than one page.
Here is another variation of the program, this time using three methods. Altho there is no real need for these methods in such a small program, large programs are in fact composed of many small methods. It is the essential way that all code is structured. Each of the user-defined method names, both in the call and the definition, is hilited.
One method is void, which means it doesn't return a value. Three methods call other methods. The main program consists mostly of calls to other methods.
Source code
// File : methods/KmToMilesMethods.java // Purpose: Converts kilometers to miles using two methods. // Author : Fred Swartz - placed in public domain // Date : 22 Apr 2006 import javax.swing.*; public class KmToMilesMethods { //================================== ======================= constants private static final double MILES_PER_KILOMETER = 0.621; //================================== ============================ main public static void main(String[]
args) { double kms = getDouble("Enter number of kilometers."); double miles = convertKmToMi(kms); displayString(kms + " kilometers is " + miles + " miles."); } //================================== =================== convertKmToMi // Conversion method kilometers to miles. private static double convertKmToMi(double kilometers) { double miles = kilometers * MILES_PER_KILOMETER; return miles; } //================================== ======================= getDouble // I/O convenience method to read a double value. private static double getDouble(String prompt) { String tempStr; tempStr = JOptionPane.showInputDialog(null, prompt); return Double.parseDouble(tempStr); } //================================== =================== displayString // I/O convenience method to display a string in dialog box. private static void displayString(String output) { JOptionPane.showMessageDialog(null, output); } }
Programming:
Hammurabi v0
Description
This program simulates the functioning of an early agricultural society. It is based on the ancient computer game Hammurabi, named after a Babylonian king (See en.wikipedia.org/wiki/Hammurabi) famous for his laws. He also ran a very authoritarian society in which the peasants were allocated fixed rations of food by the state, as in this game. Goal. The user, who has just become the ruler of a kingdom, wants to make a place in history by having having the largest population of peasants. The simulation lasts five years or until everyone has starved. Grain is the basic resource. Each year, ask the ruler how to use the grain in storage.
How many bushels to feed the people. How many bushels to use as seed for planting next year's crop.
The remaining grain, if any, is saved for the next year in case of a bad harvest. Initial Conditions The Kingdom starts with the following resources: an area of 1000 acres, a population of 100, and with 3000 bushels of grain from the previous harvest.
be 20. For example, if the ruler allocates 2400 bushels, this will support a population of 2400 / 20, which is 120. This would become the new population.
Agriculture
Seed for Planting. Not all grain can be used for feeding the people. Some must be used to plant next year's crop. It takes two bushels of grain to plant an acre. To plant everything therefore requires 2 * area bushels of grain. Harvest. There are variations in the weather each year. The yield varies from 2 to 6 bushels per planted acre. This number is randomly generated each year.
for how much grain to use to plant the next crop, and how much grain to feed the population. Extend it to add population. Keep all input/output in the main method. Check for legal values, eg, don't allow the ruler to plant more grain than there is.
Placed in public domain. // TODO : * Prompt for amount to feed peasants. // * Check for reasonable values (enough grain, ...). import java.util.*; public class Hammurabi0 { //===================================== ========================== constants private static final int MIN_GRAIN_TO_SURVIVE = 20; private static final int MAX_LAND_FARMABLE_PER_PERSON = 15; private static final int SEED_REQUIRED_PER_ACRE = 2; private static final Scanner in = new Scanner(System.in); //===================================== ========================== variables // Everything prefixed with "k_" is part of the status of a Kingdom. // All "k_" variables should be part of a Kingdom class, but.... private static int k_grain = 3000; // Bushels of grain in storage. private static int k_area = 1000; // Area of kingdom in acres. Note that // k_area isn't used yet, but will be if // you add a check for the total amount // of land that can be planted. private static int k_year = 0; // Years since founding of kingdom. private static int k_harvest = 0; // Last harvest in bushels. private static int k_peasants = 100; // Number of peasants. //===================================== ============================= main public static void main(String[] args) { //... Run the simulation for 5 years or until everyone starves. while (k_peasants > 0 && k_year <= 5) { displayStatus();
// Show status of kingdom every year. int food = promptForFood(); // How much grain should be use for food? int seeds = promptForSeeds(); // How much grain should be used for planting? simulateOneYear(food, seeds); // Simulate effect of feeding and planting. k_year++; // Another year has passed. } //... End of simulation. status. if (k_peasants == 0) { System.out.println("Everyone starved!!!!"); } else { System.out.println("Congratuations, you survived for 5 years!"); } displayStatus(); } //===================================== ===================== displayStatus public static void displayStatus() { System.out.println(" Kingdom status at year " + k_year + ": last harvest = " + k_harvest + "\nTotal grain = " + k_grain + "\nPeasants = " + k_peasants); } //===================================== ===================== promptForFood private static int promptForFood() { //TODO: Ask the ruler how much to feed the people. return 0; } //===================================== ==================== promptForSeeds Show
private static int promptForSeeds() { //.. Ask the ruler how much grain should be used for seed. System.out.println("Exalted Ruler, how many bushels should be planted?"); return in.nextInt(); //... TODO: Check if not enough grain for this request, Reprompt. } //===================================== ==================== simulateOneYear private static void simulateOneYear(int food, int seed) { //... Calculate new population.based on food. k_peasants = food / MIN_GRAIN_TO_SURVIVE; k_grain -= food; // Reduce grain by amount used for seed.
//... Calculate new harvest // 1. How many acres can be planted with seed. // 2. The yield per acre is random (2-6) // 3. Harvest is yield * area planted. int acresPlanted = seed / SEED_REQUIRED_PER_ACRE; k_grain -= seed; // Reduce grain by amount used for seed. // TODO: Check that there are enough people and there is // enough land to actually plant that number of acres. int yieldPerAcre = 2 + (int)(5 * Math.random()); // Number from 2 to 6 k_harvest * acresPlanted; = yieldPerAcre
//... Compute new amount of grain after the harvest. k_grain += k_harvest; // New amount of grain in storage. }
Misc info
Name. Please change the name from Hammurabi to something else. You can also change the basic elements of the game, as long as it remains a oneplayer game trying to rule a civilization. One player. Do not try to make this into a two-player game where there are multiple kingdoms. A twoplayer game would best be done using multiple kingdom objects, which you don't know how to use yet. If you try to build two kingdoms on this static structure it turn into an ugly mess. The Object-Oriented approach to this would be a simple way to make multiple kingdomes. Cooperating. If you want to pair up with someone else on this program, that's fine, but I'll expect a more extensive final program. You should try to keep from stepping on each other's code by writing separate methods.
Better interface. Don't hesitate to make the interface better, use JOptionPane, better output, ... Buying and selling land. Land can be bought from the neighboring kingdoms (or sold back to them) for 20 bushels per acre. The yearly plan that the ruler makes should therefore include how much land to buy/sell in addition to the food and seed allocations. Allowing land to be added is the only way to get the population to really grow. Farming limits. The amount of land that one peasant can farm should be limited to 15 acres. If the population drops, it may not be possible to farm all land. Revolt. If more than 45% of the population starves, there is a revolt which overthrows the ruler, and the game ends. Soldiers. My wife's first reaction was to ask about the soldiers. They could play an important role, but exactly what will be left up to you (eg, suppress revolts, capture land, etc). But perhaps they must be fed better than the peasants. Random events o Rats. Each year, rats eat a random amount
from 0-10% of your grain Plague. There is a 15% chance each year of a plague. If that happens, half the population dies.
Methods 6 - Overloading
Purpose of this lesson:
Programming ideas
The method name is important to the human programmer as the key to describing an action to be performed. It's often useful to do the "same" action, but with different kinds of parameters. The compiler can distinguish methods that have the same name only if they have a different numberand / or type of parameters.
Here is a small program which simply computes the average of three numbers. It uses three overloaded methods to read the numbers. For such a small program you would not use three different methods, of course, but this shows how overloaded methods are defined and used. It's very common for one overloaded method to call another. another variation of the program, this time using three methods. Altho there is no real need for these methods in such a small program, large programs are in fact composed of many small methods. It is the essential way that all code is structured. Each of the user-defined method names, both in the call and the definition, is hilited.
One method is void, which means it doesn't return a value. Three methods call other methods. The main program consists mostly of calls to other methods.
Good practices
Coherence. It's important that all the methods do the "same" thing, so that the program is human comprehensible. All methods sharing the same name should return the same value, have the same side effects, and all be either static or instance methods. The language doesn't require this, but doing otherwise is asking for trouble.
Call each other. Because all overridden methods should be doing the same thing, it is very common for there to be calls from one to another, supplying extra default parameter values as required. Default parameter values. Some programming languages allow you to specify default values for parameters, and if a the parameter is not supplied, the default value is used. Java doesn't have default parameters, but you can easily implement them using overloaded methods.
//===================================== ==================== getDouble // I/O convenience method to read a double value. // This version of the getDouble method simply calls on another // version passing it a generic input message. private static double getDouble() { return getDouble("Enter a number"); } //===================================== ==================== getDouble // I/O convenience method to read a double value given a prompt. // This version of getDouble displays the user supplied prompt. private static double getDouble(String prompt) { String tempStr; tempStr = JOptionPane.showInputDialog(null, prompt); return Double.parseDouble(tempStr); } //===================================== ==================== getDouble // I/O convenience method to read a double value in a range. // It builds a new prompt and calls another version to get // the value, looping until a value in the range is found. private static double getDouble(String prompt, double low, double high) { double result; String rangePrompt = prompt + " Value must be in range " + low + " to " + high; //... Read and loop back if the number is not in the right range. do { result = getDouble(rangePrompt); } while (result < low || result
> high); return result; } //===================================== ================ displayString // I/O convenience method to display a string in dialog box. private static void displayString(String output) { JOptionPane.showMessageDialog(null, output); } }
//======================================================= primeFactor // Return a factor of n, or 0 if n is prime. // Uses simple algorithm of dividing by all numbers up to n. // Could improve efficiency many ways. public static int primeFactor(int n) { for (int divisor = 2; divisor < n; divisor++) { if ((n % divisor) == 0) {
public static double max(double a, double b) { double result; if (a > b) { result = a; } else { result = b; } return result; }
This method is clear in either style, but the rigid application of the one-return rule can make some methods very convoluted. The overriding principle that most programmers use is that program flow should be as clear as possible. Version 3 - No need for else when the true part returns.
return b; }
?: operator is a favorite of programmers who want to impress others. It does result in shorter source
code (but not more efficient execution). Most people don't find this more readable.
return (a > b) ? a : b;
Library. Of course you should use the predefined methods (Math.max()) rather than writing your own!
return statement and the end of the method are not the only ways to return
from a method. Uncaught exceptions are the other way to exit a method.
Calling any method which throws exceptions many be a point at which the method is exited. If it throws unchecked exceptions, it may not be easy to determine that they are thrown. Any statement which could cause an exception is another potential exit point.
Additional Discussion
Multiple return statements (http://www.javapractices.com/Topic114.cjp) Argues that multiple returns, altho subject to abuse, can make code clearer, especially in handling exception cases at the beginning. Multiple Return Statements (http://onthethought.blogspot.com/2004/12/multiple-return-statements.html) Bruce Eckel argues that multiple return statements can make "cleaner" code. If multiple returns are being used in a bad way, enforcing a rule like this probably won't make the programmer write clear code in any case. Code improvement guidelines (http://jroller.com/comments/fra3k/Weblog/code_improvement_guidelines) Argues for single return statements on the basis of clarity, which is curiously the same basis that is used for multiple return statements. Nice argument for multiple return statements in a response to this blog entry (in equals()).
Overview
o o o o
Classes combine data and methods. A class defines a data type. Advantage: Classes correspond to concepts in the problem domain. Advantage: Classes reduce complexity by increasing coherence and reducing coupling.
Terminology
Instance variable = Variable declared in class outside a method. Typically private. Field = Synonym for instance variable, attribute. Common informal term. Attribute = Synonym for instance variable, field. Often used in the design phase. Property = Synonym for instance variable, field. Common term if field is known publicly.
Group values. Classes are used to group a number of related, named, data values associated with an entity. By entity we mean something that is typically a noun when you are talking about a problem you are solving. For example, in a university computing system, you might have a class to represent a student, an instructor, a classroom, a department, a course, a section of a course, .... Student example. The information you would store about students, would be their name, id, etc. We'll keep this example simple and just save name and id information. Declare each field. A class contains declarations of the fields (instance variables, attributes) that hold the data associated with a student. These declarations are like declaring a local variable in a method, except that you will also specify the visibility. We'll start by using the visibility modifier public, which lets anyone see them. For example, the following
Student1 class can be used to represent a student. As you go through these notes, we'll make improvements
to this class.
public class Student1 { public String firstName; public String lastName; public int } id;
A class is a type
Noun. Classes are usually given the name of a noun, and the fields (variables) in it are attributes associated with that noun. Predefined Java classes that you have already used are String, which contains the characters in the string and the length,
JOptionPane, which contains information necessary to produce a dialog box on the screen, etc. These
predefined Java classes are used for the programming infrastructure that you need. Many common infrastructure classes are available in the 3000+ predefined Java library classes. But, of course, these don't address your problems. Business objects. Most classes you define will concern your problem domain, where they represent business objects (Student, TimeOfDay, Order, ...). Declaring business object variables is like declaring any other variable. Use the class name as a type, just as you've used String, int, etc. For example, the following declares two Student1 variables.
These don't have values yet, but we'll do that in the next section.
Instead of working with simple temperature numbers, you might have TemperatureMeasurement objects, which have several attributes: the temperature, the time, and the location. A Student class would represent information about a student: name, id, address, ... Each object of this class would have a data about a specific student. A Classroom class would have information about a classroom's building, room number, capacity, whether it has a projector, etc.
Object-Oriented Design involves deciding which classes you need to represent the things (entities) in your problem. For simple programs, you often use only simple variables, but as programs get larger, you will define more and classes to represent the things you are working with.
Classes versus objects. How to create objects and assign values to the instance variables (fields). Good practice: One class per file is the standard.
Using
// File
: oop/dataclass/Student1.java
// Purpose: Information about a student. public class Student1 { public String firstName; // First name public String lastName; public int } id; // Last name // Student id
Analogy: Form stamp and filled out forms. There are rubber ink pad stamps that are used to stamp out a small form. These have been used in several places in my passport, where passport control stamped my passport, then filled out the fields with the date, how long I could stay, etc. The rubber stamp is like a class, defining the fields. The filled out form in each passport are the objects, with specific values for each field.
Use
new
A class defines what fields an object will have when it's created. When a new Student1 object is created, a block of memory is allocated, which is big enough to hold these three fields -- as well as some extra overhead that all objects have.
new followed by the name of the class (eg, Student1), followed by parentheses. Later
we'll see that we can specify arguments in the parentheses when creating a new objects. Default field values - null, zero, false Unlike local variables in a method, fields do have default values (like the default values in arrays). Object references are null, numbers are zero, and booleans are false. Access public fields with dot notation The fields (firstName,
lastName, id) name data which is stored in each object. Another term for field isinstance
variable. All public fields can be referenced using dot notation (later we'll see better ways to access and set fields). To reference a field, write the object name, then a dot, then the field name.
//... Create a new Student1 object for Tatiana. tatiana = new Student1(); // Create a new Student1 object with default values. tatiana.firstName = "Tatiana"; // Set values of the fields. tatiana.lastName = "Johnson"; tatiana.id = 9950842; JOptionPane.showMessageDialog(null, "One student is named: " + tatiana.lastName + ", " + tatiana.firstName);
Awkward? This simple example with one student is somewhat awkward, but we'll get to examples that show real advantages soon.
memory and assigning a default value to each field. A little later you will learn how to write aconstructor to control the initialization. It's not very interesting to create only one object of class, so the following example creates a couple of them.
//
File
: oop/dataclass/TestStudent1.java
import javax.swing.*;
public class TestStudent1 { public static void main(String[] args) { Student1 tatiana; Student1 pupil; //... Create new Student1 object with new. tatiana = new Student1(); tatiana.firstName = "Tatiana"; tatiana.lastName = "Johnson"; tatiana.id = 9950842; //... Create another Student1 object. pupil = new Student1(); pupil.firstName = JOptionPane.showInputDialog(null, "First name"); pupil.lastName = JOptionPane.showInputDialog(null, "Last name"); pupil.id = Integer.parseInt(JOptionPane.showInputDialog(null, "ID")); JOptionPane.showMessageDialog(null, "One student is named: " + tatiana.lastName + ", " + tatiana.firstName + "\n and another is named: " + pupil.lastName + ", " + pupil.firstName); } }
To introduce the concept of constructors. Purpose of constructors is both guaranteed initialization and convenience.
Constructor syntax - like a method but with class name and implicit type/value.
Constructor Syntax
Similar to method. A constructor is similar to a method -- it's called like a method, has parameters like a method, and it returns. But it must have the same name as the class for which it is a constructor. Also, the type and return value are implicit.
// File
: oop/dataclass/Student2.java
// Purpose: Information about a student. Defines constructor. public class Student2 { public String firstName; // First name public String lastName; public int id; // Last name // Student id
//======================================== constructor public Student2(String fn, String ln, int idnum) { firstName = fn; lastName id } }
Note that a constructor has no return type and the name is the same as the class name. Using the constructor Here is the first program rewritten to use the above class definition with a constructor.
= ln; = idnum;
// File : oop/dataclass/TestStudent2.java // Purpose: Tests Student2 constructor. import javax.swing.*; public class TestStudent2 { public static void main(String[] args) { Student2 tatiana; Student2 pupil; //... Create new Student2 object with new. tatiana = new Student2("Tatiana", "Johnson", 9950842); //... Create another Student2 object. String first = JOptionPane.showInputDialog(null, "First name"); String last = JOptionPane.showInputDialog(null, "Last name"); int studID= Integer.parseInt(JOptionPane.showInputDialog(null, "ID")); pupil = new Student2(first, last, studID); JOptionPane.showMessageDialog(null, "One student is named: " + tatiana.lastName + ", " + tatiana.firstName
Student2 someone; someone = new Student2(); constructor. // ILLEGAL. There is no default // OK. Must specify 3
The constructor can check that all fields are defined with legal values. We're not going to extend the Student2 class any further to test for legal values, but we will in the next example. For the moment we'll leave the Student class, and move to something different to show the same ideas.
oop/timeofday/TimeOfDay1.java A 24 hour time-of-day class. This simple value class has public fields. Fred Swartz - 2005-05-30 - Placed in public domain.
oop/timeofday/TimeTest1.java Test the TimeOfDay1 class. Shows use of dot notation to select public fields. Fred Swartz, 2005-05-04, Placed in public domain.
3 import javax.swing.*; 4 5 6 7 8 9 10 11 12 13 } 14 15 16 17 18 19 20 21 } public class TimeTest1 { public static void main(String[] args) { //... Create a new TimeOfDay object. TimeOfDay1 now = new TimeOfDay1(); //... Set the fields. now.hour = 10; // Set fields explicitly. now.minute = 32; //... Display the time by referencing the fields explicitly. JOptionPane.showMessageDialog(null, now.hour + ":" + now.minute);
public class TimeOfDay1b { public int hour; public int minute; //==================================================== constructor //Note 1 public TimeOfDay1b(int h, int m) { hour = h; // Set the fields from the parameters. minute = m; } }
9 10 11 12 13 14 15 16 17
Notes 1. Constructors have no return type, and have the same name as the class.
// File : oop/timeofday/TimeTest1b.java // Purpose: Test the TimeOfDay1b class. // Author : Fred Swartz, 2007-02-27, Placed in public domain. import javax.swing.*; public class TimeTest1b { public static void main(String[] args) { //... Create a TimeOfDay1b object for 10:30 in the morning. TimeOfDay1b now = new TimeOfDay1b(10,30); //... Display it. JOptionPane.showMessageDialog(null, now.hour + ":" + now.minute); } }
Introduce constructor overloading -- multiple constructors differing in number or types of parameters. Typically this is used to provide optional initialization parameters.
Use of "this(...)" to call one constructor from another. The "this(...)" call must be first line a constructor.
Good Practice:
Overloading constructors
It's common to overload constructors - define multiple constructors which differ in number and/or types of parameters. For example, exact hours are common, so an additional constructor could be defined which takes only the hour parameter. You can then set to minutes to a default value.
oop/timeofday/TimeOfDay1c.java A time-of-day class with constructor, which provides a more convenient and conventional way to build objects. The constructor is overloaded (more than one version) for more convenience. Fred Swartz, 2005-05-04, Placed in public domain.
5 public class TimeOfDay1c { public int hour; 6 public int minute; 7 //==================================================== constructor 8 public TimeOfDay1c(int h, int m) { 9 hour = h; minute = m; 10 } 11 //==================================================== constructor 12 public TimeOfDay1c(int h) { 13 hour = h; minute = 0; // Set minutes to 0. 14 } 15 } 16 17 18 19 20 21 22 23
oop/timeofday/TimeOfDay1d.java A time-of-day class with overloaded constructors. One constructor calls the other using "this". Fred Swartz, 2007-02-27, Placed in public domain.
public class TimeOfDay1d { public int hour; public int minute; //==================================================== constructor public TimeOfDay1d(int h, int m) { hour = h; minute = m; } //==================================================== constructor public TimeOfDay1d(int h) { this(h, 0); // Call other constructor. } }
"Copy" Constructors
Another reason to overload constructors is to provide a copy constructor, which builds an object from the values of another object of the same class. [Note: Another way to do this is to implement the clone() method.]
oop/timeofday/TimeOfDay1d.java A time-of-day class with overloaded constructors. Implements a copy constructor. Fred Swartz, 2007-02-27, Placed in public domain.
public class TimeOfDay1e { public int hour; public int minute; //==================================================== constructor public TimeOfDay1e(int h, int m) { hour = h; minute = m; } //==================================================== constructor public TimeOfDay1e(int h) { this(h, 0); // Call other constructor. } //==================================================== constructor public TimeOfDay1e(TimeOfDay1e other) { this(other.hour, other.minute); // Call other constructor. } }
Questions
1. What would happen if you called the following constructor?
Throwing an IllegalArgumentException.
User errors. If the user makes an error, the user interface code must tell them what they did wrong, how to correct it, etc. This is an issue for the user interface code, not for your basic value objects. Either the user interface should check for values before passing them to the model code, or the model code should have some way of informing the user interface that there was an error, typically by throwing an exception. Programmer errors. You should be very unforgiving of programmer errors discovered during testing There are several ways to handle errors (see Error processing). One of the best ways to handle bad values is to throw an exception (see Throwing exceptions) to stop execution if an illegal situation is encountered.
Crash please. The TimeOfDay class is called by a programmer, not the user. If illegal values are passed to the constructor, it's because the programmer isn't doing their job, so the program should be stopped immediately. The program below should crash because an illegal number of minutes is passed to the second call on the constructor. The result would be an error message something like the following.
Exception in thread "main" java.lang.IllegalArgumentException: TimeOfDay: Bad constructor value: 14:99 at TimeOfDay2a.<init>(TimeOfDay2a.java:16) at TimeTest2a.main(TimeTest2a.java:27)
TimeOfDay class
// // // // //
oop/timeofday/TimeOfDay2a.java A 24 hour time-of-day class to demo intro OOP concepts. Fred Swartz 2009-09-18 Validity checking.
public class TimeOfDay2a { //========================================= instance variables public int hour; public int minute; //================================================ constructor public TimeOfDay2a(int h, int m) { //... Check values for validity. if (h < 0 || h > 23 || m < 0 || m > 59) { throw new IllegalArgumentException( "TimeOfDay: Bad constructor value: " + h + ":" + m); } hour = h; minute = m; } }
Test program source The following program will produce a dialog box indicating that an exception was correctly thrown, then it will produce an error on the console (if run from the console) because the exception was not caught.
// File : oop/timeofday/TimeTest2a.java // Purpose: Test TimeOfDay2a exception throwing.. // Author : Fred Swartz - 2006-09-18 - Placed in public domain.
// Issues : When you run this test, it should produce an error. import javax.swing.*; public class TimeTest2a { public static void main(String[] args) { //... First constructor call is OK try { TimeOfDay2a now = new TimeOfDay2a(8 , 35); // OK } catch (IllegalArgumentException iax) { JOptionPane.showMessageDialog(null, "Failure: " + iax); } //... Constructor should fail. try { TimeOfDay2a never = new TimeOfDay2a(14, 88); // ILLEGAL VALUE. } catch (IllegalArgumentException iax) { JOptionPane.showMessageDialog(null, "Success: " + iax); } //... Exception gets passed up to runtime environment to console. TimeOfDay2a never = new TimeOfDay2a(14, 99); // ILLEGAL VALUE. } }
Make data private for reliability. Write getters and setters to work with private data. Instance methods can use the instance variables (fields).
private keyword.
Methods defined without the
Good Practices
Only define getters for values that people need to know about. Setters are often not defined so that immutable objects can be created.
To make reliable software, it's essential to protect the data in an object - insure that it is initialized, and prevent illegal or inconsistent values. The key to this is to declare the fields private. Private data reduces complexity by preventing unintended coupling -- references to fields inside your class from other classes.
// Legal values.
But who would do that? Of course no programmer would intentionally set the value to something illegal like this, but there could be several reasons to mistakenly set it. Here are two examples.
methods in the same class can use the field variables, but no one outside the class can see them. However, if they're private, something must be done to allow getting and setting the values. This is done with so calledgetter and setter methods.
They can make your code inflexibly dependent on a particular implementation. There is often no need for others to know about every internal field. Don't write both a getter and setter if both are not appropriate. Getter and setter methods do not even need to correspond to specific fields -- they provide a user interface to your class, but internally you are free to make changes. We'll make such a change to the TimeOfDay class shortly.
It seems awkward to write these, and some languages and IDEs have made this either automatic or easier to do. There is controversy about how easy it should be go create getters and setters, because they shouldn't exist for all instance variables.
Alternative to getters/setters
A principle of OOP is that you should ask an object to perform a service for you, not just give you data. If your class is a simple "value" class -- a class which just represents a simple value, it may be appropriate to have getters and setters. As a class becomes more complex, it's often more appropriate to supply methods which do the necessary computation rather than letting the user get the values and do the computation themselves.
Less common, but you will see it sometimes. Instance variable looks like other variables. Common, but unhelpful. A C++ convention for "member" variables. Common in Java too. Another common convention originating in C++. The "f" denotes "field" variable.
Controversial. Like brace conventions, field prefixes are controversial. I highly recommend naming instance variables differently than local variables, but when you collaborate with others on a project there will often be a naming convention which you must use, or your instructor may insist on one style. Therefore be flexible on this topic. Naming other kinds of variables. Some programmers use naming conventions for parameters (eg, start parameter names with "p") or class (static) variables (eg, start names with "c"). Because parameters are essentially local variables which occur in only a limited span of code, there isn't as much interest in distinguishing them. However, class variables are seriously different, and it's good practice to name them differently. Because they're relatively rare, there hasn't been as much controversy or consensus on naming conventions.
TimeOfDay3
Here's the TimeOfDay class, rewritten with private instance variables and getters and setters.
// File : oop/timeofday/TimeOfDay3.java // Purpose: A 24 hour time-of-day class to demo intro OOP concepts. 2 // Author : Fred Swartz - 2006-09-18 - Placed in the public domain. // Issues : Makes variables private, adds getters and setters. 3 // Can be improved by calling setters from constructor. 4 public class TimeOfDay3 { 5 //========================================= instance variables private int _hour; 6 private int _minute; 7 //================================================ constructor 8 public TimeOfDay3(int h, int m) { 9 //... Check values for validity. if (h < 0 || h > 23 || m < 0 || m > 59) { 10 throw new IllegalArgumentException( 1
11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 } }
"TimeOfDay: Bad constructor value: " + h + ":" + m); } _hour = h; _minute = m; //================================================== getHour public int getHour() { return _hour; } //================================================== setHour public void setHour(int h) { if (h < 0 || h > 23) { throw new IllegalArgumentException( "TimeOfDay setHour: Bad hour value: " + h); } _hour = h; } //================================================ getMinute public int getMinute() { return _minute; } //================================================ setMinute public void setMinute(int m) { if (m < 0 || m > 59) { throw new IllegalArgumentException( "TimeOfDay setMinute: Bad minute value: " + m); } _minute = m; }
48 49 50
See one of the problems below for a way to improve this class. Test program
// File : oop/timeofday/TimeTest3.java // Purpose: Test the TimeOfDay3 class.. // Author : Fred Swartz - 2006-09-18 - Placed in public domain. import javax.swing.*; public class TimeTest3 { public static void main(String[] args) { TimeOfDay3 then = new TimeOfDay3(8, 35); TimeOfDay3 now = new TimeOfDay3(14, 5); //... Print the hours and minutes of the times. JOptionPane.showMessageDialog(null, "From " + then.getHour() + ":" + then.getMinute() + " to " + now.getHour() + ":" + now.getMinute()); // THE FOLLOWING WOULD BE ILLEGAL // now._Hour = 99; // Can't reference private field. } }
Terminology note
Java uses the terms getter and setter, but you may also see the C++ terms accessor and mutator.
Programming exercises
1. Improvement. This class definition above is OK, but it could be improved by calling the setter methods from the constructor so that code isn't repeated. This code violates the DRY (Don't Repeat Yourself) principle. Rewrite the constructor with this improvement. Make the Student class (see 3. Student - Constructor) robust with private instance variables, getters and setters, and checks for legal values (eg, name strings must not be null or the empty string and the idmust be in a certain range).
2.
Good Practices
Declare fields private, except for very simple value classes. Exceptions should be rare. Most methods will not be private.
Reduce coupling
To achieve the best encapsulation (information hiding) you should always declare methods with the least visibility that works. In small programs there's really no problem, but in large programs this problem of excessivecoupling is very serious. Coupling occurs when one part depends on the specific implementation of another. The more coupling there is the more costly it becomes to make changes because too much code depends on specific implementations. This causes software rot - a program becomes progressively less usable because it can't be easily upgraded.
Visibility
public
private
, or package
private - This should always be your first choice for instance variables. It is rare for an instance variable to have any permission except private. All instance methods in the same class can use a private instance
variable, but no methods in other classes can even see it. Communication between classes should be by means of methods, not variables, so methods will normally not be private. default (package) - If you don't specify anything, the default visibility allows classes in the same package (directory) to see it. This is a good, and common, choice for classes and methods, but should not be used for instance variables, which should be private.
public - Let's anyone see it. Choose this if you've defined a method that will be used by others outside of your project. Note that main is the only method that must be declared public so the run-time system can see it
and call it.
protected - Only use this after careful thought. See optional discussion below.
protected
There is substantial controversy about whether additionally grants visibility to child classes.
One problem is that anyone can make a child class which can reference it. If someone has access to your class, they can make this variable or method effectively public by supplying a getter and setter for it. There is protection against casual access, but not against anyone who feels a real need to access private data/methods. Perhaps its main use is in making anyone who is thinking about using protected variables/methods think a little more about it. Protected access also encourages coupling between the parent and child class, as with all inheritance. This coupling can prevent improvements or changes to the parent class, and should be discouraged. Using protected access is only useful if you've designed your class so that others can extend it. Designing for inheritance is not trivial because you have to imagine all the ways it might be used. I believe Elliotte Rusty Harold (Java book author) offered the opinion that designing for inheritance takes about three times as much work as not allowing inheritance! For example, if the user wanted to use it in a Map, you did override hashcode(), didn't you? Of course, if it's for your own use, you'll be able to fix things easily.
toString() method.
The
toString
instance method
toString, which returns a string representation of an object. This is convenient for
displaying the value of an object for either debugging or for console IO. String concatenation of objects. If you try to convert an arbitrary object to a String, as when you concatenate anything with a String, Java calls the object's toString method. If you didn't define this method, then your class inherited toString from the Object class. Object's version of toString doesn't produce anything very useful -- it gives the name of the class and the hexadecimal location in memory, for example, "TimeOfDay@82ba41".
TimeOfDay2 with
// // // // File : Purpose: Author : Issues :
toString
method
toString method has been defined.
This class is the same as in the previous example, except that the
oop/timeofday/TimeOfDay2.java A 24 hour time-of-day class to demo intro OOP concepts. Fred Swartz - 2006-09-18 - Placed in public domain. Add a toString() method.
public class TimeOfDay2 { //========================================= instance variables private int _hour; private int _minute; //================================================ constructor public TimeOfDay2(int h, int m) { setHour(h); setMinute(m); } //================================================== getHour public int getHour() { return _hour; } //================================================== setHour public void setHour(int h) { if (h < 0 || h > 23) { throw new IllegalArgumentException( "TimeOfDay setHour: Bad hour value: " + h); } _hour = h; } //================================================ getMinute public int getMinute() { return _minute; } //================================================ setMinute
public void setMinute(int m) { if (m < 0 || m > 23) { throw new IllegalArgumentException( "TimeOfDay setMinute: Bad minute value: " + m); } _minute = m; } //================================================= toString @Override public String toString() { return _hour + ":" + _minute; } }
Test program using
toString
// // // // //
File : oop/timeofday/TimeTest2.java Purpose: Test the TimeOfDay2 class. toString called automatically for concatenation. Author : Fred Swartz Date : 2005-04-30
import javax.swing.*; public class TimeTest2 { public static void main(String[] args) { TimeOfDay2 then = new TimeOfDay2(8, 35); TimeOfDay2 now = new TimeOfDay2(14, 5); JOptionPane.showMessageDialog(null, "From " + then + " to " + now); } }
Exercises
1.
Two digit times We normally expect to see time values expressed with two digits, but the version oftoString above formats numbers less than 10 as single digits, eg 12:7 although we would expect to see12:07. Change toString to do this. Although a good way to do this would be to use DecimalFormat or the newer String.format to accomplish this, try it using only if statements and
string operations for practice. toString12(). Write a method,
2.
appending AM or PM as appropriate. For example, for the time 22:37 would be returned as "10:37 AM". There are three cases: o Before 12 is AM. o Between 12 and 13 is PM, but written with 12. o 13 and after are PM with 12 subtracted from the hour.