Declaring a Method

   

Methods are truly the heart and soul of Java programs. They serve the same purpose in Java that functions do in C, C++, and Pascal ”only the terminology is different. All execution that takes place in any application or applet takes place within a method. It is only by combining multiple dynamic methods that large-scale Java applications are written.

Like C++ functions, Java methods are the essence of each class and are responsible for managing all the tasks that can be performed. It is also true that methods exist only within classes in Java. There is no concept of a global function in Java that exists outside of a class.

Look at the parts of a method as shown in the following simple example:

 void simpleMethod(){   System.out.println("Inside simpleMethod"); } 

A method has two parts: a signature and a body. A method signature specifies a method's name , its return type, its modifiers and access specifiers, the list of parameters it requires, and the types of exceptions it can throw. Unlike C++, Java methods are defined in one place rather than having a separate header file that declares the method signatures for a class. You also do not have to be concerned about defining method prototypes like some languages require you to do if you want to call a method from code that is declared before the method. The Java compiler does not place any restriction on the order of method declarations.

Although the actual implementation of a method is contained within its body (see "Blocks and Statements" later in this chapter), a great deal of important information is defined in the method signature. Using the preceding example, the simplest form of a method signature looks like this:

 void simpleMethod() 

At the very least, a method signature specifies what the method will return, and the name by which the method will be known. As you will soon see, there are several more options that are available. In general, method signatures take the form

  access_specifier  modifiers return_value nameOfMethod (  parameters  )  throws ExceptionList  

where everything in italics is optional.

Access Specifiers

You use methods by calling them from other methods. Remember how the HelloWorld application in Chapter 2, "HelloWorld: Your First Java Program," demonstrated that the Java interpreter calls the main method of an application to start its execution. The main method then calls other methods to perform the work of the application. The simpleMethod example performs its undemanding task by calling the println method of the PrintStream class.

The first option available within a method signature addresses the fact that a method can place restrictions on who can call it. Given that methods are only called by other methods and methods only exist within classes, the "who" here refers to the classes that can call a method. The access specifier in a method signature determines how visible a method is to other classes. At a minimum, any method is accessible to the other methods in the same class. You can, however, exercise control over access beyond that by selecting an appropriate specifier . By choosing to accept the default access level, or to instead declare a method as public, protected, or private, you determine a great deal about how that method can be used.

public

The public modifier is the least- restrictive modifier for a method. When you declare a method as public, you can access it from any other class regardless of inheritance or any other class relationships. A public method's signature looks like this:

 public void toggleStatus() 

You should reserve this modifier for methods that make up the external interface you want to expose for a class. The set of public methods declared by a class determines how it is seen and used by the rest of the world.

protected

The protected modifier raises the subject of packages, which are discussed later in Chapter 7, "Classes." For now, think of a package as a group of related classes. Because classes in a package are intended to form a cohesive unit, you have the option to allow less-restrictive method access between classes in the same package. Any class in the same package, or any class that extends the current class, regardless of package, can access a protected method. For instance, the class java.awt.Component has a protected method named paramString. This method is used in subclasses such as java.awt.Button, but it is not accessible to any class that you might create that does not extend Component. You declare a protected method like the following:

 protected void toggleStatus() 

See Chapter 7, "Packages,"

When you attempt to use a protected method, or any other restricted method, that you do not have access to from the current scope, the compiler reports an error. This error might be difficult to diagnose at first because of the way inaccessible methods are hidden from the compiler. Instead of getting an error that tells you that you are attempting to violate an access restriction, you get an error telling you that there is no such method as the one you are attempting to call. Such an error resembles the following:

 method paramString() not found in class java.awt.Button 

This makes sense after you think about it. As far as the compiler is concerned, protected methods do not exist unless you have sufficient access to call them. They are literally hidden by the accompanying encapsulation.

Default

You might have noticed that the simpleMethod declaration did not include an access specifier. When the specifier is omitted, the default access restrictions are imposed. The default access type is sometimes referred to as friendly or package access. Methods declared in this manner are only accessible to other classes within the same package. Consider the following class declarations:

 package abc; public class NetworkSender {   void sendInfo(String mes) {     System.out.println(mes);   } } package abc; public class NetworkSenderTest {   String mes = "test";   void informOthers(String mes) {     NetworkSender messenger;     messenger = new NetworkSender();     messenger.sendInfo(mes); // this is legal   } } package xyz; import abc.NetworkSender; public class NetworkSenderTest2 extends NetworkSender{   String mes = "test";   void informOthers(String mes) {     NetworkSender messenger;     messenger = new NetworkSender();     messenger.sendInfo(mes); // this is NOT legal   } } 

In this example, NetworkSender in the abc package declares a sendInfo method with default access. The informOthers method of NetworkSenderTest is allowed to call sendInfo because this class is also in the abc package. However, the same call is illegal in NetworkSenderTest2 because this class is in a different package. This is true even though NetworkSenderTest2 is a subclass of NetworkSender. Such a call would be legal if sendInfo had instead been declared as protected.

private

The private modifier assigns the highest degree of protection available to a method. A private method is only accessible to methods in the same class. Even classes that extend the current class cannot access a private method. Just like the other specifiers, a private method's signature takes the form:

 private void toggleStatus() 

You might not immediately see the need for private methods. After all, why declare a method that no other class can use? The answer is that private methods allow you to factor the work a class does into smaller, more manageable methods while still hiding the implementation details. This access level is well suited for utility or intermediate methods that support the work of other methods in a class but do not offer any general usefulness outside the class.

Modifiers

Modifiers are similar to access specifiers in how they are used within method declarations. They do serve a different purpose, however. In fact, each of the method modifiers ( static, abstract, final, native, and synchronized ) serves a different purpose altogether. Some of these modifiers will make more sense to you after you get to Chapter 7, but you'll get enough details now to make this discussion of the method signature complete.

static

The static modifier was briefly introduced in the HelloWorld application in Chapter 2 when you looked at the signature of the main method for an application:

 public static void main(String args[]) 

When you declare a method as static, you are stating that the method belongs to a class and not to an instance of a class, which would represent an individual object. Static methods are often referred to as class methods, as opposed to instance methods, for this reason. Instance methods operate on instance variables to access and maintain the state of a particular object. A static method, on the other hand, has no knowledge of an individual object's state. It is intended to provide a service that is independent of a class instance.

The difference between instance and class methods can best be demonstrated with an example. Listings 4.1 and 4.2 show examples of both method types used within a pair of cooperating classes. The complete details of class declarations are covered in Chapter 7, so for now, the focus is only on the method implementations that are shown.

Listing 4.1 AccountManager.java ” Account Manager Example with a Static
 Method  public class AccountManager {  // account numbers are validated simply based on length   private static int MIN_ACCOUNT_NUM_LENGTH = 8;   private static int MAX_ACCOUNT_NUM_LENGTH = 9;   public static boolean validAccountNumber(String accountNum) {      // make sure length is ok      if ( (accountNum.length() < MIN_ACCOUNT_NUM_LENGTH)        (accountNum.length() > MAX_ACCOUNT_NUM_LENGTH) ) {         return false;     }     // make sure every character is a digit     for (int i=0; i<accountNum.length(); i++) {       if ( !Character.isDigit( accountNum.charAt(i) ) ) {         return false;       }     }     // passed all the tests     return true;   } } 
Listing 4.2 Account.java ” Account Example with Instance Methods
 public class Account {   private String accountNumber;   private float balance;   public String getAccountNumber() {     return accountNumber;   }   public void setAccountNumber(String num) {     // call static method of AccountManager class to validate     // the account number     if ( AccountManager.validAccountNumber( num ) ) {       accountNumber = num;     }   }   public void deposit(float amount) {     balance += amount;   }   public void withdraw(float amount) {     balance += amount;   } } 

In Listing 4.1, AccountManager is declared as a class without any instance variables or methods. This means that an instance of the class has no state to maintain. The validAccountNumber method is declared as static because it is not tied to any particular instance of AccountManager. You could have 100 instances of the class in your application and still get the same result from the method no matter which instance you use to call the method. Account, in Listing 4.2, on the other hand, has both instance variables and methods. Each instance of an Account has its own account number and balance that cannot be managed by static methods because the associated values are always attached to a particular instance.

The setAccountNumber method of Account calls the static validAccountNumber method of AccountManager to make sure the account number it is passed is acceptable to use. Notice that this method call is stated using the class name instead of a variable name before the dot and the method name. The validAccountNumber method in turn uses the static variables declared in AccountManager and the static isDigit method of the Character class to do its work. A static method can access any static variables or other static methods defined in its class, but attempting to reference an instance variable or method causes a compiler error. However, in the reverse situation, it is perfectly legal for an instance method to access static variables and methods in addition to instance ones.

An important concept to note here is that you never even need to create an instance of a class to access the static methods and variables that it defines. Here, the Account class does not create an AccountManager, but it makes use of the class's functionality nonetheless.

Note

The conventional way to access a static method is to specify the class name followed by a dot and the method name. If you have an instance of a class with a static method, you can also use that instance identifier to access the method. Although legal, this is not as clear to someone reading your code because it does not make it obvious that a static method is being used.


abstract

An abstract method is a method that is declared but not implemented in the current class. The responsibility for defining the body of the method is passed on to the subclasses of the current class. This type of method is useful when you know that a certain behavior will be implemented in a hierarchy of classes, but there is no default implementation of that behavior. An abstract method serves as a placeholder for the behavior in the parent class. The ability to define abstract methods is a key part of object-oriented design. The value will be more obvious to you when polymorphism and abstract classes are discussed in Chapter 7.

An abstract method declaration takes the form

  access_specifier modifiers  return_value nameOfMethod (  parameters  )  throws ExceptionList;  

where everything in italics is optional. Unlike the previous use of this declaration template, this actually defines a complete method. The semicolon at the end of an abstract method's signature takes the place of the method body.

There are a couple of restrictions associated with the abstract modifier. First, a class that contains one or more abstract methods must also be declared as abstract itself. Also, you cannot declare a static method, class constructor, or final method to be abstract.

final

By placing the keyword final in front of a method declaration, you prevent any subclasses of the current class from overriding the method. This is your way to dictate that this implementation of a method is the only implementation to be allowed by the compiler. It makes your intent regarding the use of the method decisively clear.

In C++, you must declare a method as virtual to allow it to be overridden. In contrast, all methods in Java can be overridden by default. The final modifier is your way to turn off this default behavior for a specific method.

Tip

Besides providing control over how your methods are used, the final and static modifiers and the private specifier offer an additional performance advantage. A method declared using any of these attributes cannot be overridden, so any calls to it can be bound at compile time. This saves the time that would otherwise be spent at runtime performing a dynamic lookup.


native

Native methods are methods that you want to use, but do not want to write in Java. They are most commonly written in C or C++ and can provide several benefits, such as faster execution time, reuse of existing code, or access to features unique to a platform. Similar to abstract methods, you declare a native method by placing the modifier native in front of the method declaration and substituting a semicolon for the method body. It is important to remember that this declaration informs the compiler of the return type, name, and arguments associated with a method. Even though you do not define a method body here, you must make the signature consistent with its implementation.

Use of native methods is not that common for most programmers. If you find it necessary to implement part of a class this way, refer to Sun's documentation of the Java Native Interface (JNI). JNI provides the functionality required to load a native method implementation at runtime and associate it with a Java class.

synchronized

One of the most powerful features of Java is the built-in support it provides for developing multi-threaded applications. Declaring a method as synchronized signals that incorrect results might occur if use of the method by multiple threads is not guarded . When a synchronized method is being executed, no other thread can call that method, or any other synchronized method, in the same class until the method returns.

See "Thread Synchronization"

Returning Information

When a method completes its execution, it can return a primitive value, an object reference, or nothing at all to the method that called it. The return type in a method signature defines what, if anything, is returned. Methods that do not return anything must be declared with a return type of void.

Although returning information is an important part of what a method can do, there is actually little to discuss on this subject. A method returns a value or reference with a return statement that includes an expression that evaluates to the appropriate type. A void method uses a return statement without an expression. In fact, the use of a return is optional in this situation unless there is a condition that requires the method to exit before its last statement is executed.

The compiler requires that any return statement in a method be consistent with the declared return type. If a method is not declared void, it must have at least one corresponding return statement.

For example, the following method is declared to return a value of type boolean. The return is accomplished by executing one of the two return statements:

 public boolean inOrder(int x, int y) {   if (x<y) {     return true;   }   else {     return false;   } } 

As a simplification, a return statement with an expression can be substituted:

 public boolean inOrder(int x, int y) {   return x<y; } 

Method Name

A method signature includes the method name. The rules regarding method names are simple and are the same as any other Java identifier. A method name must begin with a letter, underscore , or currency symbol. The remaining characters in the name can be any of these characters or a digit. To follow standard naming conventions, you should start method names with a lowercase letter and use mixed case to separate words.

Parameters

Simply put, the parameter list in a method signature is the list of information that will be passed to the method whenever it is called. A parameter list takes the following form and can consist of as many parameters as you want:

 (DataType variableName, DataType variableName,...) 

If you have no parameters, Java requires that you leave the parentheses empty. (This is unlike other languages that permit you to omit a parameter list, or C, which requires the keyword void. ) Therefore, a method that takes no parameters has a signature resembling the following:

 public void reset() 

The strong typing and compile-time checking of Java result in a number of restrictions on parameter lists. There is no direct support for variable length parameter lists or default parameter values. Each call to a method has to include each parameter declared for the method and provide them in the correct order. The type of each parameter does not have to match exactly as long as those that differ can be implicitly converted to the type required. The concepts of variable parameter lists and default values are best handled in Java by using sets of overloaded methods. You might also consider replacing individual arguments with an array, or one of the collection classes introduced later in Chapter 10, "Data Structures and Java Utilities." The following example demonstrates an overloaded method that does the equivalent of assigning a default parameter value:

 public void setFont(String fontName) {   // no point size specified using this method,   // so default to 12   setFont(fontName, 12); } public void setFont(String fontName, int pointSize) {   // use the two method parameters to set the font   ... } 

Here, setFont is overloaded so that it can be called either with or without a pointSize parameter. If this argument is omitted, the single-parameter version of the method is called. This method calls the other version to do the real work by supplying a default value of 12 for the missing argument. A key point here is that methods such as this should be chained together as shown instead of being implemented by copying and pasting the same code into each one.

For the most part, the name of a method parameter can be any valid Java identifier. One restriction is that a parameter name cannot be the same as a parameter to a catch clause, which is described later in this chapter when exception handling is discussed.

Caution

The Java compiler does not place any restriction on the number of parameters you include in a method declaration, but a long parameter list is often an indication of a poorly designed method. When you find yourself passing a lot of parameters to a method, you might be trying to do too much within a method that should really be broken out into smaller pieces. If this is not the issue, grouping some of the parameters into a class might be an alternative that will simplify your design.


Pass by Reference or Pass by Value?

An important concept to understand about any programming language is whether variables passed to a method as arguments are passed by value or by reference. If a language uses pass by reference, when you pass a variable into a method and the method changes the variable's value, the change is seen in the calling method as well. On the other hand, if a language uses pass by value, changes made to a parameter inside a method are never seen by the calling method. Here, pass by value literally means that only a value is passed into the method and not an associated variable that can be modified in such a way that changes are reflected outside the method. A parameter in this case is like a local variable of the method that gets its initial value from the calling method instead of being known in advance.

Some languages allow you to specify when you want to pass by value and when you want to pass by reference. For example, in Pascal the var keyword allows you to distinguish parameters that are passed by reference from those that are not. C++ provides similar flexibility allowing you to attach the & operator to a parameter's declaration so that it is interpreted as a reference. Java, on the other hand, always treats parameters in a consistent fashion.

The short answer is that Java always passes parameters by value. This does not give you the complete story however, and probably sounds confusing given that objects in Java are always handled by reference. First, consider the simple case. Primitive types ( byte, short, char, int, long, float, double, and boolean ) are passed by value and any modifications made to them in a method are seen only by that method and not by the caller. You cannot change the value of a primitive variable by passing it into another method.

Objects are also passed by value when used as method arguments, but the interpretation is different. It is the object reference that is passed by value, not the object itself. That might sound like a play on words, but the implications of this are significant. You cannot change an object reference within a method call, but you can change the state of the object that is being referenced. When you pass an object to a method, a reference to the object is passed and not a copy of the object and its attributes. You are free to modify the attributes of an object within the method and those changes will be seen by the calling method as well. What you cannot do is assign a different object reference to the parameter and have that change reflected in the calling method.

Listing 4.3 demonstrates the basic differences between passing primitives and passing object references. As shown here, if you pass an int to a method and that method changes the int, the calling class still sees the original value. However, when a class instance is passed and a variable within it is changed, the change is visible in the calling method as well.

Listing 4.3 PassingDemo.java ” The Difference Between Passing an Object and a
 Primitive Type  public class PassingDemo {   public void first() {     xObject o = new xObject();     // initialize an object and a primitive     o.x   = 5;     int x = 5;     // pass them to another method and then recheck their values     changeThem(x, o);     System.out.println();     System.out.println("Back in the original method");     System.out.println("The value of o.x is now "+o.x);     System.out.println("But, The value of x is still "+x);   }  public void changeThem (int x, xObject o) {     x   = 9;     o.x = 9;     System.out.println("In the changeThem method");     System.out.println("The value of o.x was changed to "+o.x);     System.out.println("The value of x was changed to "+x);   }  public static void main(String args[]) {     PassingDemo myDemo = new PassingDemo();     myDemo.first();   } } class xObject {   public int x; } 

The resulting output from this code is

 In the changeThem method The value of o.x was changed to 9 The value of x was changed to 9 Back in the original method The value of o.x is now 9 But, The value of x is still 5 

That example shows how you change the value of an object attribute. Now look at Listing 4.4 where the meaning of not being able to change a reference is demonstrated. Here, the code might at first appear to be swapping the two references passed by the calling method. However, references are passed by value, so the attempted swap has no real effect.

Listing 4.4 SwapAttempt.java ” A Method Cannot Change a Reference Passed
 as a Parameter  public class SwapAttempt {   public static void swap( String s1, String s2 ) {     // these statements are legal but they have no     // effect on the variables in the calling method     String temp = s1;     s1 = s2;     s2 = temp;   }   public static void main( String args[] ) {     String s1 = "First String";     String s2 = "Second String";     swap(s1, s2);     // the object references are unchanged     System.out.println("After the swap");     System.out.println("s1 is " + s1);     System.out.println("s2 is " + s2);   } } 

The output of this program isi

 After the swap s1 is First String s2 is Second String 
Holder Classes

There might be situations in which you want to change a reference within a method. In particular, you might want a method to create an object if an existing one is not supplied by an argument. A variable of a class, array, or interface type does not always refer to a valid object. It might instead point to nothing, which is indicated by the reserved word null. If you pass a null argument to a method, the method cannot create an object and assign it to the variable used in the method call. This is the same issue as in the string-swapping example; references are passed by value and cannot be changed.

A common solution to this limitation is to wrap the argument in what is known as a holder class. A holder class is declared with a single attribute of the type that the argument of your method would normally be. If you pass a non- null holder class instance to a method, the method can create an object of the wrapped type and assign it to the holder. This changes an attribute of the method parameter and not the reference to the parameter, so the new object can be accessed in the calling method. This approach can also be used to correct the string swapper , as shown in Listing 4.5.

Listing 4.5 StringSwap.java ” A Method Can Change the References Stored by a Holder Class
 Holder Class  public class StringSwap {   public static void swap( StringHolder s1, StringHolder s2 ) {     // swap two strings wrapped using a holder class     String temp = s1.myString;     s1.myString = s2.myString;     s2.myString = temp;   }   public static void main( String args[] ) {     StringHolder s1 = new StringHolder();     StringHolder s2 = new StringHolder();     s1.myString = "First String";     s2.myString = "Second String";     swap(s1, s2);     // the string object references are now swapped     System.out.println("After the swap");     System.out.println("s1 is " + s1.myString);     System.out.println("s2 is " + s2.myString);   }   static class StringHolder {     public String myString;   } } 

The output of this program is

 After the swap s1 is Second String s2 is First String 

You will become quite familiar with holder classes if you ever develop CORBA applications using Java. CORBA requires that methods be able to return references to new objects through variables in their parameter lists. Holder classes are the mechanism used to do this in Java.

Exceptions

The last part of a method signature is the list of exceptions that can be thrown during execution. Exceptions are discussed in detail later in this chapter, but a method must declare any checked exceptions that it might throw by listing them after the throws keyword. (Checked exceptions are also described later.) Identifying exceptions in a method declaration tells any calling method what potential problems it might need to handle.

   


Special Edition Using Java 2 Standard Edition
Special Edition Using Java 2, Standard Edition (Special Edition Using...)
ISBN: 0789724685
EAN: 2147483647
Year: 1999
Pages: 353

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net