Вот и подошёл к концу наш конкурс! Поздравляем победителей!!! Подробнее.

Проект Programmers.kz и школа hotPen3D2D предлагает Вам курсы по веб-дизайну, веб-программированию и компьютерной графике. Подробности здесь.

 
Информация к новости
 (голосов: 1)

Methods, Parameters, and Variables

Категория: Программирование » Java » Уроки Java

Java Unit 8

Methods, Parameters, and Variables

8.1  Passing Parameters of Simple Types

8.2  Passing Object References as Parameters

8.3  Accessor Methods, Mutator Methods, and Side Effects

8.4  Static Methods and Variables

8.5  A Complete Classification of Variables in Java

8.6  More Syntax

8.1  Passing Parameters of Simple Types

It will be helpful to learn a little more terminology about passing parameters and to discuss their use more specifically.  Here is one of the primary models for writing code in Java:

object.method(parameters);

The object is referred to as the implicit parameter, and any variable declared inside the parentheses is an explicit parameter.  More than one parameter may be declared inside the parentheses:

public void mymethod(double x, double y)

{

     …

}

The variables x and y, declared in the method definition, can be referred to as formal parameters.  This terminology means that they are placeholders for values which they will receive at run time.  These variables are active when the method code is being run.  Their names are valid between the braces enclosing the method code, like the names of variables that are declared within those braces.  When the method call is finished, whether the method returns anything or not, the formal parameter variables do not retain any values.

There is only one copy of the code for each method of a class.  Each object gets its own copies of the instance variables but the method code is shared by all objects of that class that are in existence.  As time passes, different objects may call the same method.  However, no parameter value from a previous call will be retained when a subsequent call to the method is made.

In a program, the call of the method above would take this form:

double q, r;

object.mymethod(q, r);

The variables q and r are referred to as actual parameters.  In the program they are given values which are then passed to the method during the call.  These actual parameters should be of the same type as the formal parameters declared in the method.  From the point of view of the variables, the first thing that happens in the execution of the method call is the following:

The value in the actual parameter q is copied into the formal parameter x.

The value in the actual parameter r is copied into the formal parameter y.

Parameter passing is like an assignment that crosses the boundary between the program and the method.  There is no connection between q and x, and r and y except for the copying of values from one to another.  No changes to x and y in the method have any effect on q and r.  At the end of the call, when the method returns, the values in x and y are lost.  The values of q and r in the calling program remain unchanged.  If it is desirable to obtain a result from a method, it is necessary to write the method to return that result.

Because the copying of actual parameter values to the formal parameters is like an assignment, the actual parameters do not always have to be of exactly the same type as the formal parameters.  It is sufficient that the actual parameters be of a type that can be successfully assigned to the formal parameters.  For example, it is possible to assign an integer value to a double variable without casting.  That means that an integer parameter value could be passed to a double formal parameter without problems.  If the actual parameter value cannot be assigned to the formal parameter without casting, the compiler will give an error message.

Here is something that sometimes confuses beginning programmers.  Suppose that the formal parameters were declared as follows:

public void mymethod(double x, double y)

{

     …

}

Then consider the following declarations and call in a program: 

double x, y;

object.mymethod(x, y);

            Is there any relationship between the x and y in the program and the x and y in the method?  The only relationship is this:  When the method is executed, the value of the x in the program is copied to the x in the method, and the value of the y in the program is copied to the y in the method.  There are two different variables x with the same name and two different variables y with the same name.  They are different from each other because they are declared within different sets of braces, and for this reason the system can tell them apart.  Changes to x or y in the method code still have no effect on x or y in the calling code.

            There is a related issue that sometimes confuses beginning programmers.  Consider the following class code.  It will compile, but it is wrong:

public class CupX

{

  private int seedCount;

  public CupX()

  {

       seedCount = 0;

  }

  public int getSeedCount()

  {

       return seedCount;

  }

  /*  NO!  NO!  NO!  */

  public void setSeedCount(int seedCount)

  {

       seedCount = seedCount;

  }

}

            The problem lies in the assignment statement in the setSeedCount() method:  seedCount = seedCount.  Consider what would happen when the following program, which tests the class, is run:

public class TestCupX

{

     public static void main(String[] args)

     {

          MyTerminalIO myterminal = new MyTerminalIO();

          CupX mycup = new CupX();

          mycup.setSeedCount(5);

          int result = mycup.getSeedCount();

          myterminal.println(result);

     }

}

            This program will give 0 as output.  When the cup object is contructed, seedCount is set to 0.  The call to setSeedCount() has no effect.  It is possible to explain why this is the case.  The instance variable seedCount is declared at the top of the set of braces containing the class definition.  It is in existence within that set of braces.  The formal parameter seedCount is defined in the parentheses at the beginning of the definition of the setSeedCount() method.  It is in existence within the set of braces containing that definition.  The system will not complain that there are two different variables named seedCount because they are distinct.  Within the method, the variable named seedCount is the parameter.  Anywhere else in the class definition, the variable named seedCount is the instance variable.

            The line of code in the method, seedCount = seedCount, only involves the parameter.  It does not affect the instance variable of the same name.  When the example program is run, in the method code the current value of the parameter seedCount, 5, is assigned to the parameter again.  The instance variable, which contains the value 0, is unchanged by the execution of the method.  The system can keep track of the variables with the same names and distinguish where each is valid.  However, the class code given above is both useless and misleading to a programmer because it suggests that it does something that it doesn’t.

            To sum up the foregoing, actual parameters and formal parameters can have the same names, while formal parameters and instance variables cannot have the same names.  You might also wonder whether actual parameters and instance variables can have the same names.  The answer is yes, because the system can distinguish between them.  Actual parameters are declared and exist within the braces of the calling program.  Instance variables are declared and exist within the braces of the class definition.  You may wish to use different names so that you don’t get confused.  However, for example, the following would be perfectly good code in a calling program:

int seedCount = 5;

mycup.setSeedCount(seedCount);

            There is one more topic related to the names and types of formal parameters.  We have seen that it is possible to have more than one constructor for the same class, and the names of the constructors are the same.  The system distinguishes between them based on the parameter list.  A class can also have more than one method with the same name.  The types and sequence of the formal parameters allow the system to distinguish between the different constructors and methods.  The names of the formal parameters don’t allow the system to distinguish between different constructors with the same name or different methods with the same name.

            For example you could not have two different methods in a single class with parameter lists like these:

public void mymethod(double x, int y)

public void mymethod(double v, int w)

            The system would detect an error.  The names of the formal parameters are different, but each method of the same name has a parameter list containing one double and one int, in that order.

            On the other hand, it would be possible to have two methods such as these:

public void mymethod(int x, double y)

public void mymethod(double x, int y)

            Alternatively, it would also be possible to have two methods such as these:

     public void mymethod(int x, double y)

     public void mymethod(double y, int x)   

In both cases the system notes that the first method has a parameter list consisting of an int followed by a double, while the second method has a double followed by an int.  This is all that matters.  The names of the formal parameters, x and y, do not.  Clearly there are limitations to allowing different methods to have the same name and distinguishing them by their parameter lists.  The important thing to grasp is that having distinct names is important for instance variables and formal parameters.  However, it is not possible to distinguish between two methods of the same name simply on the basis of distinct formal parameter names.

8.2  Passing Object References as Parameters

Here is a call in a program to set the seedCount of an object:

mycup.setSeedCount(4);

Here is the setSeedCount() method:

  public void setSeedCount(int newCount)

  {

       seedCount = newCount;

  }

The object that a method is called on is known as an implicit parameter.  It is available inside the method.  In this example, the method code is able to change the value of the instance variable mycup.

We have also seen object references passed as explicit parameters:

Point2D.Double mypoint = Point2D.Double(10, 20, 30, 40);

myterminal.println(mypoint);

The explicit parameter is mypoint and it becomes available inside the println() method.  In this case the method code doesn’t change the value of the instance variable, but it has access to it, and causes information about it to be printed out.

It is now time to learn how to write class methods that accept object references as explicit parameters.  The Cup4 class currently has a set method that accepts an integer as a parameter and changes the seedCount to that value.  Suppose you would like to be able to assign the seedCount of one cup object to the seedCount of another.  It would be possible to do this in a program by using get and set methods.  It is also possible to do this by writing a new method for the cup class.  Because a new method is being added, let the cup class be renamed Cup5.  Look at the method shown here:

public void assignSeedCountToAnotherCup(Cup5 anotherCup)

{

  int temp = this.getSeedCount();

  anotherCup.setSeedCount(temp);

}

In this code the keyword “this” is used for the implicit parameter.  How the code works is quite straightforward.  First you get the seedCount of the implicit parameter and save it in a temporary variable.  Then you set the seedCount of the explicit parameter to this value.

Now consider the use of such a method in a program:

Cup5 mycup = new Cup5(0);

Cup5 yourcup = new Cup5(4);

mycup.assignSeedCountToAnotherCup(yourcup);

This fragment of code would cause the seedCount of the explicit parameter, yourcup, to be set to 0.  Notice how different this is to the use of simple parameter types as explained in the previous section.  When the explicit parameter is a simple type it is not changed as a result of the call.  When the explicit parameter is an object reference, when it is passed its value is still just copied into the formal parameter.  The result is that there are two references to the same object, one reference in the calling program and one in the method.  Any changes made through the formal parameter affect that one object.  After the method call ends and execution returns to the calling program, the reference there is still to that same object, which has just been changed.  Here is an illustration of the situation at the beginning of the method call, after the actual parameter has been copied to the formal parameter.  There are two references are to the same object, containing the value 4.

      Here is the situation after executing the single line of code in the method:

      At the end of the method call the formal parameter reference is not retained.  The actual parameter still refers to the same object as before, but the object’s contents have been changed.

      In the example above, it is the explicit parameter that is changed by the method call.  It is also possible to write a method where it is the object that the method is called on, which is changed by the call.  The point is that the implicit parameter is always a reference, and that reference may be accessed and affected by code inside a method.

public void setSeedCountFromAnotherCup(Cup5 anotherCup)

{

  int temp = anotherCup.getSeedCount());

  this.setSeedCount(temp);

}

8.3  Accessor Methods, Mutator Methods, and Side Effects

Calls to methods may have implicit parameters.  Explicit parameters may not be needed because the method has the implicit parameter to work with.  Ignoring the possibility of a return value for the moment, the model for such calls is:

object.method(parameter(s) optional);

These methods that can be called on objects generally fall into one of two categories:  accessor, or get methods, and mutator, or set methods.  Accessor methods, in their simplest form, are typed and return the value of an instance variable of an object.  Mutator methods, in their simplest form, are void, take a parameter, and change the value of an instance variable of an object.

Both accessor and mutator methods can be somewhat more complicated.  An accessor may return a value derived from a calculation based on the values of instance variables.  A mutator may take more than one parameter, and the change to the value of an instance variable may be based on those parameters.

            Here is an example of how methods start to get more complicated.

public void exchangeSeedCounts(Cup5 anotherCup)

{

  int tempthis = this.getSeedCount();

  int tempthat = anotherCup.getSeedCount();

  this.setSeedCount(tempthat);

  anotherCup.setSeedCount(tempthis);

}

            With respect to the implicit parameter, this is a mutator method.  It takes a parameter and the value of an instance variable of the implicit parameter is changed.  However, the explicit parameter is an object reference and is also changed by the call.  The change to the implicit parameter is not the only effect.  The secondary or additional effect is known as a side effect.  If a method has a side effect, it is desirable that the method name somehow indicate that, as the name of this method does.  If it is possible to avoid side effects altogether in the methods of a class, that is a good design decision.

            With respect to the implicit parameter, the increaseSeedCount() method, shown below, is a mutator.  If the addedNumber parameter is not positive, then its value is not added to the seedCount.  The uninformed user might expect the method to add the value unconditionally.  In a sense, the conditional execution of the addition is a side effect.  By returning a boolean value, the method signals which action was taken.  This signal could also be regarded as a side effect, in this case a desirable outcome incidental to the main effect of the method.

  public boolean increaseSeedCount(int addedNumber)

  {

       if(addedNumber > 0)

       {

            seedCount = seedCount + addedNumber;

            return true;

       }

       else

            return false;

  }

            The following example illustrates how unpleasant side effects can be:

  public int getSeedCount()

  {

       int temp = seedCount;

       seedCount = 0;

       return temp;      

  }

This method takes the meaning of “get” literally.  It retrieves the seedCount, leaving nothing behind.  However, it is unlikely that such an outcome would be expected or desired.  At the very least the method should be named in such a way that its action is clear.  Since the same result can be accomplished by a call to a normal getSeedCount() method followed by a call to setSeedCount() with a parameter of 0, a method such as this one is probably not needed.

To sum up this section, here is the code for the Cup5 class with all of the changes mentioned up to this point:

public class Cup5

{

  private int seedCount;

  public Cup5()

  {

    seedCount = 0;

  }

  public Cup5(int initialCount)

  {

    seedCount = initialCount;

  }

  public int getSeedCount()

  {

    return seedCount;

  }

  public void setSeedCount(int newCount)

  {

    seedCount = newCount;

  }

  public boolean increaseSeedCount(int addedNumber)

  {

    if(addedNumber > 0)

    {

      seedCount = seedCount + addedNumber;

      return true;

    }

    else

      return false;

  }

  public void assignSeedCountToAnotherCup(Cup5 anotherCup)

  {

    int temp = this.getSeedCount();

    anotherCup.setSeedCount(temp);

  }

  public void setSeedCountFromAnotherCup(Cup5 anotherCup)

  {

    setSeedCount(anotherCup.getSeedCount());

  }

  public void exchangeSeedCounts(Cup5 anotherCup)

  {

    int tempthis = this.getSeedCount();

    int tempthat = anotherCup.getSeedCount();

    this.setSeedCount(tempthat);

    anotherCup.setSeedCount(tempthis);

  }

}

8.4  Static Methods and Variables

Static methods are those methods that are not called on objects.  In other words, they don’t have an implicit parameter.  Random number generation provided an example where neither an implicit nor an explicit parameter were required.  The methods in the Math class exemplify the more typical case, where some useful computation is done on explicit parameters and the result is returned.  These methods belong to the class that contains them.  They may be written to take simple types or object references of any class as explicit parameters.  Calls to static methods take this general form:

variable = Class.method(parameter(s));

            A specific example that we’ve seen is:

     double x, y;

     …

     y = Math.sqrt(x);

            The key word “static” is used when declaring such methods.  You have seen this used in writing programs:

     public class MyProg

     {

          public static void main(String[] args)

          {

              …

            The main() method in programs is a static method.  The program class itself is simply a container for this useful method.  The class does not have a constructor.  The system calls the static method directly in order to run the program.

            It is possible to write your own static methods that take explicit parameters and do some calculation or produce some other desired result.  Here is a brief example:

public class MyMethodContainer

{

     public static double findPercent(double first, double second)

     {

          return ((first / second) * 100.0);

     }

}

            Assuming that the MyMethodContainer class is in the same directory where the program is, here is a small program that would use the static method:

public class TestContainer

{

     public static void main(String[] args)

     {

          MyTerminalIO myterminal = new MyTerminalIO();

          double x = 15;

          double y = 20;

          double percentresult = MyMethodContainer.findPercent(x, y);

          myterminal.println(x + “ is “ + percentresult + “ % of “ + y);

     }

}

            You would expect to get 75.0% as the result.

            The class TestContainer might also contain a method like this, which illustrates taking an object reference as a parameter.

     public static double findPercent(Cup5 first, double second)

     {

          return ((first.getSeedCount() / second) * 100.0);

     }

In addition to static methods, Java supports static variables.  These are variables which are declared in a class but which differ from instance variables in this way:  There is only one copy of the variable, and it is maintained in the class; objects, namely instances of the class, do not receive separate copies of this variable.  The static variable may be declared private.  If so, it is not directly accessible from outside the class.  However, it is directly accessible to every object of the class.  A static variable may also be declared public.  You have already encountered such variables.  The mathematical constants PI and E in the Math class are public static final doubles.

It would be possible to declare a minimum seedCount for objects of the cup class.  The example below shows the declaration and initialization of this variable.  Since there is only one copy of the minimumSeedCount variable for the whole class, this variable can be initialized in its declaration.  It doesn’t make sense to assign it a value in a constructor that is called every time a new object of the class is created.  The example also shows a constructor and the setSeedCount() method rewritten so that the value of seedCount couldn’t go below the value of the static variable.

public class Cup6

{

     private int seedCount;

     private static final int minimumSeedCount = 0;

     …

     Public Cup6()

     {

          seedCount = minimumSeedCount;

     }

     …

     public boolean setSeedCount(int newSeedCount)

     {

          if(newSeedCount >= minimumSeedCount)

          {

seedCount = newSeedCount;

return true;

          }

          else

              return false;

     }

     …   

}

            It would also be possible to declare and initialize the static variable so that it isn’t final:

            private static int minimumSeedCount = 0;

            Then a static method could be added to the class that would allow the minimum to be changed:

public static void changeMinimum(int newMin)

{

     minimumSeedCount = newMin;

}

            Notice that this method does not change the instance variable for an object.  It changes the minimumSeedCount that the class maintains for all objects of the class.

            Here is the code for the Cup6 class with changes to support a minimumSeedCount which could be changed during the course of the use of the class in a program.  One of the changes is the removal of the default constructor.  Since the minimum seed count may be greater than 0, it would not be suitable to be able to construct instances with an initial value of 0.  Notice also that in practice it would be possible to have created cup objects with a certain number of seeds in them and then raise the minimum afterwards.  No attempt is made to try and make pre-existing cups conform to the new minimum.

public class Cup6

{

  private int seedCount;

  private static int minimumSeedCount = 0;

  public Cup6(int initialCount)

  {

    if(initialCount >= minimumSeedCount)

      seedCount = initialCount;

    else

      seedCount = minimumSeedCount;

  }

  public static void changeMinimum(int newMin)

  {

    minimumSeedCount = newMin;

  }

  public int getSeedCount()

  {

    return seedCount;

  }

  public boolean setSeedCount(int newCount)

  {

    if(newCount >= minimumSeedCount)

      {

      seedCount = newCount;

      return true;

    }

    else

      return false;

  }

  public boolean addToSeedCount(int addedNumber)

  {

    if(seedCount + addedNumber >= minimumSeedCount)

    {

      seedCount = seedCount + addedNumber;

      return true;

    }

    else

      return false;

  }

  public boolean assignSeedCountToAnotherCup(Cup6 anotherCup)

  {

    if(seedCount >= minimumSeedCount)

    {

      int temp = this.getSeedCount();

      anotherCup.setSeedCount(temp);

      return true;

    }

    else

      return false;

  }

  public boolean setSeedCountFromAnotherCup(Cup6 anotherCup)

  {

    int temp = anotherCup.getSeedCount();

    if(temp >= minimumSeedCount)

    {

      setSeedCount(anotherCup.getSeedCount());

      return true;

    }

    else

      return false;

  }

  public boolean exchangeSeedCounts(Cup6 anotherCup)

  {

    int tempthis = this.getSeedCount();

    int tempthat = anotherCup.getSeedCount();

    if(tempthis >= minimumSeedCount && tempthat >= minimumSeedCount)

    {

      this.setSeedCount(tempthat);

      anotherCup.setSeedCount(tempthis);

      return true;

    }

    else

      return false;

  }

}

8.5  A Complete Classification of Variables in Java

We have already encountered the idea of a variable type.  It is also possible to think about variables in terms of their kind, namely:  1) Local variables, 2) Instance variables, 3) Formal parameter variables, and 4) Static variables.  Java only has these four kinds of variables.  We have encountered all four kinds.  Any variable that might be declared between a pair of opening and closing braces is local to that set of braces.  The term is usually used for general purpose variables in programs.  It is also possible to declare general purpose variables as needed in the bodies of methods.  These are then local to that method.

We have seen declarations of the accessibility of variables, including public, private, and no declaration at all.  Local program variables are simply declared with their type, and they are accessible within the set of braces where they are defined.  Parameters also do not have a public or private declaration.  They are local to the method they belong to.  Public and private declarations can be applied to instance and static variables (as well as methods).  You may have noticed by accident that it is possible to declare instance variables with neither the keyword public or private.  What kind of accessibility such variables have will be discussed in a future unit.  In the meantime you should just use the convention that instance variables should be private and declare them that way.

In addition to the question of whether a variable is declared public or private, for each kind of variable it is possible to answer the questions of when and where it comes into and goes out of active existence, where it is accessible within a program or a class, and whether or not it has default initialization.  Here are the answers to these questions for the four kinds of variables.

  1. Local variables come into existence at the point in code where they are declared.  Their existence continues in time and space to the ending brace of the block they are declared in.  As long as that block of code is executing they are in existence and can be used in that block.  As noted in the section on loops, it is possible to have more than one variable with the same name literally within the same block because the blocks themselves are nested.  The system distinguishes these in the following way:  In the inner nesting the variable name refers to the variable declared there.  Outside of that block, both before and after the inner block, the variable refers to the variable declared outside.  Local variables do not have default initialization.  It is usually a good idea for the programmer to initialize them in a program.
  1. Instance variables come into existence when the object they belong to is constructed.  They go out of existence when that object no longer exists.  For our purposes they no longer exist when we no longer have a handle on the object anywhere in our program.  If declared private, as they should be, they are only accessible from within the class in which they are declared.  In other words, anywhere in the constructors or methods of that class it is possible to use the instance variable without qualification and without using a get method.  When instance variables are used without qualification the object they belong to is the one being constructed or the implicit parameter of the code they appear in.  From outside of their class code, in a program for example, instance variables can only be accessed by the use of a method.  Instance variables do have default initialization.  Numeric variables are initialized to 0, boolean variables to false, and object references to null.  The programmer is always free to override these initializations in constructors, or write constructors which explicitly do such initializations so that there is no doubt what values instance variables have when reading the code.
  1. Formal parameters come into existence when the call to a method or constructor is made.  They go out of existence when the return is made from such code.  They are accessible anywhere inside the block of code belonging to the method or constructor. 
Уважаемый посетитель, Вы зашли на сайт как незарегистрированный пользователь.
Мы рекомендуем Вам зарегистрироваться либо войти на сайт под своим именем.

Добавление комментария

Имя:*
E-Mail:
Комментарий:
Введите два слова, показанных на изображении: *