Параметры методов

From AsIsWiki
Jump to: navigation, search
Форум

Назад | Оглавление | Дальше


Параметры методов

Способы передачи параметров методам:

  • Call by value (вызов по значению) - метод получает значение, переданное ему вызывающим модулем.
  • Call by reference (вызов по ссылке) - метод получает от вызывающего модуля адрес (location) переменной.
  • Call by name (вызов по имени) - был применен в языке Algol, одном из первых языков программирования высокого уровня.

Метод может модифицировать значение, хранящееся в переменной, переданной по ссылке, но не может изменять значение переменной, переданной по значению. Термин "Call by" описывает способ передачи параметров в различных языках программирования.

В Java всегда используется только вызов по значению. Это значит, что метод получает копии всех параметров. По этой причине метод не может модифицировать содержимое ни одного параметра, переданного ему.

Рассмотрим следующий вызов:

double percent = 10;

harry.raiseSalary(percent);

Не имеет значения, как именно реализован метод, мы знаем, что после его вызова значение переменной percent останется равным 10.

Допустим, что метод пытается утроить значение параметра:

public static void tripleValue(double x) {  // не работает
    x = 3 * x;
}

Вызовем метод:

double percent = 10;

tripleValue(percent);

После вызова метода значение переменной percent останется равным 10:

  1. Переменная x инициализируется копией значения параметра percent (т.е. числом 10).
  2. Значение переменной x утраивается - теперь оно равно 30. Однако значение переменной percent остается равным 10.
  3. Метод завершает свою работу, и параметр x больше не используется.

Несмотря на то, что после завершения метода передаваемые значения остаются неизменными, необходимо помнить, что существуют два типа параметров:

  • Простые типы (числа, логические переменные и т.д.).
  • Ссылки на объекты.

Методы не могут модифицировать параметры простых типов. С объектами ситуация иная. Можно легко реализовать метод, утраивающий зарплату сотрудника:

public static void tripleSalary(Employee x) {  // работает
    x.raiseSalary(200);
}

Вызовем метод:

harry = new Employee( ... );

tripleSalary(harry);

При выполнении кода будут выполнены следующие действия:

  1. Переменная x инициализируется копией значения переменной harry, т.е. ссылкой на объект.
  2. Метод raiseSalary() применяется к этой ссылке на объект. Метод объекта Employee, на который указывают ссылки x и harry, увеличивает зарплату сотрудника на 200%.
  3. Метод завершает свою работу, и параметр x больше не используется. Переменная harry продолжает ссылаться на объект, в котором зарплата увеличена втрое.

Реализовать метод, изменяющий состояние объекта, передаваемого как параметр, очень легко. Причина этого проста - метод получает копию ссылки на объект, поэтому и копия, и оригинал ссылки указывают на один и тот же объект.

Во многих языках программирования есть два способа передачи параметров: вызов по значению и вызов по ссылке. Некоторые программисты утверждают, что Java использует при передаче объектов только вызов по ссылке. Однако это не так. Попробуем написать метод, выполняющий обмен двух объектов Employee:

public static void swap(Employee x, Employee y) {  // не работает
    Employee temp = x;
    x = y;
    y = temp;
}

Если бы в Java, для передачи объектов в качестве параметров, использовался вызов по ссылке, этот метод работал бы так:

Employee a = new Employee("Alice", ... );
Employee b = new Employee("Bob", ... );
swap(a, b);
// Ссылается ли теперь a на Bob, а b - на Alice?

Метод не меняет местами объектные ссылки, хранящиеся в переменных a и b. Параметры x и y метода swap() инициализируются копиями этих ссылок, и метод свопит эти копии:

Employee temp = x;  // ссылка x = Alice, а ссылка y = Bob

x = y;

y = temp;  // теперь x = Bob, а y = Alice

По завершении работы метода, переменные x и y уничтожаются, а исходные переменные a и b продолжают ссылаться на прежние объекты.

В Java для передачи объектов не используется вызов по ссылке. Вместо этого ссылки на объекты передаются по значению. Резюме:

  • Метод не может изменять параметры простых типов (числа и логические значения).
  • Метод может изменять состояние объекта, передаваемого как параметр.
  • Метод не может изменять ссылки и переназначать их на новые объекты.

Следующая программа иллюстрирует эти утверждения:

public class ParamTest {

    public static void main(String[] args) {
        /**
         * Test 1: Methods can't modify numeric parameters
         */
        System.out.println("Testing tripleValue:");
        double percent = 10;
        System.out.println("Before: percent = " + percent);
        tripleValue(percent);
        System.out.println("After: percent = " + percent);

        /**
         * Test 2: Methods can change the state of object
         * parameters
         */
        System.out.println("\nTesting tripleSalary:");
        Employee harry = new Employee("Harry", 50000);
        System.out.println("Before: salary = " + harry.getSalary());
        tripleSalary(harry);
        System.out.println("After: salary = " + harry.getSalary());

        /**
         * Test 3: Methods can't attach new objects to
         * object parameters
         */
        System.out.println("\nTesting swap:");
        Employee a = new Employee("Alice", 70000);
        Employee b = new Employee("Bob", 60000);
        System.out.println("Before: a = " + a.getName());
        System.out.println("Before: b = " + b.getName());
        swap(a, b);
        System.out.println("After: a = " + a.getName());
        System.out.println("After: b = " + b.getName());
    }

    public static void tripleValue(double x)  // doesn't work
    {
        x = 3 * x;
        System.out.println("End of method: x = " + x);
    }

    public static void tripleSalary(Employee x)  // works
    {
        x.raiseSalary(200);
        System.out.println("End of method: salary = " + x.getSalary());
    }

    public static void swap(Employee x, Employee y) {
        Employee temp = x;
        x = y;
        y = temp;
        System.out.println("End of method: x = " + x.getName());
        System.out.println("End of method: y = " + y.getName());
    }
}

class Employee  // simplified Employee class
{

    public Employee(String n, double s) {
        name = n;
        salary = s;
    }

    public String getName() {
        return name;
    }

    public double getSalary() {
        return salary;
    }

    public void raiseSalary(double byPercent) {
        double raise = salary * byPercent / 100;
        salary += raise;
    }

    private String name;
    private double salary;
}

Сначала программа безуспешно пытается утроить значение числового параметра:

Testing tripleValue:
Before: percent = 10.0
End of method: x = 30.0
After: percent = 10.0

Затем программа успешно утраивает зарплату сотрудника:

Testing tripleSalary:
Before: salary = 50000.0
End of method: salary = 150000.0
After: salary = 150000.0

После выполнения метода состояние объекта, на который ссылается переменная harry, изменяется. Ничего невероятного здесь нет, поскольку метод модифицировал состояние объекта с помощью копии ссылки на него.

В заключение программа демонстрирует безрезультатную работу метода swap():

Testing swap:
Before: a = Alice
Before: b = Bob
End of method: x = Bob
End of method: y = Alice
After: a = Alice
After: b = Bob

Переменные x и y меняются местами, но переменные a и b остаются прежними.

В C++ применяется как вызов по значению, так и вызов по ссылке. Например, можно легко реализовать методы:

void tripleValue(double& x)

или

void swap(Employee& x, Employee& y)

Эти методы модифицируют свои параметры, являющиеся ссылками.



Форум

Назад | Оглавление | Дальше

Personal tools
Namespaces

Variants
Actions
Navigation
Tools